使用epoll方法,用c/c++实现一个FTP服务器
Posted Madao东治
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用epoll方法,用c/c++实现一个FTP服务器相关的知识,希望对你有一定的参考价值。
先贴个代码上来,晚点补全教程
编译环境:ubuntu16.04
编译命令(先编译执行服务端):g++ serv.cpp -o serv.out
./serv.out
客户端:g++ client.cpp -o serv.cpp
./client.out
可以实现三种命令:get <filename>获取服务端文件夹内指定文件
ls 获取文件列表
quit 退出
服务端代码:
1 #include<iostream> 2 #include<stdlib.h> 3 #include<unistd.h> 4 #include<errno.h> 5 #include<sys/socket.h> 6 #include<arpa/inet.h> 7 #include<sys/epoll.h> 8 #include<string.h> 9 #include<fcntl.h> 10 #include<cstdio> 11 using namespace std; 12 13 #define SER_IP "127.0.0.1" 14 15 // server port 16 #define SER_PORT 8860 17 18 //epoll size 19 20 #define EPOLL_SIZE 0xFFFF 21 #define BUF_SIZ 0xFFFF 22 #define BUF_SIZE 0xFFFF 23 24 void ErrorHandling(string message){ 25 26 cout<<message<<endl; 27 system("pause"); 28 exit(-1); 29 } 30 /*设置epoll为边缘触发模式*/ 31 void setnoblockingmode(int fd){ 32 33 fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); 34 35 } 36 37 bool close_connection(int epfd,int cnlt_socket) 38 39 { 40 41 cout<<"client "<<cnlt_socket<<"disconnected"<<endl; 42 close(cnlt_socket); 43 epoll_ctl(epfd,EPOLL_CTL_DEL,cnlt_socket,NULL); 44 cout<<"close done!"<<endl; 45 46 } 47 48 bool send_file(int cnlt_socket,char *file_name,int epfd) 49 { 50 51 FILE* fd = NULL; 52 char data[BUF_SIZE]; 53 size_t num_read; 54 fd = fopen(file_name, "r"); // 打开文件 55 cout<<"file_name is"<<file_name<<endl; 56 if (!fd){ 57 char send_error[BUF_SIZE]; 58 sprintf(send_error,"550"); 59 send(cnlt_socket,send_error,BUF_SIZE,0); 60 close_connection(epfd,cnlt_socket); 61 } 62 else 63 { 64 cout<<"file open successful"<<endl; ; 65 do 66 { 67 num_read = fread(data, 1, BUF_SIZE, fd); // 读文件内容 68 if (num_read < 0) 69 printf("error in fread()\\n"); 70 71 if (send(cnlt_socket, data, num_read, 0) < 0) // 发送数据(文件内容) 72 perror("error sending file\\n"); 73 74 }while (num_read > 0); 75 cout<<"transfer done!closing socket"<<endl; 76 close(cnlt_socket); 77 cout<<"closing done!"<<endl; 78 } 79 } 80 81 82 int main() 83 { 84 char tempbuffer[BUF_SIZE]; 85 int serversocket,clientsocket; 86 struct sockaddr_in seraddr,clientaddr; 87 seraddr.sin_family=PF_INET; 88 seraddr.sin_port=htons(SER_PORT); 89 seraddr.sin_addr.s_addr=inet_addr(SER_IP); 90 char message[BUF_SIZ]="connected"; 91 char buffer[BUF_SIZ]; 92 char *copy_buffer; 93 memset(buffer,0,sizeof(buffer)); 94 95 serversocket=socket(PF_INET,SOCK_STREAM,0); 96 97 if(bind(serversocket,(struct sockaddr*)&seraddr,sizeof(seraddr))==-1) 98 perror("connect error"); 99 100 if(listen(serversocket,5)==-1) 101 perror("listen Error"); 102 /*注册epoll*/ 103 int epfd,event_cnt; 104 epfd = epoll_create(EPOLL_SIZE); 105 struct epoll_event ep_event[EPOLL_SIZE]; 106 struct epoll_event event; 107 event.events=EPOLLIN; 108 event.data.fd=serversocket; 109 epoll_ctl(epfd,EPOLL_CTL_ADD,serversocket,&event); 110 111 while(1) 112 { 113 114 event_cnt=epoll_wait(epfd,ep_event,EPOLL_SIZE,-1); 115 116 if(event_cnt==-1) 117 { 118 perror("epoll_wait error"); 119 exit(-1); 120 } 121 122 for(int i=0;i<event_cnt;i++) 123 { 124 125 if(ep_event[i].data.fd==serversocket) 126 { 127 128 socklen_t clientsize; 129 130 clientsocket=accept(serversocket,(struct sockaddr *)&clientaddr,&clientsize); 131 132 if(clientsocket==-1) 133 ErrorHandling("accept Error"); 134 else 135 { 136 137 setnoblockingmode(clientsocket); 138 event.events=EPOLLIN|EPOLLET; 139 event.data.fd=clientsocket; 140 epoll_ctl(epfd,EPOLL_CTL_ADD,clientsocket,&event); 141 142 cout<<"fd added to epoll!"<<endl; 143 cout<<clientsocket<<"connected"<<endl; 144 } 145 } 146 147 else{ 148 149 int strlen=read(ep_event[i].data.fd,buffer,BUF_SIZE); 150 151 if(strlen==0) 152 { 153 154 close_connection(epfd,ep_event[i].data.fd); 155 break; 156 157 }else if(strlen<0) 158 { 159 160 cout<<"strlen error"<<endl; 161 162 } 163 else 164 { 165 char m_copy[BUF_SIZE]; 166 sprintf(m_copy,buffer); 167 copy_buffer=strtok(buffer," "); 168 169 if(strcmp(copy_buffer,"ls") == 0) 170 { 171 /*使用系统命令获取文件清单*/ 172 system("ls >temp.txt"); 173 174 send_file(ep_event[i].data.fd,"temp.txt",epfd); 175 close_connection(epfd,ep_event[i].data.fd); 176 177 }else if(strcmp(copy_buffer,"quit") == 0) 178 179 { 180 181 close_connection(epfd,ep_event[i].data.fd); 182 183 } 184 else if(strcmp(copy_buffer,"get")==0) 185 { 186 187 copy_buffer=strtok(m_copy," "); 188 copy_buffer=strtok(NULL," "); 189 cout<<"file name is: "<<copy_buffer<<endl; 190 send_file(ep_event[i].data.fd,copy_buffer,epfd); 191 192 }else 193 close_connection(epfd,ep_event[i].data.fd); 194 } 195 } 196 } 197 } 198 199 close(serversocket); 200 close(epfd); 201 202 return(0); 203 }
客户端代码:
1 /*write by hakase 2016.12*/ 2 #include<iostream> 3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<errno.h> 6 #include<sys/socket.h> 7 #include<arpa/inet.h> 8 #include<sys/epoll.h> 9 #include<string.h> 10 #include<fcntl.h> 11 #include<cstdio> 12 using namespace std; 13 14 #define SER_PORT 8860 15 #define SER_IP "127.0.0.1" 16 17 #define EPOLL_SIZE 0xFFFF 18 #define BUF_SIZE 0xFFFF 19 #define GET_FILE "get %s" 20 21 void ErrorHandling(string message){ 22 23 cout<<message<<endl; 24 system("pause"); 25 exit(-1); 26 } 27 28 int main(){ 29 30 int ser_socket; 31 struct sockaddr_in seraddr; 32 char message[BUF_SIZE]; 33 char send_message[BUF_SIZE]; 34 bzero(message,0); 35 char q_message[BUF_SIZE]; 36 sprintf(q_message,"quit"); 37 38 /*设置服务器地址*/ 39 seraddr.sin_family=PF_INET; 40 seraddr.sin_port=htons(SER_PORT); 41 seraddr.sin_addr.s_addr=inet_addr(SER_IP); 42 43 /*创建服务端套接字*/ 44 ser_socket=socket(PF_INET,SOCK_STREAM,0); 45 if(ser_socket < 0) { perror("sock error"); exit(-1); } 46 47 48 if(connect(ser_socket, (struct sockaddr *)&seraddr, sizeof(seraddr)) < 0) { 49 perror("connect error"); 50 exit(-1); 51 } 52 else { 53 cout<<"connect!"<<endl; 54 55 int test_flag=1; 56 57 if(test_flag==1){ 58 char data[BUF_SIZE]; 59 int size; 60 char file_name[BUF_SIZE]; 61 char input[BUF_SIZE]; 62 63 /*获取用户的指令*/ 64 gets(file_name); 65 66 /*用于拆分命令的空格行时使用临时空间储存被拆分的命令*/ 67 char *copy_buffer=(char *)malloc(sizeof(char)*100); 68 char *m_copy2=(char *)malloc(sizeof(char)*100); 69 70 sprintf(m_copy2,file_name); 71 72 /*拆分命令*/ 73 copy_buffer=strtok(m_copy2," "); 74 cout<<"request oder is:"<<copy_buffer<<endl; 75 76 /*如果是获取文件清单命令*/ 77 if(strcmp(copy_buffer,"ls")==0){ 78 cout<<"enter ls"<<endl; 79 char temp_data[BUF_SIZE]; 80 81 /*本地建立临时文件,并且从服务端获取文件列表*/ 82 FILE* fd = fopen("temp.txt", "rt+"); 83 send(ser_socket,copy_buffer,BUF_SIZE,0); 84 85 while ((size = read(ser_socket, data, BUF_SIZE)) > 0) 86 fwrite(data, 1, size, fd); 87 88 size_t numb_read=1; 89 do{ 90 numb_read=fread(temp_data,1,BUF_SIZE,fd); 91 if (numb_read < 0) 92 printf("error in fread()\\n"); 93 }while(numb_read>0); 94 95 cout<<temp_data<<endl; 96 cout<<"done!"<<endl; 97 close(ser_socket); 98 return(0); 99 100 } 101 /*如果是退出命令,则直接退出*/ 102 else if(strcmp(copy_buffer,"quit")==0){ 103 send(ser_socket,copy_buffer,BUF_SIZE,0); 104 close(ser_socket); 105 return(0); 106 } 107 /*如果是获取命令,则拆分命令,获得后半部分的文件名字*/ 108 else if(strcmp(copy_buffer,"get")==0){ 109 110 char check[100]; 111 size_t check_lenth; 112 copy_buffer=strtok(file_name," "); 113 copy_buffer=strtok(NULL," "); 114 FILE* fd = fopen(copy_buffer, "w"); 115 sprintf(send_message,"get %s",copy_buffer); 116 //cout<<"send_message is :"<<send_message<<"aaaa"<<endl; 117 send(ser_socket,send_message,BUF_SIZE,0); 118 119 /*在传输文件前,需要先向服务端询问是否存在该文件*/ 120 check_lenth = recv(ser_socket,check,BUF_SIZE,0); 121 /*如果不存在该文件,则转告用户*/ 122 if(strcmp(check,"550")==0){ 123 cout<<"didn\'t find file named "<<copy_buffer<<endl; 124 close(ser_socket); 125 return(0); 126 } 127 /* 将服务器传来的数据(文件内容)写入本地建立的文件 */ 128 while ((size = recv(ser_socket, data, BUF_SIZE, 0)) > 0) 129 fwrite(data, 1, size, fd); 130 131 cout<<"data transfer success"<<endl; 132 133 close(ser_socket); 134 fclose(fd); 135 136 return(0); 137 }else{ 138 /*命令不符合要求*/ 139 cout<<"wrong request"<<endl; 140 send(ser_socket,q_message,BUF_SIZE,0); 141 close(ser_socket); 142 return(0); 143 } 144 } 145 } 146 close(ser_socket); 147 return(0); 148 }
以上是关于使用epoll方法,用c/c++实现一个FTP服务器的主要内容,如果未能解决你的问题,请参考以下文章
6种epoll的做法,从redis,memcached到nginx的网络模型实现