【C-项目】网盘(一期)

概述

登录服务器后,即可浏览服务器的文件系统。通过命令上传或下载文件。


服务器使用tcp类型的套接字,每收到一个连接请求,就新开一个子进程去处理


一期功能


客户端可以使用的命令

命令解析
ls显示当前工作目录的所有文件
pwd显示当前工作目录
cd [dir]切换工作目录
rm [filename]删除当前目录下的文件
mkdir [dir]创建一个新目录
puts [filename]上传文件
gets [filename]下载文件


启动


启动服务器

1、在服务器的bin目录下使用Makefile,生成可执行文件

w@Ubuntu20:bin $ make

2、启动服务器

w@Ubuntu20:bin $ ./server ../conf/server.conf

Makefile

OBJS:=$(wildcard ../src/*.c)
server:$(OBJS)gcc $^ -o $@ -I../include
clean:rm server


启动客户端

1、在客户端的bin的目录下使用Makefile,生成可执行文件

w@Ubuntu20:bin $ make

2、启动客户端

w@Ubuntu20:bin $ ./client client.conf

Makefile

OBJS:=$(wildcard ../src/*.c)
client:$(OBJS)gcc $^ -o $@ -I../include
clean:rm client


目录设计

服务器

  • bin:存放二进制文件
  • conf:存放配置文件
  • include:存放头文件
  • src:存放源文件
w@Ubuntu20:bin $ tree ..
..
├── bin
│   ├── Makefile
│   └── server
├── conf
│   └── server.conf
├── include
│   ├── func.h
│   └── head.h
└── src├── disk_conf.c├── disk_func.c├── disk_handle.c├── disk_server.c└── tcp_init.c4 directories, 10 files

客户端

w@Ubuntu20:bin $ tree ..
..
├── bin
│   ├── client
│   └── Makefile
├── conf
│   └── client.conf
├── include
│   ├── func.h
│   └── head.h
└── src├── disk_client.c├── disk_conf.c├── disk_func.c└── tcp_connect.c4 directories, 9 files


配置文件


服务器配置文件 server.conf

存放服务器ip地址,服务器port端口

根据实际情况自行更改

server_ip = 192.168.160.129
server_port = 2000

客户端配置文件 client.conf

存放服务器ip地址,服务器port端口

根据实际情况自行更改

server_ip = 192.168.160.129
server_port = 2000


传输文件

使用自定义协议传输:先发送数据长度,再发送数据内容



使用类型

//接收发送缓冲区大小
#define BUF_SIZE 1024
//命令中最多有几个参数
#define MAX_WORDNUM 8
//命令一个参数的最大长度
#define MAX_WORDLEN 30//套接字类型
typedef int socket_t;//通讯类型
typedef struct {socket_t  _sess_fd ;        //对端套接字char _sess_buf[BUF_SIZE] ;  //缓冲区
}Session_t, *pSession_t ;//命令类型                                                    
typedef struct {int _argc;       //命令中有几个参数char _cmd[MAX_WORDNUM][MAX_WORDLEN];
}Cmd_t, *pCmd_t;


服务器


主流程搭建


  1. 注册SIGCHLD信号============>实现子进程的异步退出
  2. 创建一个绑定服务器ipporttcp类型的套接字,开始监听客户端的连接
  3. 有新的用户连接,创建一个子进程,将新用户交给子进程处理


工作进程

每个子进程的具体工作

int child_handle(pSession_t ps)

参数:通信类型(有客户端的套接字,和发送接收缓冲区)

功能:处理任务(回应客户端的发来的命令)


  1. 循环接收客户端发来的命令,直到客户退出
    1. 先接受数据长度(如果为0表示客户退出,退出子进程)
    2. 再接收数据到缓冲区
    3. 从缓冲区中取出命令
    4. 如果是命令合法就执行,如果不合法就跳过


命令


ls

  • 功能:发送当前目录下所有文件信息

  • 函数:int do_ls(pSession_t ps);

  • 输入:通信类型(客户端套接字,缓冲区)

  • 实现

    1. 生成一个当前目录的目录流
    2. 循环读取目录流中的每一个目录项
      1. 根据目录项的信息,拼接一条格式化(比如文件类型、文件名、文件大小)后的文件信息
      2. 发送给客户端(先发信息长度,再发信息)
    3. 所有信息发完后,再发送一个结束标识符,告诉客户端命令已完成
    4. 关闭目录流

pwd

  • 功能:发送当前目录

  • 函数:int do_pwd(pSession_t ps);

  • 输入:通信类型(客户端套接字,缓冲区)

  • 实现

    1. 利用getcwd接口获取信息
    2. 发送给客户端(先发信息长度,再发信息)

cd

  • 功能:切换工作目录

  • 函数:int do_cd(pSession_t ps, pCmd_t pcmd);

  • 输入:通信类型(客户端套接字,缓冲区),命令类型

  • 实现

    1. 得到目标目录,就是命令的第二个参数
    2. 切换到目标目录
    3. 回应客户端,将切换后的目录发送过去(先发信息长度,再发信息)

rm

  • 功能:删除文件

  • 函数:int do_rm(pSession_t ps, pCmd_t pcmd);

  • 输入:通信类型(客户端套接字,缓冲区),命令类型

  • 实现

    1. 得到待删除文件,就是命令的第二个参数
    2. 在当前目录下查找是否存在
      1. 如果存在,删除文件,回应客户端删除成功(先发信息长度,再发信息)
      2. 如果不存在,回应客户端删除失败(先发信息长度,再发信息)

mkdir

  • 功能:创建一个新目录

  • 函数:int do_mkdir(pSession_t ps, pCmd_t pcmd);

  • 输入:通信类型(客户端套接字,缓冲区),命令类型

  • 实现

    1. 得到新目录名字,就是命令的第二个参数
    2. 查找此目录是否存在
      1. 如果存在,回应客户端创建失败(先发信息长度,再发信息)
      2. 如果不存在,创建目录,回应客户端创建成功(先发信息长度,再发信息)

gets

  • 功能:下载文件(服务器是下载,客户端是上传)

  • 函数:int do_gets(pSession_t ps, pCmd_t pcmd);

  • 输入:通信类型(客户端套接字,缓冲区),命令类型

  • 实现

    1. 得到文件名字,就是命令的第二个参数
    2. 下载文件
      1. 创建一个同名文件
      2. 循环接收文件内容
        1. 先接收数据长度(如果为0表示数据传输完毕,退出)
        2. 根据长度,接收数据
        3. 将数据写入文件
      3. 关闭文件

puts

  • 功能:上传文件(服务器是上传,客户端是下载)

  • 函数:int do_puts(pSession_t ps, pCmd_t pcmd);

  • 输入:通信类型(客户端套接字,缓冲区),命令类型

  • 实现

    1. 得到文件名字,就是命令的第二个参数
    2. 上传文件
      1. 打开待上传文件
      2. 循环上传文件内容
        1. 从文件中读取数据到缓冲区(如果返回值为0,表示文件全部读完,break)
        2. 根据读取的字节数量,发送数据(如果返回值为-1,表示对端断开,return -1)
      3. 文件发完后,发送结束标识符,通知对端传输完毕
      4. 关闭文件


客户端


主流程搭建

  1. 从配置文件中取出:服务器的ip地址,port端口号
  2. 连接服务器
  3. 循环从stdin中读取命令
    1. stdin中读取一行数据(如果读到quit,结束程序)
    2. 将这一行数据,格式化成命令格式
    3. 判断命令是否合法
      1. 如果不合法,跳过此次循环
      2. 如果合法,将命令发送给服务器
    4. 等待服务器返回数据
      1. 如果命令是cdpwdrmmkdir
        1. 先接数据长度,再接数据,打印命令结果
      2. 如果命令是ls
        1. 循环接收数据
          1. 先接数据长度(如果为0,表示传输完毕,break)
          2. 再接数据,打印命令结果
      3. 如果命令是gets,下载文件
      4. 如果命令是puts,上传文件



代码


服务器代码


head.h

#ifndef __HEAD_H__
#define __HEAD_H__#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>//检查系统调用返回值
#define EXIT_CHECK(ret, num, msg) if (ret == num) {\do {\perror(msg);\exit(-1);\}while(0); }#endif

func.h

#ifndef __FUNC_H__
#define __FUNC_H__//接收发送缓冲区大小
#define BUF_SIZE 1024
//命令中最多有几个参数
#define MAX_WORDNUM 8
//命令一个参数的最大长度
#define MAX_WORDLEN 30//套接字类型
typedef int socket_t;//通讯类型
typedef struct {socket_t  _sess_fd ;             //对端套接字char _sess_buf[BUF_SIZE] ;  //缓冲区
}Session_t, *pSession_t ;//命令类型
typedef struct {int _argc;       //命令中有几个参数char _cmd[MAX_WORDNUM][MAX_WORDLEN];
}Cmd_t, *pCmd_t;void conf_get(char *conf, char *ip, char *port); //从配置文件中获取参数
int tcp_init(char *ip, int port); //返回一个正在监听的tcp类型的服务器套接字
int child_handle(pSession_t ps); //子进程的具体工作,与客户端交互
int upload(int fd_socket, char *filename); //上传文件
int download(int fd_socket, char *filename); //下载文件
#endif

disk_server.c

#include "head.h"
#include "func.h"void sig_handle(int signum)
{//回收子进程资源wait(NULL);/* printf("client exit!\n"); */signum = 0;
}int main(int argc, char *argv[]) 
{//参数:配置文件路径if (2 != argc) {fprintf(stderr, "Args error!\n");return -1;}//注册子进程退出信号,用于异步回收子进程资源signal(SIGCHLD, sig_handle);//从配置文件中拿到 服务器ip和portchar ip[16] = "";char port[10] = "";conf_get(argv[1], ip, port);//建立tcp监听socket_t fd_server = tcp_init(ip, atoi(port));EXIT_CHECK(fd_server, -1, "socket_init");socket_t fd_client;pSession_t ps;//通信类型printf("DiskServer[ip:%s, port:%s] boot...\n", ip, port);//等待客户端连接int client_num = -1;//客户端的fd序号char buf[128] = "";//读写缓冲区int pid;  //子进程idwhile (1) {fd_client = accept(fd_server, NULL, NULL);EXIT_CHECK(fd_client, -1, "accept");//显示客户端连接bzero(buf, sizeof(buf));sprintf(buf, "%d connect...\n", ++client_num);write(1, buf, sizeof(buf)); //交给子进程处理ps = (pSession_t)calloc(1, sizeof(Session_t));ps->_sess_fd = fd_client;pid = fork();if (0 == pid) {child_handle(ps);exit(-1);}}return 0;
}

disk_conf.c

#include "head.h"//得到一行中'='符号后面的字符串
static void set_arg(char *line, char *arg)
{//将指针偏移到字符=char *ptr = strchr(line, '=');if (NULL == ptr) {fprintf(stderr, "conf_file is error!\n");exit(-1);}//= 后面的字符串strcpy(arg, ptr + 2);
}//从配置文件中获取参数
void conf_get(char *conf, char *ip, char *port)
{FILE *fp = fopen(conf, "r");EXIT_CHECK(fp, NULL, "fopen");char line[128] = "";//得到ipfgets(line, sizeof(line), fp);line[strlen(line) - 1] = '\0';set_arg(line, ip);//得到portmemset(line, 0, sizeof(line));fgets(line, sizeof(line), fp);line[strlen(line) - 1] = '\0';set_arg(line, port);fclose(fp);
}

disk_handle.c

#include "head.h"
#include "func.h"//静态函数的可见域是本文件
//向客户端发送命令结果
static int do_ls(pSession_t ps);
static int do_pwd(pSession_t ps);
static int do_cd(pSession_t ps, pCmd_t pcmd);
static int do_rm(pSession_t ps, pCmd_t pcmd);
static int do_mkdir(pSession_t ps, pCmd_t pcmd);
static int do_puts(pSession_t ps, pCmd_t pcmd);
static int do_gets(pSession_t ps, pCmd_t pcmd);
//将文件类型从int转成char*
static void file_type(mode_t mode, char *type);//子进程的具体工作,回应客户端发来的命令
int child_handle(pSession_t ps)
{//获取客户端的发来的命令int fd_client = ps->_sess_fd;int data_len = -1;int ret = -1;Cmd_t cmd;while (1) {memset(ps->_sess_buf, 0, BUF_SIZE);memset(&cmd, 0, sizeof(Cmd_t));//先接收数据长度ret = recv(fd_client, &data_len, sizeof(data_len), 0);if (0 == ret || 0 == data_len) {//对端已断开close(fd_client);printf("client exit!\n");exit(1);}//再接收具体的数据recv(fd_client, ps->_sess_buf, data_len, 0);//从接收缓冲区中读取命令memcpy(&cmd, ps->_sess_buf, sizeof(Cmd_t));printf("cmd: ");for (int i = 0; i < cmd._argc; ++i) {printf("%s ", cmd._cmd[i]);}printf("\n");//判断是什么命令if (!strcmp("cd", cmd._cmd[0])) {do_cd(ps, &cmd);}else if (!strcmp("pwd", cmd._cmd[0])) {do_pwd(ps);}else if (!strcmp("ls", cmd._cmd[0])) {do_ls(ps);}else if (!strcmp("rm", cmd._cmd[0])) {do_rm(ps, &cmd);}else if (!strcmp("mkdir", cmd._cmd[0])) {do_mkdir(ps, &cmd);}else if (!strcmp("puts", cmd._cmd[0])) {do_puts(ps, &cmd);}else if (!strcmp("gets", cmd._cmd[0])) {do_gets(ps, &cmd);}else {//非法命令continue;}}
}//将文件类型从int转成char*
static void file_type(mode_t mode, char *type)
{if (S_ISREG(mode)) {strncpy(type, "-", 1);}else if (S_ISDIR(mode)) {strncpy(type, "d", 1);}else if (S_ISFIFO(mode)) {strncpy(type, "p", 1);}else {strncpy(type, "o", 1);}
}static int do_ls(pSession_t ps)
{//生成目录流DIR *dirp = opendir("./");if (NULL == dirp) {int flag = -1;  //目录流打开失败,发送-1作为标志send(ps->_sess_fd, &flag, sizeof(flag), 0);return -1;}else {struct dirent *dir_info;int data_len = -1;//循环读取目录项while ((dir_info = readdir(dirp)) != NULL) {if (!strncmp(".", dir_info->d_name, 1) || !strncmp("..", dir_info->d_name, 2)) {continue;}//获取目录项的信息struct stat stat_info;memset(&stat_info, 0, sizeof(stat_info));stat(dir_info->d_name, &stat_info);//获取文件类型char type[1] = "";file_type(stat_info.st_mode, type);//拼接此文件信息memset(ps->_sess_buf, 0, BUF_SIZE);sprintf(ps->_sess_buf, "%-2s%-20s   %10ldB", type, dir_info->d_name, stat_info.st_size);//发送此文件信息data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(data_len), 0);//先发数据长度send(ps->_sess_fd, ps->_sess_buf, data_len, 0);}//此目录文件已读完, 发送0作为标志data_len = 0;send(ps->_sess_fd, &data_len, sizeof(data_len), 0);}closedir(dirp);return 0;
}static int do_pwd(pSession_t ps)
{//获取当前工作目录memset(ps->_sess_buf, 0, BUF_SIZE);getcwd(ps->_sess_buf, BUF_SIZE);//回应客户端int data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, ps->_sess_buf, data_len, 0);return 0;
}static int do_cd(pSession_t ps, pCmd_t pcmd)
{//拿到目标目录char dir[128] = "";strcpy(dir, pcmd->_cmd[1]);//显示目标目录puts(dir);//切换目录chdir(dir);//回应客户端getcwd(dir, sizeof(dir));int data_len = strlen(dir);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, dir, data_len, 0);return 0;
}static int do_rm(pSession_t ps, pCmd_t pcmd)
{//查看文件是否存在DIR *dirp = opendir("./");struct dirent *dir_cur;while ((dir_cur = readdir(dirp)) != NULL) {if (!strcmp(dir_cur->d_name, pcmd->_cmd[1])) {break;}}if (dir_cur) {//删除文件char cmd[256] = "";sprintf(cmd, "rm -rf %s", pcmd->_cmd[1]);system(cmd);//回应客户端sprintf(ps->_sess_buf, "file [%s] removed success!", pcmd->_cmd[1]);int data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, ps->_sess_buf, data_len, 0);}else {//文件不存在sprintf(ps->_sess_buf, "file [%s] removed failed!", pcmd->_cmd[1]);/* sprintf(ps->_sess_buf, "Not find the [%s], cannot remove!", pcmd->_cmd[1]); */int data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, ps->_sess_buf, data_len, 0);}return 0;
}static int do_mkdir(pSession_t ps, pCmd_t pcmd)
{//查看新目录是否存在DIR *dirp = opendir("./");struct dirent *dir_cur;while ((dir_cur = readdir(dirp)) != NULL) {if (!strcmp(dir_cur->d_name, pcmd->_cmd[1]) && S_ISDIR(dir_cur->d_type)) {break;}}if (NULL == dir_cur) {//创建目录char dir[128] = "";strcpy(dir, pcmd->_cmd[1]);puts(dir);mkdir(dir, 0775);//回应客户端sprintf(ps->_sess_buf, "create dir [%s] succeed!", pcmd->_cmd[1]);int data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, ps->_sess_buf, data_len, 0);}else {//目录已存在sprintf(ps->_sess_buf, "the [%s] is already exist!", pcmd->_cmd[1]);int data_len = strlen(ps->_sess_buf);send(ps->_sess_fd, &data_len, sizeof(int), 0);send(ps->_sess_fd, ps->_sess_buf, data_len, 0);}return 0;}static int do_puts(pSession_t ps, pCmd_t pcmd)
{//客户端是上传,服务器是下载char filename[256] = "";strcpy(filename, pcmd->_cmd[1]);if (0 == download(ps->_sess_fd, filename)) {printf("file [%s] download success \n", filename);}else {printf("file [%s] download failed \n", filename);}return 0;
}static int do_gets(pSession_t ps, pCmd_t pcmd)
{//客户端是下载,服务器是发送char filename[256] = "";strcpy(filename, pcmd->_cmd[1]);if (0 == upload(ps->_sess_fd, filename)) {printf("file [%s] upload success \n", filename);}else {printf("file [%s] upload failed \n", filename);}return 0;
}

disk_func.c

#include "head.h"//传输文件协议:小货车
#define BUF_SIZE 1024
typedef struct {int _data_len;//货车头,表示数据长度char _data[BUF_SIZE];//火车车厢,表示数据
}Truck_t;//上传文件
int upload(int fd_socket, char *filename)
{int ret = -1;//定义一个小货车,用来传输文件Truck_t truck;memset(&truck, 0, sizeof(Truck_t));//根据文件名打开传输文件int fd_file = open(filename, O_RDONLY);EXIT_CHECK(fd_file, -1, "open");#if 0//发文件大小struct stat file_info;memset(&file_info, 0, sizeof(file_info));fstat(fd_file, &file_info);truck._data_len = sizeof(file_info.st_size);memcpy(truck._data, &file_info.st_size, truck._data_len);ret = send(fd_socket, &truck, sizeof(int) + truck._data_len, 0);EXIT_CHECK(ret, -1, "send_filesize");
#endif//发文件内容while (1) {memset(truck._data, 0, sizeof(truck._data));//读取文件truck._data_len = read(fd_file, truck._data, BUF_SIZE);if (0 == truck._data_len) {//传输完成,退出循环break;}//发送ret = send(fd_socket, &truck, sizeof(int) + truck._data_len, 0);if (-1 == ret) {//客户端异常断开,退出循环printf("client already break!\n");return -1;}}//传输完成,通知fdtruck._data_len = 0;send(fd_socket, &truck._data_len, sizeof(int), 0);//关闭传输文件close(fd_file);return 0;
}//下载文件
int download(int fd_socket, char *filename)
{//打开或创建一个文件int fd_file = open(filename, O_WRONLY | O_CREAT, 0600);EXIT_CHECK(fd_file, -1, "open");#if 0//接收文件大小int filesize = 0;recv(fd_socket, &filesize, sizeof(int), 0);printf("filesize: %d", filesize);
#endif//接收文件内容Truck_t truck;while (1) {memset(&truck, 0, sizeof(truck));//接收数据长度recv(fd_socket, &truck._data_len, sizeof(truck._data_len), 0);if (0 == truck._data_len) {//文件传输完毕break;}//接收数据内容recv(fd_socket, truck._data, truck._data_len, MSG_WAITALL);write(fd_file, truck._data, truck._data_len);}close(fd_file);return 0;
}

tcp_init.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <arpa/inet.h>#define ERROR_CHECK(ret, num, msg) { if (ret == num) {\perror("msg");  return -1;} }//输入:服务器的ip地址,端口号
//输出:绑定了服务器ip和端口的,正在监听的套接字
int tcp_init(char *ip, int port)
{//生成一个tcp类型的套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(sfd, -1, "ser_socket");//将端口号设置为可重用, 不用再等待重启时的TIME_WAIT时间int reuse = 1;setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));//给套接字绑定服务端ip和portstruct sockaddr_in serverAddr;memset(&serverAddr, 0, sizeof(struct sockaddr_in));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(ip);serverAddr.sin_port = htons(port);int ret = bind(sfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));ERROR_CHECK(ret, -1, "ser_bind");//将套接字设为监听模式,并指定最大监听数(全连接队列的大小)ret = listen(sfd, 10); ERROR_CHECK(ret, -1, "ser_listen");/* printf("[ip:%s, port:%d] is listening...\n", ip, port); */return sfd;
}


客户端代码


head.h

#ifndef __HEAD_H__
#define __HEAD_H__#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>//检查命令行参数个数
#define ARGS_CHECK(argc, num) {if (argc != num) {\fprintf(stderr, "Args error\n");\exit(-1);}}//检查系统调用返回值
#define EXIT_CHECK(ret, num, msg) if (ret == num) {\do {\perror(msg);\exit(-1);\}while(0); }#endif

func.h

#ifndef __FUNC_H__
#define __FUNC_H__//接收发送缓冲区大小
#define BUF_SIZE 1024
//命令中最多有几个参数
#define MAX_WORDNUM 8
//命令一个参数的最大长度
#define MAX_WORDLEN 30//套接字类型
typedef int socket_t;//传输小货车
typedef struct {int _data_len;//货车头,表示数据长度char _data[BUF_SIZE];//货车车厢,表示数据
}Truck_t;//通讯类型
typedef struct {socket_t  _sess_fd ;             //对端套接字char _sess_buf[BUF_SIZE] ;  //发送接收缓冲区
}Session_t, *pSession_t;//命令类型
typedef struct {int _argc;       //命令中有几个参数char _cmd[MAX_WORDNUM][MAX_WORDLEN];
}Cmd_t, *pCmd_t;void conf_get(char *conf, char *ip, char *port);//从配置文件中获取参数
int tcp_connect(char *ip, int port);//连接服务器
int upload(int fd_socket, char *filename);//上传文件
int download(int fd_socket, char *filename);//下载文件void init_cmd(char *line, pCmd_t pcmd); //将line分割,存入cmd
void print_cmd(pCmd_t pcmd); //打印命令
int cmd_check(pCmd_t pcmd);//检查命令是否合法
#endif

disk_client.c

#include "head.h"
#include "func.h"int main(int argc, char *argv[])
{//命令行参数:配置文件路径ARGS_CHECK(argc, 2); //拿到服务器ip和portchar ip_server[16] = "";char port_server[5] = "";conf_get(argv[1], ip_server, port_server);//连接服务器socket_t fd_server = tcp_connect(ip_server, atoi(port_server));if (-1 == fd_server) {perror("socket_server");exit(-1);}//从stdin中读取命令,发给服务器char line[128] = "";Cmd_t cmd;Truck_t truck;//小货车,传输数据while (1) {memset(&truck, 0, sizeof(truck));memset(&cmd, 0, sizeof(Cmd_t));memset(line, 0, sizeof(line));//从标准输入中读取命令read(STDIN_FILENO, line, sizeof(line));line[strlen(line) - 1] = '\0';if (!strcmp(line, "quit")) {exit(1); }//格式化这行数据,并存入cmdinit_cmd(line, &cmd);//判断命令是否合法if (-1 == cmd_check(&cmd)) {printf("the cmd is illegal!\n");continue;}print_cmd(&cmd);//将cmd打包放入小货车memcpy(truck._data, &cmd, sizeof(cmd));//将命令发送给服务器truck._data_len = sizeof(Cmd_t);send(fd_server, &truck._data_len, sizeof(int), 0);send(fd_server, truck._data, truck._data_len, 0);//等待服务器返回数据if (!strncmp("cd", cmd._cmd[0], 2)) {system("clear");//先接长度,后接数据recv(fd_server, &truck._data_len, sizeof(int), 0);recv(fd_server, truck._data, truck._data_len, MSG_WAITALL);printf("%s\n", truck._data);}else if (!strncmp("pwd", cmd._cmd[0], 3)) {system("clear");//先接长度,后接数据recv(fd_server, &truck._data_len, sizeof(int), 0);recv(fd_server, truck._data, truck._data_len, MSG_WAITALL);printf("%s\n", truck._data);}else if (!strncmp("rm", cmd._cmd[0], 2)) {system("clear");//先接长度,后接数据recv(fd_server, &truck._data_len, sizeof(int), 0);recv(fd_server, truck._data, truck._data_len, MSG_WAITALL);printf("%s\n", truck._data);}else if (!strncmp("mkdir", cmd._cmd[0], 5)) {system("clear");//先接长度,后接数据recv(fd_server, &truck._data_len, sizeof(int), 0);recv(fd_server, truck._data, truck._data_len, MSG_WAITALL);printf("%s\n", truck._data);}else if (!strncmp("ls", cmd._cmd[0], 2)) {system("clear");//先接长度,后接数据while (1) {recv(fd_server, &truck._data_len, sizeof(int), 0);/* printf("data_len: %d\n", truck._data_len); */if (0 == truck._data_len) {break;}recv(fd_server, truck._data, truck._data_len, MSG_WAITALL);printf("%s\n", truck._data);}}else if (!strncmp("puts", cmd._cmd[0], 4)) {system("clear");//上传文件if (0 == upload(fd_server, cmd._cmd[1])) {printf("file [%s] upload success!\n", cmd._cmd[1]);} else {printf("file [%s] upload failed!\n", cmd._cmd[1]);}}else if (!strncmp("gets", cmd._cmd[0], 4)) {system("clear");//下载文件if (0 == download(fd_server, cmd._cmd[1])) {printf("file [%s] download success!\n", cmd._cmd[1]);}else {printf("file [%s] download failed!\n", cmd._cmd[1]);}}}return 0;
}

disk_conf.c

#include "head.h"//得到一行中'='符号后面的字符串
static void set_arg(char *line, char *arg)
{//将指针偏移到字符=char *ptr = strchr(line, '=');if (NULL == ptr) {fprintf(stderr, "conf_file is error!\n");exit(-1);}//= 后面的字符串strcpy(arg, ptr + 2);
}//从配置文件中获取参数
void conf_get(char *conf, char *ip, char *port)
{FILE *fp = fopen(conf, "r");EXIT_CHECK(fp, NULL, "fopen");char line[128] = "";//得到ipfgets(line, sizeof(line), fp);line[strlen(line) - 1] = '\0';set_arg(line, ip);//得到portmemset(line, 0, sizeof(line));fgets(line, sizeof(line), fp);line[strlen(line) - 1] = '\0';set_arg(line, port);fclose(fp);
}

disk_func.c

#include "head.h"
#include "func.h"//将line分割,存入cmd
void init_cmd(char *line, pCmd_t pcmd)
{//使用strtok将line拆分char *token;const char s[] = {' ', '\n'};//首次使用strtok时,需传入待分割的字符串和分隔符集合//之后再调用,第一个参数设为NULL,表示从继续上次的位置分割token = strtok(line, s);while (NULL != token) {strcpy(pcmd->_cmd[pcmd->_argc++], token);token = strtok(NULL, s);}
}//打印命令
void print_cmd(pCmd_t pcmd)
{printf("cmd: ");for (int i = 0; i < pcmd->_argc; ++i) {printf("%s ", pcmd->_cmd[i]);}printf("\n");
}//检查命令是否合法
int cmd_check(pCmd_t pcmd) 
{char tmp[64] = "";strncpy(tmp, pcmd->_cmd[0], 64);if (!strcmp(tmp, "cd") || !strcmp(tmp, "ls") ||!strcmp(tmp, "pwd") ||!strcmp(tmp, "rm") ||!strcmp(tmp, "puts") ||!strcmp(tmp, "gets") ||!strcmp(tmp, "mkdir")) {return 0;} else {return -1;}
}//上传文件
int upload(int fd_socket, char *filename)
{int ret = -1;//定义一个小货车,用来传输文件Truck_t truck;memset(&truck, 0, sizeof(Truck_t));//根据文件名打开传输文件int fd_file = open(filename, O_RDONLY);EXIT_CHECK(fd_file, -1, "open");#if 0//发文件大小struct stat file_info;memset(&file_info, 0, sizeof(file_info));fstat(fd_file, &file_info);truck._data_len = sizeof(file_info.st_size);memcpy(truck._data, &file_info.st_size, truck._data_len);ret = send(fd_socket, &truck, sizeof(int) + truck._data_len, 0);EXIT_CHECK(ret, -1, "send_filesize");
#endif//发文件内容while (1) {memset(truck._data, 0, sizeof(truck._data));//读取文件truck._data_len = read(fd_file, truck._data, BUF_SIZE);if (0 == truck._data_len) {//传输完成,退出循环break;}//发送ret = send(fd_socket, &truck, sizeof(int) + truck._data_len, 0);if (-1 == ret) {//服务器异常断开,退出循环printf("server already break!\n");break;}}//传输完成,通知fdtruck._data_len = 0;send(fd_socket, &truck._data_len, sizeof(int), 0);//关闭传输文件close(fd_file);return 0;
}//下载文件
int download(int fd_socket, char *filename)
{//打开或创建一个文件int fd_file = open(filename, O_WRONLY | O_CREAT, 0600);EXIT_CHECK(fd_file, -1, "open");/* //接收文件大小 *//* int filesize = 0; *//* recv(fd_socket, &filesize, sizeof(int), 0); *//* printf("filesize: %d", filesize); *///接收文件内容Truck_t truck;while (1) {memset(&truck, 0, sizeof(truck));//接收数据长度recv(fd_socket, &truck._data_len, sizeof(truck._data_len), 0);if (0 == truck._data_len) {//文件传输完毕break;}//接收数据内容recv(fd_socket, truck._data, truck._data_len, MSG_WAITALL);write(fd_file, truck._data, truck._data_len);}close(fd_file);return 0;
}

tcp_connect.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>//连接服务器
int tcp_connect(char *ip, int port)
{int fd_server = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serAddr;memset(&serAddr, 0, sizeof(serAddr));serAddr.sin_family = AF_INET;serAddr.sin_addr.s_addr = inet_addr(ip);serAddr.sin_port = htons(port);if (-1 == connect(fd_server, (struct sockaddr*)&serAddr, sizeof(serAddr))) {perror("connect");return -1;}return fd_server;
}


总结

此版本未用进程池,导致创建进程或者回收进程时会占用一定的时间,也不太方便管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/54062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot:Web开发(基于SpringBoot使用MyBatis-Plus+JSP开发)

目录 前期准备 构建项目&#xff08;IDEA2023.1.2&#xff0c;JDK21&#xff0c;SpringBoot3.3.3&#xff09; 添加启动器 Model准备 这里我们利用MybatisX插件生成我们所需要的实体类、数据访问层以及服务层 注意选择MyBatis-Plus3以及Lombok 然后再在service接口中定义…

C++——矩阵无重复行列取数问题

矩阵无重复行列取数问题 题目&#xff1a;有一个矩阵&#xff0c;我每次可以取一个数&#xff0c;后面再取数时不能再取该数所在行和列的其他数&#xff0c;我现在想一直取直到无法再取数位置&#xff0c;现在想要取到的数的和最大&#xff0c;问应该怎么取&#xff0c;最大的和…

yolov8 rect batch_shapes 672 图像大小变化

遇到这样一种情况&#xff1a;img_sz640,但在val时&#xff0c;输入网络的张量h和w是672 为什么输入图像会从640变大到672&#xff1f; 这是因为一种rectangle增强方法&#xff0c;“同个batch里做rectangle宽高等比变换&#xff0c; 加快训练 &#xff0c;对于多余的黑边做到…

【算法】-单调队列

目录 什么是单调队列 区域内最大值 区域内最小值 什么是单调队列 说到单调队列&#xff0c;其实就是一个双端队列&#xff0c; 顾名思义&#xff0c;单调队列的重点分为「单调」和「队列」。「单调」指的是元素的「规律」——递增&#xff08;或递减&#xff09;。「队列」指…

电脑提示丢失mfc140u.dll的详细解决方案,mfc140u.dll文件是什么

遇到电脑显示“缺少 mfc140u.dll 文件”的错误其实是比较常见的。这种提示通常表示某个应用程序在尝试运行时未能找到它所需的关键 DLL 文件&#xff0c;导致无法正常启动。不过&#xff0c;别担心&#xff0c;本文将一步步引导你通过几种不同的方法来解决这个问题&#xff0c;…

树模式数据表设计学习

引子&#xff1a; 场景&#xff1a;某读书网站&#xff0c;支持读者评论文章&#xff0c;并且对评论支持回复功能。设计的表如下&#xff1a; 问题点&#xff1a;你想获取一个评论下所有的评论信息&#xff1f; 将所有评论一次性取出、轮巡遍历&#xff0c;获取到所有数据。 …

C#【必备技能篇】替换一个字节(byte)中连续几位(bit)的内容

文章目录 一、一个示例二、通用方法 一、一个示例 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp1 {class Program{static void Main(string[] args){Method1();}public static…

多人开发小程序设置体验版的痛点

抛出痛点 在分配任务时,我们将需求分为三个分支任务,分别由前端A、B、C负责: 前端A: HCC-111-实现登录功能前端B: HCC-112-实现用户注册前端C: HCC-113-实现用户删除 相应地,我们创建三个功能分支: feature_HCC-111-实现登录功能feature_HCC-112-实现用户注册feature_HCC-1…

从C语言过渡到C++

&#x1f4d4;个人主页&#x1f4da;&#xff1a;秋邱-CSDN博客☀️专属专栏✨&#xff1a;C &#x1f3c5;往期回顾&#x1f3c6;&#xff1a;单链表实现&#xff1a;从理论到代码-CSDN博客&#x1f31f;其他专栏&#x1f31f;&#xff1a;C语言_秋邱的博客-CSDN博客 目录 ​…

Golang | Leetcode Golang题解之第395题至少有K个重复字符的最长子串

题目&#xff1a; 题解&#xff1a; func longestSubstring(s string, k int) (ans int) {for t : 1; t < 26; t {cnt : [26]int{}total : 0lessK : 0l : 0for r, ch : range s {ch - aif cnt[ch] 0 {totallessK}cnt[ch]if cnt[ch] k {lessK--}for total > t {ch : s[…

为什么阿里开发手册不允许使用系统提供的线程池?

FixedThreadPool、SingleThreadPool和CachedThreadPool是Java中常用的几种线程池实现&#xff0c;它们各自具有不同的特性和潜在问题&#xff0c;这些问题可能导致如内存溢出&#xff08;OOM&#xff09;和CPU使用率过高&#xff08;CPU 100%&#xff09;等性能问题。以下是对这…

cas单点登录流程揭密

前言 前几篇文章&#xff0c;经过大篇幅讲解了cas整合以及Cookie和Session。 springbootvue集成cas单点登录最详细避坑版讲解 关于cookie和session的直观讲解&#xff08;一&#xff09; 关于cookie和session的直观讲解&#xff08;二&#xff09; 那么&#xff0c;接下来&…

数据结构修炼——顺序表和链表的OJ题练习

目录 一、顺序表相关OJ题1 移除元素题目解析 2 合并两个有序数组题目解析 二、链表相关OJ题1 移除链表元素题目解析 2 反转链表题目解析 3 链表的中间结点题目解析 4 合并两个有序链表题目解析 5 链表的回文结构题目解析 6 相交链表题目解析 7 环形链表的判断题目解析 8 环形链…

OCR 通用端到端模型GOT

摘要 在人工智能领域&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术已经取得了显著的进展。随着技术的不断进步&#xff0c;我们正迈向OCR 2.0时代。本文将介绍由Vary团队开发的通用端到端模型GOT&#xff0c;这一模型在OCR领域具有革命性的潜力。 论文概览 论文…

水滴式多功能粉碎机:粉碎中草药的好帮手

水滴式中草药粉碎机&#xff0c;顾名思义&#xff0c;其设计灵感源自自然界中水滴的柔和与力量。它摒弃了传统粉碎机粗犷的粉碎方式&#xff0c;采用低速研磨技术&#xff0c;模拟水滴穿透岩石的细腻与持久&#xff0c;对中草药进行温和而深入的粉碎。这种技术不仅保留了药材中…

Unreal游戏初始化流程

前言 本文主要是总结Unreal在游戏启动时的初始化流程&#xff0c;包括讨论PIE和Standalone的区别&#xff0c;避免把一些初始化逻辑放在不合适的位置&#xff0c;比如我希望在所有Actor BeginPlay后执行某个逻辑&#xff0c;那我如果把它放在Subsystem的initialize中显然就会搞…

初阶数据结构【TOP】- 11.普通二叉树的介绍 - 1. (细致,保姆~~!)

文章目录 前言一、普通二叉树的链式结构二、 造树三、普通二叉树的遍历四、遍历完整代码五、总结 前言 本篇文章笔者将会对普通二叉树部分进行细致的讲解 , 本篇主要包括以下内容: 二叉树链式结构的介绍 ,二叉树的遍历. 笔者会一步一步分析带学者领略递归的美好~~ 一、普通二叉…

DPO: Direct Preference Optimization 介绍

DPO 是 RLHF 的屌丝版本&#xff0c;RLHF 需要加载 4 个模型&#xff08;2个推理&#xff0c;2个训练&#xff09;&#xff0c;DPO 只需要加载 2 个模型&#xff08;1个推理&#xff0c;一个训练&#xff09;。 RLHF&#xff1a; DPO&#xff1a; DPO 原理 DPO 的本质是监督对…

YoloV10 训练自己的数据集(推理,转化,C#部署)

目录 一、下载 三、开始训练 train.py detect.py export.py 超参数都在这个路径下 四、C#读取yolov10模型进行部署推理 如下程序是用来配置openvino 配置好引用后就可以生成dll了 再创建一个控件&#xff0c;作为显示 net framework 4.8版本的 再nuget工具箱里下载 …

价值流与核心理论框架对比解析:企业业务架构优化的全景指南

企业架构优化中的理论框架选择 随着数字化转型和全球竞争的加剧&#xff0c;企业管理者越来越意识到优化业务流程以提升竞争力的重要性。然而&#xff0c;在众多优化方法中&#xff0c;企业如何选择最适合自己的理论框架成为一大挑战。由The Open Group发布的《价值流指南》系…