Linux网络编程学习之---简单局域网FTP文件传输服务器
2015-04-06 19:09
525 查看
这两天在学习Linux网络编程,老师留了个小作业,就是做一个基于TCP协议的文件服务器。具体要求如下:
编写TCP文件服务器和客户端。
客户端功能:
支持如下指令:
help:显示客户端所有命令和说明。
list:先是服务器端可下载文件列表。
get filename:下载文件。
put filenme:上传文件。
quit:退出。
服务器端功能(单进程):
解析客户端指令并提供相应服务。
以下是代码:
ftp_server.c
ftp_client.c
network.h
network.c
<完>
第一次发贴,不足之处还望见谅
学习交流联系:
whjwnavy@163.com
805400349@qq.com
编写TCP文件服务器和客户端。
客户端功能:
支持如下指令:
help:显示客户端所有命令和说明。
list:先是服务器端可下载文件列表。
get filename:下载文件。
put filenme:上传文件。
quit:退出。
服务器端功能(单进程):
解析客户端指令并提供相应服务。
以下是代码:
ftp_server.c
/* * ftp_server.c * * Copyright 2015 wnavy <whjwnavy@163.com> * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/stat.h> #include <fcntl.h> #include "network.h" #define INET_NUM 8888//端口号 #define IP_ADDRESS "192.168.1.111"//IP地址 #ifndef BUFF_LEN #define BUFF_LEN 1028 #endif #ifndef CMD_HELP #define CMD_HELP 11//帮助 #define CMD_LIST 22//列出所有文件 #define CMD_GET 33//下载文件 #define CMD_PUT 44//上传文件 #define CMD_QUIT 55//退出 #define CMD_ERROR -1//错误 #endif int main(void) { int sockfd;//网络套接字 //初始化网络连接 sockfd = network_init(NET_SERVER, IP_ADDRESS, INET_NUM); if(sockfd == -1) { perror("Network init error!"); exit(EXIT_FAILURE); } printf("LISTEN-ing...\n"); char RCV_BUFF[BUFF_LEN];//接收缓存 char SEND_BUFF[BUFF_LEN];//发送缓存 int cmd_result;//命令解析结果 int connectfd;//建立连接后用于通信的套接字文件 int readlen;//读取到的字节数 while(1) { if((connectfd = accept(sockfd, NULL, NULL)) == -1)//出错 { perror("Connect error!\n"); break; } printf("Connect success!\n"); while(1) { //接收命令 readlen = read(connectfd, RCV_BUFF, sizeof(RCV_BUFF)); if(readlen <0)//接收出错 { perror("Read error!\n"); break; } else { if(readlen == 0)//客户端关闭文件描述符后就会断开连接 { printf("Welcome to use again!\nQUIT...\n"); break; } else { //printf("RECV:%s\n",RCV_BUFF); cmd_result = ftp_cmd_analyse(RCV_BUFF);//解析命令 switch(cmd_result) { case CMD_ERROR: printf("CMD_ERROR!\n"); break; case CMD_LIST: printf("List file:%s\n",RCV_BUFF+5); if(ftp_putlist(connectfd, RCV_BUFF+5) == -1) { printf("List files error!\n"); } else { printf("List files success!\n"); } break; case CMD_GET://客户端从服务器下载文件 printf("Put files:%s\n", RCV_BUFF+4); if(ftp_putfile(connectfd, RCV_BUFF+4) == -1) { printf("Put files error!\n"); } else { printf("Put files success!\n"); } break; case CMD_PUT://客户端上传文件到服务器 printf("Get files:%s\n", RCV_BUFF+4); if(ftp_getfile(connectfd, RCV_BUFF+4) == -1) { printf("Get files error!\n"); } else { printf("Get files success!\n"); } break; default: break; } } } } close(connectfd);//客户端退出,断开连接 } close(sockfd); return 0; }
ftp_client.c
/* * ftp_client.c * * Copyright 2015 wnavy <whjwnavy@163.com> * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/stat.h> #include <fcntl.h> #include "network.h" #define INET_NUM 8888//端口号 #define IP_ADDRESS "192.168.1.111"//服务器IP #ifndef BUFF_LEN #define BUFF_LEN 1028 #endif #ifndef CMD_HELP #define CMD_HELP 11//帮助 #define CMD_LIST 22//列出所有文件 #define CMD_GET 33//下载文件 #define CMD_PUT 44//上传文件 #define CMD_QUIT 55//退出 #define CMD_ERROR -1//错误 #endif int main(void) { int sockfd; //初始化网络连接 sockfd = network_init(NET_CLIENT, IP_ADDRESS, INET_NUM); if(sockfd == -1) { perror("Network init error!"); exit(EXIT_FAILURE); } printf("Connect success!\n"); //向服务器发送数据 char SEND_BUFF[BUFF_LEN]; //char RCV_BUFF[BUFF_LEN]; int cmd_result;//命令解析结果 while(1) { fgets(SEND_BUFF, sizeof(SEND_BUFF), stdin);//从键盘输入命令 SEND_BUFF[strlen(SEND_BUFF)-1] = '\0';//去掉最后输入的回车符 cmd_result = ftp_cmd_analyse(SEND_BUFF);//解析命令 switch(cmd_result) { case CMD_ERROR: printf("ERROR!\n"); break; //////////////////////////////////////////////////////////// case CMD_HELP: ftp_print_help(); break; //////////////////////////////////////////////////////////// case CMD_LIST: printf("List file:%s\n",SEND_BUFF+5); //列出服务器端可下载的所有文件 if(ftp_getlist(sockfd, SEND_BUFF+5) == -1) { printf("List file error!\n"); } else { //printf("List file success!\n"); } break; //////////////////////////////////////////////////////////// case CMD_QUIT://跳出循环,关闭文件描述符 printf("Welcome to use again!\nQUIT!\n"); close(sockfd);//客户端关闭文件描述符后就会自动断开连接 exit(EXIT_SUCCESS); break; //////////////////////////////////////////////////////////// case CMD_GET://下载文件 printf("Download file:%s\n",SEND_BUFF+4); //向服务器发送命令 if(write(sockfd, SEND_BUFF, BUFF_LEN) == -1) { perror("Send cmd error!"); break; } if(ftp_getfile(sockfd, SEND_BUFF+4) == -1)//下载文件 { printf("Download error!\n"); } else { printf("Download success!\n"); } break; //////////////////////////////////////////////////////////// case CMD_PUT: printf("Upload file:%s\n",SEND_BUFF+4); //向服务器发送命令 if(write(sockfd, SEND_BUFF, BUFF_LEN) == -1) { perror("Send cmd error!"); break; } if(ftp_putfile(sockfd, SEND_BUFF+4) == -1)//上传文件 { printf("Upload error!\n"); } else { printf("Upload success!\n"); } break; //////////////////////////////////////////////////////////// default: break; } } close(sockfd);//客户端关闭文件描述符后就会自动断开连接 return 0; }
network.h
/* * network.h * * Copyright 2015 wnavy <whjwnavy@163.com> * */ #ifndef __NETWORK_H_ #define __NETWORK_H_ #define NET_SERVER 11 #define NET_CLIENT 22 #define BUFF_LEN 1028//接收发送缓冲区大小 #define CMD_HELP 11//帮助 #define CMD_LIST 22//列出所有文件 #define CMD_GET 33//下载文件 #define CMD_PUT 44//上传文件 #define CMD_QUIT 55//退出 #define CMD_ERROR -1//错误 #define E_NOFILE "ERROR:No such file or directory!\n" #define E_DODNLOAD "ERROR:Download error!\n" #define E_UPLOAD "ERROR:Upload error!\n" #define GET_LIST_END "SUCCESS:GET LIST SUCCESS!" int network_init(int net_type, const char* IP_ADDRESS, \ short INET_NUM); void ftp_print_help(void); int ftp_cmd_analyse(const char* cmd); int ftp_getlist(int getsockfd, const char* LIST_NAME); int ftp_putlist(int putsockfd, const char* LIST_NAME); int ftp_getfile(int getsockfd, const char* GET_FILENAME); int ftp_putfile(int putsockfd, const char* PUT_FILENAME); #endif
network.c
/* * network.c * * Copyright 2015 wnavy <whjwnavy@163.com> * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/stat.h> #include <fcntl.h> #include "network.h" #include <stddef.h> #include <dirent.h> #define BACKLOG 10//最大连接数 /*********************************************************************** * 函数作用:网络初始化 * 函数参数: * net_type:网络类型 * NET_SERVER:服务器 * NET_CLIENT:客户端 * NET_IP:IP地址 * NET_NUM:端口号 * 返回值: * 已经创建好的网络的套接字 * 说明: * 无 **********************************************************************/ int network_init(int net_type, const char* NET_IP, short NET_NUM) { int sockfd; //创建监听套接字 if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("Create socket error!"); //exit(EXIT_FAILURE); return -1; } struct sockaddr_in sockadd; memset(&sockadd, 0, sizeof(sockadd)); sockadd.sin_family = AF_INET; sockadd.sin_port = htons(NET_NUM); sockadd.sin_addr.s_addr = inet_addr(NET_IP); if(net_type == NET_CLIENT) { //连接服务器 if(connect(sockfd, (struct sockaddr*)(&sockadd), \ sizeof(sockadd)) == -1) { perror("Connect error!"); //exit(EXIT_FAILURE); return -1; } } else { if(net_type == NET_SERVER) { //绑定IP地址断口号 if(bind(sockfd, (struct sockaddr*)(&sockadd), \ sizeof(sockadd)) == -1) { perror("Bind error!"); //exit(EXIT_FAILURE); return -1; } //监听客户端 if(listen(sockfd, BACKLOG) == -1) { perror("Listen error!"); //exit(EXIT_FAILURE); return -1; } } else { return -1; } } return sockfd; } /*********************************************************************** * 函数作用:命令解析 * 函数参数: * cmd:存放命令的字符串 * 返回值: * 命令解析结果(事先定义好的宏) * 说明: * 带参数的命令,参数与命令之间用空格分开 **********************************************************************/ int ftp_cmd_analyse(const char* cmd) { if(cmd == NULL) { return CMD_ERROR; } else { if(strcmp(cmd, "help") == 0)//无参数命令 return CMD_HELP; else { if(strncmp(cmd, "list ", 5) == 0)//有参数命令 return CMD_LIST; else { if(strcmp(cmd, "quit") == 0)//无参数命令 return CMD_QUIT; else { if(strncmp(cmd, "get ", 4) == 0)//有参数命令 return CMD_GET; else { if(strncmp(cmd, "put ", 4) == 0)//有参数命令 return CMD_PUT; else { return CMD_ERROR; } } } } } } } /*********************************************************************** * 函数作用:打印帮助信息 * 函数参数: * 无 * 返回值: * 无 * 说明: * 无 **********************************************************************/ void ftp_print_help(void) { printf("--------------------------------\n"); /*printf("Thankyou to use our system!\n"); printf("Creat by: WHJWNAVY\n"); printf("Contact us: whjwnavy@163.com\n\n");*/ printf("Command List:\n"); printf("help: search for help.\n"); printf("list: list all files on server.\n"); printf("quit: exit this system.\n"); printf("get filename: download file.\n"); printf("put filename: upload file.\n"); } /*********************************************************************** * 函数作用:从服务器端获取文件列表 * 函数参数: * getsockfd:用于接收文件的文件描述符 * LIST_NAME:路径 * 返回值: * 成功:非负值(getsockfd) * 失败:-1 * 说明: * 文件路径末尾的"/"不能省略! * "list /home"这种写法是不对的,必须写成“list /home/” **********************************************************************/ int ftp_getlist(int getsockfd, const char* LIST_NAME) { //接收缓存,BUFF_LEN必须定义,否则无法使用本函数!!! char GET_BUFF[BUFF_LEN]; int readsize; sprintf(GET_BUFF, "list "); sprintf(GET_BUFF+5, "%s", LIST_NAME); if(write(getsockfd, GET_BUFF, BUFF_LEN) == -1)//向服务器发送命令 { perror("Send cmd error!"); return -1; } else { while(1)//循环读取 { readsize = read(getsockfd, GET_BUFF, BUFF_LEN); if(readsize <= 0)//读错误 { perror("Get list error!"); return -1; } else { if(strncmp(GET_BUFF, GET_LIST_END, \ sizeof(GET_LIST_END)) == 0)//判断服务器是否发送完毕 { printf("\n"); break;//发送完毕,退出 } else { printf("%s", GET_BUFF);//服务器端发送完毕,显示文件 } } } } return getsockfd; } /*********************************************************************** * 函数作用:把服务器端文件列表发送到客户端 * 函数参数: * putsockfd:用于接收文件的文件描述符 * LIST_NAME:路径 * 返回值: * 成功:非负值(putsockfd) * 失败:-1 * 说明: * 无 **********************************************************************/ int ftp_putlist(int putsockfd, const char* LIST_NAME) { char PUT_BUFF[BUFF_LEN]; int strn, strm; DIR* dp; struct dirent *ep; struct stat st; char LIST_PATH[256]; dp = opendir(LIST_NAME);//打开目录 if(dp != NULL)//打开目录成功 { while(ep = readdir(dp))//循环读目录 { if(ep->d_name[0] != '.')//如果不是隐藏文件或目录 { //用stat函数读取文件信息,文件名带路径 sprintf(LIST_PATH + sprintf(LIST_PATH, "%s", LIST_NAME), \ "%s", ep->d_name);//为文件名加上路径 if(stat(LIST_PATH, &st) != -1) { mode_t filemode = st.st_mode & S_IFMT; switch (filemode) { case S_IFDIR: strn = sprintf(PUT_BUFF, "DIRE\t");//目录 break; case S_IFREG: strn = sprintf(PUT_BUFF, "FILE\t");//文件 break; case S_IFBLK: strn = sprintf(PUT_BUFF, "BLCK\t");//块文件 break; case S_IFCHR: strn = sprintf(PUT_BUFF, "SPEC\t");//特殊文件 break; case S_IFIFO: strn = sprintf(PUT_BUFF, "PIPE\t");//管道文件 break; default: break; } strm = strn; strn = sprintf(PUT_BUFF+strm, "%o\t", st.st_mode & \ 0x1ff);//权限 strm += strn; strn = sprintf(PUT_BUFF+strm, "%ld\t", st.st_size); strm += strn; strn = sprintf(PUT_BUFF+strm, "%s\n", ep->d_name); strn = 0; strm = 0; //把字符串写回客户端 if(write(putsockfd, PUT_BUFF, BUFF_LEN) == -1) { perror("Put list error!");//接收出错,返回 return -1; } memset(PUT_BUFF, 0, BUFF_LEN); } else { perror("Stat error!"); return -1; } } } if(write(putsockfd, GET_LIST_END, BUFF_LEN) == -1)//发送结束 { perror("Write endstring error!"); return -1; } } else { if(write(putsockfd, GET_LIST_END, BUFF_LEN) == -1)//发送结束 { perror("Write endstring error!"); return -1; } perror("Can't open the directory!"); return -1; } closedir(dp); return putsockfd; } /*********************************************************************** * 函数作用:接收文件 * 函数参数: * getsockfd:用于接收文件的文件描述符 * GET_FILENAME:带路径的文件名 * 返回值: * 成功:接收到的文件的文件描述符 * 失败:-1 * 说明: * GET数据包格式: * 数据包总长度为1028个字节 * 数据包头区:前4个字节,存放数据区大小 * 数据内容区:后1024个字节,存放实际的数据 * * 服务器每次会从文件中读取1024个字节加上包头(4个字节)后发送给客户端。 * 最后一次(读到文件末尾)时实际读取的数据小于1024个字节。可以以此判断 * 是否读取结束。 * * 使用本函数前BUFF_LEN必须定义,否则无法使用或造成无法预知的结果!!! **********************************************************************/ int ftp_getfile(int getsockfd, const char* GET_FILENAME) { int getfilefd;//存放接收文件的文件描述符 int getfilesize;//实际接收的文件大小 /* * #ifndef BUFF_LEN * #define BUFF_LEN 1028 * #endif */ //接收缓存,BUFF_LEN必须定义,否则无法使用本函数!!! char GET_BUFF[BUFF_LEN]; //打开一个文件描述符用与保存来自发送端的文件 if((getfilefd = open(GET_FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0666)) \ == -1) { perror("Can't open or creat file!"); return -1; } else { //接收文件 while(getfilesize = read(getsockfd, GET_BUFF, BUFF_LEN) > 0) { /*发送端出错会发送一个“ERROR:xxxx”格式的字符串,接收端以此判断 是否出错,如果发送端不发送此错误信息,发送端出错后挂机,接收端也 将卡机*/ if(strncmp(GET_BUFF, "ERROR:", 6) == 0)//接收文件出错 { printf("%s", GET_BUFF); return -1; } else { //取出数据包头中包含的数据区大小 memcpy(&getfilesize, GET_BUFF, 4); /*GET_BUFF+4是因为数据包前四个字节存放的是数据长度,之后的 1024个字节才存放的实际的数据*/ if(write(getfilefd, GET_BUFF+4, getfilesize) == -1) { perror("Download error!");//接收出错,返回 close(getfilefd);//关闭文件 return -1; } if(getfilesize < (BUFF_LEN-4))//已经读取到文件末尾 break;//接收结束,退出 } } close(getfilefd);//关闭文件 return getfilefd;//接收完成,返回接收到的文件的文件描述符。 /*但是此值仅有数值上的意义,无实际作用。因为该文件在函数返回前已被 关闭要想在函数中通过此文件描述符打开此文件,就不要在此函数结束时关 闭该文件!*/ } } /*********************************************************************** * 函数作用:发送文件 * 函数参数: * putsockfd:用于发送文件的文件描述符 * PUT_FILENAME:带路径的文件名 * 返回值: * 成功:发送的文件的文件描述符 * 失败:-1 * 说明: * 无 **********************************************************************/ int ftp_putfile(int putsockfd, const char* PUT_FILENAME) { int putfilefd;//存放接收文件的文件描述符 int putfilesize;//实际接收的文件大小 /* * #ifndef BUFF_LEN * #define BUFF_LEN 1028 * #endif */ //接收缓存,BUFF_LEN必须定义,否则无法使用本函数!!! char PUT_BUFF[BUFF_LEN]; if((putfilefd = open(PUT_FILENAME, O_RDONLY)) == -1)//打开文件 { perror("Open error!"); write(putsockfd, E_NOFILE, BUFF_LEN);//把错误信息写回。 /*如果不写回错误信息,发送端会卡死*/ return -1; } else { //printf("Open %s success!\n",PUT_FILENAME); /* * 先从文件中读取1024个字节放入发送缓冲区的第五个字节开始的1024个字 * 节中,后在把实际读取到的字节数放入发送缓冲区的前四个字节中(int型), * 最后再把这1028个字节发送出去。 */ while((putfilesize = read(putfilefd, PUT_BUFF+4, (BUFF_LEN-4))) \ >0) { memcpy(PUT_BUFF, &putfilesize, 4); if(write(putsockfd, PUT_BUFF, BUFF_LEN) == -1) { perror("Put file error!"); close(putfilefd); return -1; } memset(PUT_BUFF, 0, BUFF_LEN);//清空缓冲区 //printf("\rDownloading..."); } } close(putfilefd); return putfilefd; }
<完>
第一次发贴,不足之处还望见谅
学习交流联系:
whjwnavy@163.com
805400349@qq.com
相关文章推荐
- Qt学习心得之网络编程简单的局域网聊天服务端建立
- Qt学习心得之网络编程简单的局域网聊天服务端建立
- javaME学习之-简单网络编程
- 百度笔试题2005题目大致是这样的: 第一部分选择题: 有几道网络相关的题目,巨简单,比如第一题是TCP、RIP、IP、FTP中哪个协议是传输层的......。有一道linux的 chown使用题目。其他的全是数据结构的题目!什么链,表
- Linux下使用C做简单的网络编程
- Linux Socket 网络编程 基于GTK+ 的多线程实现的局域网通信软件
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 简单UDP Socket 通信
- Proxy源代码分析--谈谈如何学习linux网络编程
- Linux网络编程:一个简单的正向代理服务器的实现
- Linux下使用C做简单的网络编程
- 简单linux网络编程
- Linux网络编程基础之二--UDP --Unix学习总结之四
- 【嵌入式Linux学习七步曲之第七篇 Linux的高级应用编程】网络编程中并发服务器的设计模式
- Linux下使用C做简单的网络编程
- Proxy源代码分析--谈谈如何学习linux网络编程 [转]
- 网络编程--简单实现javaftp服务器
- perl网络编程学习系列:Net:FTP
- vsFTPd-Linux网络安装与配置FTP服务器及Redhat局域网安装的解决办法
- [Linux网络编程]ARP简单实例
- UNIX环境高级编程学习之第十六章网络IPC:套接字 - 简单TCP Socket 通信