• LinuxFTP云盘-文件服务系统


    目录

    1.项目介绍

    2.项目运行展示

    3.实现思路

    服务端:

    客户端:

    4.相关调用函数

    socket():创建一个网络通信端点原型:int socket(int domain, int type, int protocol);

    atoi():将字符串转变成整型数据原型:int atoi(const char *nptr);

     htons():将16位无符号数从主机字节序转换成网络字节序原型:uint16_t htons(uint16_t hostshort);

     inet_aton():将IP地址从字符串类型转换成网络字节序原型:int inet_aton(const char *cp, struct in_addr *inp);

     inet_ntoa():将IP地址从网络字节序转换成字符串类型原型:char *inet_ntoa(struct in_addr in);

     bind():为套接字添加信息(协议族、IP地址、端口号)原型:int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

     listen():监听客户端的连接原型:int listen(int sockfd, int backlog);

     accept():等待客户端的连接,当没有客户端连接时,accept()会阻塞等待原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

     connect():客户端连接服务器原型:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

     fork():创建一个子进程原型:pid_t fork(void)

     open():打开一个文件原型:int open(const char *pathname, int flags, mode_t mode);

     read():从某个文件中读取数据,没读到数据时会阻塞等待原型:int read(int fd, void *buf, size_t count);

     write():向某个文件写入数据原型:int write(int fd, const void *buf, size_t count);

     lseek():移动文件的光标,通常用lseek(fd,0,SEEK_END)获取文件长度原型:int lseek(int fd, off_t offset, int whence);

     close(),一般不用主动关闭文件,在进程终止时,内核自动关闭该进程打开的文件原型:int close(int fd)

     strtok():字符串分割函数原型:char *strtok(char *str, const char *delim);

    strcmp():字符串比较函数原型:int strcmp(const char *s1, const char *s2);

    strstr():字符串包含函数原型:char *strstr(const char *s1, const char *s2);

     strcat():字符串拼接函数原型:char *strcat(char *str1, const char *str2);

     strcpy():字符串复制函数原型:char *strcpy(char *str1, const char *str2)

     memset():将一块内存设定成指定的值原型:void *memset(void *s, int c, size_t n);

     system():执行某个命令,命令执行后继续从调用点执行程序原型:int system(const char *command);

     popen():执行某个命令,并将执行命令后得到的数据存放到一个文件流中原型:FILE *popen(const char *command, const char *type);

     fread():从某个文件流中读取数据原型:int fread(void *ptr, size_t size, size_t nmemb,FILE *stream);

     fflush():刷新缓冲区,将某个流未写的数据传送至内核原型:int fflush(FILE *stream);

     chdir():修改当前的工作目录原型:int chdir(const char *path);

     access():查看文件是否具有某个权限原型: int access(const char * pathname,int mode);

    5.完整代码示例和解析

    服务端代码:

    客户端代码:


    1.项目介绍

    在 Linux 环境下使用文件、线程、网络编程等相关操作,实现 ls、lls、cd、lcd、put、get、pwd、lpwd 功能。 树莓派 3B 作为服务端、Ubuntu 作为客户端,允许 Ubuntu 上多个进程作客户端连接服务端。客户端使用 lls、 lcd、lpwd 命令查看客户端目录文件、切换工作目录、查看当前目录;使用 ls、cd、pwd 命令查看服务端目录文件、切换工作目录、查看当前目录;使用 put 加文件名将客户端文件上传至服务端,使用 get 加文件名从 服务端将文件下载至客户端,实现在同一局域网下树莓派 3B 和 Ubuntu 之间文件的

    2.项目运行展示

    3.实现思路

    服务端:
    • 与客户端建立socket连接
    • 读取客户端发送的命令
    • 进行相关的命令识别和处理,将执行命令所获得的数据发送给客户端。
    客户端:
    • 与服务器建立socket连接
    • 获取用户输入的命令,进行相关的命令识别和处理
    • 客户端会进行系统调用执行带l前缀命令,不带l前缀的命令将会发送给服务器
    • 对服务器发送的数据进行处理

    4.相关调用函数

    socket():创建一个网络通信端点
    原型:int socket(int domain, int type, int protocol);

    返回值:调用成功返回套接字描述符,调用失败返回-1

    参数: 

    int domain:指明所使用的协议族,通常为 AF_INET,表示互联网协议族(TCP/IP 协议族)

    • AF_INET IPv4因特网域.
    • AF_INET6 IPv6 因特网域
    • AF_UNIX Unix 域
    • AF_ROUTE 路由套接字
    • AF_KEY 密钥套接字
    • AF_UNSPEC 未指定

    int type:选择套接字的类型,在这里我们用SOCK_STREAM

    • SOCK_STREAM:
    • 流式套接字提供可靠的、面向连接的通信流,它使用 TCP 协议,从而保证了数据传输的正确性和顺序性
    • SOCK_DGRAM:
    • 数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用教据报协议UDP
    • SOCK_RAW:                                                                                                                            允许程序使用低层协议,原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发

    int protocol:选择协议,通常为0, 表示type参数对应的默认协议

    • IPPROTO_TCP TCP 传输协议
    • IPPROTO_UDP UDP 传输协议
    • IPPROTO_SCTP SCTP 传输协议
    • IPPROTO_TIPC TIPC 传输协议
    atoi():将字符串转变成整型数据
    原型:int atoi(const char *nptr);

    返回值:返回转换之后得到的int型数据

    参数:

    • const char *nptr:需要进行转换的字符串
     
    htons():将16位无符号数从主机字节序转换成网络字节序
    原型:uint16_t htons(uint16_t hostshort);

    返回值:转换之后的TCP/IP网络字节序

    参数:

    • uint16_t hostshort:16位无符号整数
     
    inet_aton():将IP地址从字符串类型转换成网络字节序
    原型:int inet_aton(const char *cp, struct in_addr *inp);

    返回值:调用成功返回非0,调用失败返回0

    参数:

    • const char *cp:字符串类型的IP地址
    • struct in_addr *inp:sockaddr_in类型结构体里的in_addr类型结构体地址

     
    inet_ntoa():将IP地址从网络字节序转换成字符串类型
    原型:char *inet_ntoa(struct in_addr in);

    返回值:指向字符串类型IP地址的字符串指针

    参数:

    • struct in_addr in:sockaddr_in类型结构体里的in_addr类型结构体

     
    bind():为套接字添加信息(协议族、IP地址、端口号)
    原型:int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

    返回值:调用成功返回0,调用失败返回-1

    参数:

    • int sockfd:套接字描述符
    • const struct sockaddr *addr:sockaddr类型结构体地址,在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换
    • socklen_t addrlen:参数二的内存空间大小 
     
    listen():监听客户端的连接
    原型:int listen(int sockfd, int backlog);

    返回值:调用成功返回0,调用失败返回-1

    参数:

    • int sockfd:套接字描述符
    • int backlog:指定在请求队列中允许的最大连接数
     
    accept():等待客户端的连接,当没有客户端连接时,accept()会阻塞等待
    原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

    返回值:调用成功返回0,调用失败返回-1

    参数:

    • int sockfd:套接字描述符
    • const struct sockaddr *addr:sockaddr类型结构体地址
    • socklen_t *addrlen:参数二内存空间大小的值的地址
       
     
    connect():客户端连接服务器
    原型:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

    返回值:调用成功返回套接字描述符,调用失败返回-1

    参数:

    • int sockfd:套接字描述符
    • const struct sockaddr *addr:sockaddr类型结构体地址,在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换
    • socklen_t *addrlen:参数二内存空间大小
     
    fork():创建一个子进程
    原型:pid_t fork(void)

    返回值:调用成功返回两次,0或非负整数,调用失败返回-1

    • 0:子进程
    • 非负整数(子进程的pid):父进程
     
    open():打开一个文件
    原型:int open(const char *pathname, int flags, mode_t mode);

    返回值:调用成功返回一个文件描述符,调用失败返回-1

    参数:

    • const char *pathname:文件名(相对路径)
    • int flags:打开文件的方式及附带操作
    • mode_t mode:(可选参数)文件访问的权限
     
    read():从某个文件中读取数据,没读到数据时会阻塞等待
    原型:int read(int fd, void *buf, size_t count);

    返回值:读入成功时,返回读取的字节数(如果读文件开始前光标到达文件尾端则返回0)读入失败时,返回-1

    参数:

    • fd:文件描述符
    • void *buf:读取的数据存放位置
    • size_t count:读取的字节数
     
    write():向某个文件写入数据
    原型:int write(int fd, const void *buf, size_t count);

    返回值:写入成功时,返回写入的字节数,写入数据为空返回0,写入失败返回-1

    参数:

    • fd:文件描述符
    • void *buf:写入文件的数据
    • size_t count:写入文件的字节数
     
    lseek():移动文件的光标,通常用lseek(fd,0,SEEK_END)获取文件长度
    原型:int lseek(int fd, off_t offset, int whence);

    返回值:调用成功返回光标偏移字节,调用失败返回-1

    参数:

    • fd:文件描述符
    • off_t offset:偏移字节,0为不偏移,-a为往前偏移a个字节,b为往前偏移b个字节
    • int whence:光标位置
       
     
    close(),一般不用主动关闭文件,在进程终止时,内核自动关闭该进程打开的文件
    原型:int close(int fd)

    返回值:调用成功返回0,调用失败时返回-1

    参数:

    • fd:文件描述符
     
    strtok():字符串分割函数
    原型:char *strtok(char *str, const char *delim);

    返回值:

    • 第一次传入字符串和分隔符时,返回分割的字符串
    • 第二次传入NULL和分隔符时,返回分隔的字符串
    • 直到最后一次传入NULL和空字符串时,返回最后一个分割的字符串
    • 其中第一次字符串被分割后,值和大小为第一次分割出来的字符串

    参数:

    • char *str:字符串
    • const char *delim:分隔符
    strcmp():字符串比较函数
    原型:int strcmp(const char *s1, const char *s2);

    返回值:如果两个字符串相等返回0,两个字符串不相等返回其他值

    参数:

    • const char *s1:字符串1
    • const char *s2:字符串2
    strstr():字符串包含函数
    原型:char *strstr(const char *s1, const char *s2);

    返回值:如果字符串s1包含字符串s2,返回包含字符及后面字符,如果字符串s1不包含字符串s2则返回NULL

    参数:

    • const char *s1:字符串1
    • const char *s2:字符串2
     
    strcat():字符串拼接函数
    原型:char *strcat(char *str1, const char *str2);

    返回值:返回指向字符串2拼接到字符串1的字符串地址的指针

    参数:

    • const char *s1:字符串1
    • const char *s2:字符串2
     
    strcpy():字符串复制函数
    原型:char *strcpy(char *str1, const char *str2)

    返回值:返回指向字符串2复制到字符串1尾端的字符串地址的指针

    参数:

    • const char *s1:字符串1
    • const char *s2:字符串2
     
    memset():将一块内存设定成指定的值
    原型:void *memset(void *s, int c, size_t n);

    返回值:返回一个指向内存s的指针

    参数:

    • void *s:内存块
    • int c:指定值
    • size_t n:要被设置该值的字符数
     
    system():执行某个命令,命令执行后继续从调用点执行程序
    原型:int system(const char *command);

    返回值:

    • 调用/bin/sh失败时返回127,其他失败原因返回-1
    • const char *command参数为NULL时返回非0值
    • 调用成功返回执行shell命令后的返回值

    参数:

    • const char *command:命令
     
    popen():执行某个命令,并将执行命令后得到的数据存放到一个文件流中
    原型:FILE *popen(const char *command, const char *type);

    返回值:调用成功返回文件流,调用失败返回NULL

    参数:

    • const char *command:命令
    • const char *type:返回的文件流权限,w:文件流可写,r: 文件流可读(与system等价)
     
    fread():从某个文件流中读取数据
    原型:int fread(void *ptr, size_t size, size_t nmemb,FILE *stream);

    返回值:读入成功时,返回读取的字节数,读入失败时,返回-1

    参数:

    • const void *ptr:读取数据存放位置
    • size_t size:每次读的字节数
    • size_t nmemb:读的次数
    • FILE *stream:文件流
     
    fflush():刷新缓冲区,将某个流未写的数据传送至内核
    原型:int fflush(FILE *stream);

    返回值:调用成功返回0,调用失败返回EOF

    参数:

    • FILE *stream:文件流
    • fflush(stdin):刷新标准输入缓冲区,把输入缓冲区里的东西丢弃
    • fflush(stdout):刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上
     
    chdir():修改当前的工作目录

    原型:int chdir(const char *path);

    返回值:调用成功返回0,调用失败返回-1

    参数:

    • const char *path:新的当前工作目录,绝对路径或者相对路径
     
    access():查看文件是否具有某个权限

    原型: int access(const char * pathname,int mode);

    返回值:如果文件具有某种权限返回0,不具有某种权限返回-1

    参数:

    • const char *pathname:文件名(相对路径)
    • int mode:权限

    5.完整代码示例和解析

    服务端代码:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #define LS 1
    17. #define CD 2
    18. #define PWD 3
    19. #define PUT 5
    20. #define GET 15
    21. struct Msg
    22. {
    23. int type;
    24. char cmd[50];
    25. char data[5120];
    26. };
    27. int cmd_distinguish(char *cmd)
    28. {
    29. int ret;
    30. cmd = strtok(cmd,"\n"); //由于客户端获取用户输入时,会使用enter键,因此传输到服务器的命令都带有换行符,需要进行命令分割
    31. if(!strcmp("ls",cmd)){
    32. ret = LS;
    33. }else if(strstr(cmd,"cd") != NULL){
    34. ret = CD;
    35. }else if(strstr(cmd,"get") != NULL){
    36. ret = GET;
    37. }else if(strstr(cmd,"put") != NULL){
    38. ret = PUT;
    39. }else if(!strcmp("pwd",cmd)){
    40. ret = PWD;
    41. }else{
    42. ret = 0;
    43. }
    44. return ret;
    45. }
    46. //将带有文件名、路径名的命令进行处理分割
    47. //如命令“get 1.c”,该函数就会返回1.c
    48. char *str_decompose(char *cmd)
    49. {
    50. strtok(cmd," ");
    51. cmd = strtok(NULL," ");
    52. return cmd;
    53. }
    54. void cmd_handle(int ret,struct Msg msg,int c_fd)
    55. {
    56. FILE *fp = NULL;
    57. int n_fread;
    58. char file_path[20] = "./"; //当前路径
    59. switch(ret){
    60. case PWD:
    61. fp = popen("pwd","r"); //调用终端执行pwd命令,并将数据存放到fp文件流中
    62. memset(msg.data,0,sizeof(msg.data));
    63. n_fread = fread(msg.data,1,sizeof(msg.data),fp);//读取文件流的数据
    64. msg.type = 0;
    65. write(c_fd,&msg,sizeof(msg)); //向客户端发送数据
    66. fflush(stdout); //刷新标准输出缓冲区,把输出缓冲区里的东西强制打印到标准输出设备上
    67. break;
    68. case LS:
    69. fp = popen("ls","r");
    70. memset(msg.data,0,sizeof(msg.data));
    71. n_fread = fread(msg.data,1,sizeof(msg.data),fp);
    72. msg.type = 0;
    73. write(c_fd,&msg,sizeof(msg));
    74. fflush(stdout);
    75. break;
    76. case CD:
    77. if(chdir(str_decompose(msg.cmd)) <0){ //修改当前工作目录
    78. perror("chdir");
    79. }
    80. break;
    81. case GET:
    82. strcat(file_path,str_decompose(msg.cmd));
    83. if(access(file_path,F_OK) < 0){ //判断服务器是否拥有客户端想要获取的文件
    84. perror("access");
    85. memset(msg.data,0,sizeof(msg.data));
    86. msg.type = 0;
    87. strcpy(msg.data,"Target file does not exist");
    88. write(c_fd,&msg,sizeof(msg));
    89. }else if(access(file_path,R_OK) < 0){ //判断服务器是否对该文件有读权限
    90. memset(msg.data,0,sizeof(msg.data));
    91. msg.type = 0;
    92. strcpy(msg.data,"The target file does not have read permission");
    93. write(c_fd,&msg,sizeof(msg));
    94. }else{ //获取该文件的内容,发送给客户端
    95. int file_fd = open(file_path,O_RDONLY,0600);
    96. if(file_fd < 0){
    97. perror("open");
    98. }
    99. int file_size = lseek(file_fd,0,SEEK_END);
    100. if(file_size < 0){
    101. perror("lseek");
    102. }
    103. lseek(file_fd,0,SEEK_SET);
    104. memset(msg.data,0,sizeof(msg.data));
    105. msg.type = GET;
    106. if(read(file_fd,msg.data,file_size) < 0){
    107. perror("read");
    108. }
    109. close(file_fd);
    110. write(c_fd,&msg,sizeof(msg));
    111. }
    112. break;
    113. case PUT:
    114. strcat(file_path,str_decompose(msg.cmd));
    115. int file_fd = open(file_path,O_RDWR|O_CREAT,0666); //创建一个文件,写入客户端想要存放的文件的数据
    116. if(file_fd < 0){
    117. perror("open");
    118. }
    119. if(write(file_fd,msg.data,strlen(msg.data)) < 0){
    120. perror("write");
    121. }
    122. close(file_fd);
    123. break;
    124. }
    125. }
    126. int main(int argc,char **argv)
    127. {
    128. struct sockaddr_in so_addr;
    129. struct sockaddr_in cl_addr;
    130. if(argc != 3){
    131. printf("Insufficient or inconsistent parameters\n");
    132. exit(-1);
    133. }
    134. memset(&so_addr,0,sizeof(struct sockaddr_in));
    135. memset(&cl_addr,0,sizeof(struct sockaddr_in));
    136. //1.socket
    137. int s_fd = socket(AF_INET,SOCK_STREAM,0);
    138. if(s_fd == -1){
    139. printf("socket is failusr\n");
    140. perror("socket");
    141. exit(-1);
    142. }
    143. so_addr.sin_family = AF_INET;
    144. so_addr.sin_port = htons(atoi(argv[2])); //将端口号转化成网络字节序
    145. inet_aton(argv[1],&so_addr.sin_addr); //将IP地址转化为网络字节序
    146. //2.bind
    147. int bd = bind(s_fd,(struct sockaddr *)&so_addr,sizeof(struct sockaddr_in));
    148. if(bd == -1){
    149. printf("bind is failusr\n");
    150. perror("bind");
    151. exit(-1);
    152. }
    153. //3.listen
    154. int ln = listen(s_fd,10);
    155. if(ln == -1){
    156. printf("listen is failusr\n");
    157. perror("listen");
    158. exit(-1);
    159. }
    160. int c_fd;
    161. int mark = 0;
    162. int clean = sizeof(struct sockaddr_in);
    163. pid_t pid;
    164. while(1){
    165. //4.accept
    166. c_fd = accept(s_fd,(struct sockaddr *)&cl_addr,&clean);
    167. if(c_fd == -1){
    168. printf("accept is failusr\n");
    169. perror("accept:");
    170. exit(-1);
    171. }
    172. mark++;
    173. printf("get No.%d connect: %s\n",mark,inet_ntoa(cl_addr.sin_addr));
    174. //连接成功后创建子进程接受客户端命令
    175. pid = fork();
    176. if(pid < 0){
    177. perror("fork");
    178. exit(-1);
    179. }
    180. else if(pid == 0){
    181. char *fp = NULL;
    182. struct Msg msg;
    183. int ret = 0;
    184. int c_read;
    185. while(1){
    186. memset(msg.cmd,0,sizeof(msg.cmd));
    187. c_read = read(c_fd,&msg,sizeof(msg)); //读取客户端发送的命令
    188. if(c_read <= 0){
    189. perror("read");
    190. break;
    191. }else{
    192. printf("get size is %d's cmd from NO.%d client:%s\n",c_read,mark,msg.cmd);
    193. ret = cmd_distinguish(msg.cmd); //对客户端传送的命令进行识别
    194. cmd_handle(ret,msg,c_fd); //对相关命令进行处理
    195. }
    196. }
    197. }
    198. close(c_fd);
    199. }
    200. close(s_fd);
    201. return 0;
    202. }
    客户端代码:
    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. struct Msg
    13. {
    14. int type;
    15. char cmd[50];
    16. char data[5120];
    17. };
    18. #define LLS 1
    19. #define LCD 2
    20. #define LPWD 3
    21. #define CD 4
    22. #define LQUIT 7
    23. #define PUT 8
    24. #define CMD 9
    25. #define QUIT 12
    26. #define LS 13
    27. #define PWD 14
    28. #define GET 15
    29. int cmd_distinguish(char *cmd)
    30. {
    31. int ret;
    32. cmd = strtok(cmd,"\n");
    33. if(!strcmp("lls",cmd)){
    34. ret = LLS;
    35. }else if(strstr(cmd,"lcd") != NULL){
    36. ret = LCD;
    37. }else if(!strcmp("lpwd",cmd)){
    38. ret = LPWD;
    39. }else if(!strcmp("ls",cmd)){
    40. ret = LS;
    41. }else if(!strcmp("lquit",cmd)){
    42. ret = LQUIT;
    43. }else if(strstr(cmd,"cd") != NULL){
    44. ret = CD;
    45. }else if(strstr(cmd,"get") != NULL){
    46. ret = GET;
    47. }else if(strstr(cmd,"put") != NULL){
    48. ret = PUT;
    49. }else if(!strcmp("pwd",cmd)){
    50. ret = PWD;
    51. }else{
    52. ret = 0;
    53. }
    54. return ret;
    55. }
    56. char *str_decompose(char *cmd)
    57. {
    58. strtok(cmd," ");
    59. cmd = strtok(NULL," ");
    60. return cmd;
    61. }
    62. int cmd_handle(int ret,struct Msg msg,int c_fd)
    63. {
    64. FILE *fp = NULL;
    65. int n_fread;
    66. char file_path[20] = "./";
    67. char tmp_cmd[50];
    68. switch(ret){
    69. case LPWD:
    70. printf("--------------------------------------\n");
    71. printf("\n");
    72. system("pwd"); //调用终端执行pwd命令,并将数据输出到标准输出上
    73. printf("\n");
    74. printf("--------------------------------------\n");
    75. printf("\n");
    76. break;
    77. case LLS:
    78. printf("--------------------------------------\n");
    79. printf("\n");
    80. system("ls"); //调用终端执行ls命令,并将数据输出到标准输出上
    81. printf("\n");
    82. printf("--------------------------------------\n");
    83. printf("\n");
    84. break;
    85. case LCD:
    86. if(chdir(str_decompose(msg.cmd)) < 0){//修改当前工作目录
    87. perror("chdir");
    88. }
    89. printf("\n");
    90. break;
    91. case LQUIT:
    92. printf("--------------------------------------\n");
    93. printf("\n");
    94. printf("client is quit\n");
    95. printf("\n");
    96. printf("--------------------------------------\n");
    97. printf("\n");
    98. exit(0);//退出当前进程
    99. break;
    100. case LS:
    101. msg.type = 0;
    102. write(c_fd,&msg,sizeof(msg)); //将用户输入发送给服务器,让服务器根据命令进行相应的行为
    103. break;
    104. case PWD:
    105. msg.type = 0;
    106. write(c_fd,&msg,sizeof(msg)); //将用户输入发送给服务器,让服务器根据命令进行相应的行为
    107. break;
    108. case CD:
    109. msg.type = 0;
    110. write(c_fd,&msg,sizeof(msg)); //将用户输入发送给服务器,让服务器根据命令进行相应的行为
    111. break;
    112. case GET:
    113. msg.type = 0;
    114. write(c_fd,&msg,sizeof(msg)); //将用户输入发送给服务器,让服务器根据命令进行相应的行为
    115. break;
    116. case PUT:
    117. memset(tmp_cmd,0,sizeof(tmp_cmd));
    118. strcpy(tmp_cmd,msg.cmd); //put 1.c ->tmp.cmd
    119. strcat(file_path,str_decompose(msg.cmd)); // ./1.c->file_path
    120. memset(msg.cmd,0,sizeof(msg.cmd));
    121. strcpy(msg.cmd,tmp_cmd);
    122. if(access(file_path,F_OK) < 0){ //判断客户端是否拥有想要存放到服务器的文件
    123. printf("--------------------------------------\n");
    124. printf("\n");
    125. printf("Target file does not exist\n");
    126. printf("\n");
    127. printf("--------------------------------------\n");
    128. printf("\n");
    129. }else if(access(file_path,R_OK) < 0){ //判断客户端是否对该文件有读权限
    130. printf("--------------------------------------\n");
    131. printf("\n");
    132. printf("The target file does not have read permission\n");
    133. printf("\n");
    134. printf("--------------------------------------\n");
    135. printf("\n");
    136. }else{ //获取该文件的内容,发送给服务器
    137. int pfile_fd = open(file_path,O_RDONLY,0666);
    138. if(pfile_fd < 0){
    139. perror("open");
    140. }
    141. int file_size = lseek(pfile_fd,0,SEEK_END);
    142. if(file_size < 0){
    143. perror("lseek");
    144. }
    145. lseek(pfile_fd,0,SEEK_SET);
    146. memset(msg.data,0,sizeof(msg.data));
    147. if(read(pfile_fd,msg.data,file_size) < 0){
    148. perror("read");
    149. }
    150. close(pfile_fd);
    151. write(c_fd,&msg,sizeof(msg));
    152. printf("--------------------------------------\n");
    153. printf("\n");
    154. printf("Target file is on server\n");
    155. printf("\n");
    156. printf("--------------------------------------\n");
    157. printf("\n");
    158. }
    159. break;
    160. default:
    161. printf("--------------------------------------\n");
    162. printf("\n");
    163. printf("Input error, please re-enter\n");
    164. printf("\n");
    165. printf("--------------------------------------\n");
    166. printf("\n");
    167. break;
    168. }
    169. return ret;
    170. }
    171. //对服务器发送的数据进行处理
    172. void from_socket_data_handle(struct Msg msg,int c_fd)
    173. {
    174. struct Msg socket_msg;
    175. int n_read;
    176. int file_fd;
    177. char file_path[20] = "./";
    178. n_read = read(c_fd,&socket_msg,sizeof(socket_msg));
    179. if(n_read == -1){
    180. perror("read socket");
    181. exit(-1);
    182. }else if(socket_msg.type == GET){ //如果是GET指令返回的数据,就存进文件中
    183. strcat(file_path,str_decompose(msg.cmd));
    184. file_fd = open(file_path,O_RDWR|O_CREAT,0666);
    185. write(file_fd,socket_msg.data,strlen(socket_msg.data));
    186. fflush(stdout);
    187. close(file_fd);
    188. printf("--------------------------------------\n");
    189. printf("\n");
    190. printf("%s\n","Get target file from server\n");
    191. printf("--------------------------------------\n");
    192. printf("\n");
    193. }else{ //如果是其他数据就直接打印出来
    194. printf("--------------------------------------\n");
    195. printf("\n");
    196. printf("%s\n",socket_msg.data);
    197. printf("\n");
    198. printf("--------------------------------------\n");
    199. printf("\n");
    200. }
    201. }
    202. int main(int argc,char **argv)
    203. {
    204. struct sockaddr_in c_addr;
    205. memset(&c_addr,0,sizeof(struct sockaddr_in));
    206. //1.socket
    207. int c_fd = socket(AF_INET,SOCK_STREAM,0);
    208. if(c_fd == -1){
    209. printf("socket is failusr\n");
    210. perror("socket");
    211. exit(-1);
    212. }
    213. c_addr.sin_family = AF_INET;
    214. c_addr.sin_port = htons(atoi(argv[2])); //将端口号转化成网络字节序
    215. inet_aton(argv[1],&c_addr.sin_addr); //将IP地址转化为网络字节序
    216. //2.connect
    217. if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){
    218. perror("connect");
    219. exit(-1);
    220. }
    221. int n_read;
    222. struct Msg msg;
    223. int ret = 0;
    224. int mark = 0;
    225. int tmp = 0;
    226. while(1){
    227. memset(msg.cmd,0,sizeof(msg.cmd));
    228. memset(msg.data,0,sizeof(msg.data));
    229. fgets(msg.cmd,sizeof(msg.cmd),stdin); //获取用户输入的命令
    230. ret = cmd_distinguish(msg.cmd); //对用户键入的命令进行识别
    231. tmp = cmd_handle(ret,msg,c_fd); //对命令进行处理
    232. if(tmp > CMD){
    233. from_socket_data_handle(msg,c_fd); //对服务器传送的数据进行处理
    234. }
    235. fflush(stdout);
    236. }
    237. close(c_fd);
    238. return 0;
    239. }

  • 相关阅读:
    使用Python爬虫抓取网站资源的方法
    Spring 常见面试题
    elasticsearch-.yml(中文配置详解)
    Keras Sequential 模型
    C- wait() & waitpid() & status变量
    java常量和变量
    异常点检测算法(隔离森林、局部离群因子)
    C++-openssl-aes-cbc-pkcs5
    mysql8.0英文OCP考试第131-140题
    如何实现IM即时通讯“消息”列表卡顿优化
  • 原文地址:https://blog.csdn.net/m0_74712453/article/details/132819600