旅游网站建设项目报告论文,免费正版wordpress主题,网站设计稿一般尺寸,西安专业房产网站建设在网络通信过程中#xff0c;服务端通常需要处理多个客户端。由于多个客户端的请求可能会同时到来#xff0c;服务器端可采用不同的方法来处理。总体上来说#xff0c;服务器端可采用两种模型来实现#xff1a;循环服务器模型和并发服务器模型。 循环服务器模型是指服务器端… 在网络通信过程中服务端通常需要处理多个客户端。由于多个客户端的请求可能会同时到来服务器端可采用不同的方法来处理。总体上来说服务器端可采用两种模型来实现循环服务器模型和并发服务器模型。 循环服务器模型是指服务器端依次处理每个客户端直到当前客户端的所有请求处理完毕再处理下一个客户端。这类模型的优点是简单缺点显而易见。特别是TCP循环服务器模型由于必须先处理完当前客户端容易造成其他客户端等待时间较长的情况。 为了提高服务器的并发处理能力又引入了并发服务器模型。其基本思想是在服务器端此阿勇多任务机制多进程或多线程分别为每个客户端创建一个任务来处理极大地提高了服务器的并发处理能力。 下面具体介绍循环服务器模型和并发服务器模型的流程及实现。为了更好的进行对比。本节均以TCP为例讨论相关模型。 一、循环服务器TCP 1、运行介绍 TCP循环服务器是一种常用的模型其工作流程如下 1服务器端从连接请求队列中提取请求建立连接并返回新的已连接套接字 2服务器端通过已连接套接字循环接收数据处理并发送给客户端知道客户端关闭连接 3服务器端关闭已连接套接字返回步骤1 2、特点分析 通过上面对服务器执行过程的介绍可以得到以下结论。 1服务器端采用循环嵌套来实现。外层循环依次提取每个客户端的连接请求建立TCP连接。内层循环接受并处理当前客户端的所有数据知道客户端关闭连接 2如果当前客户端没有处理结束其他客户端必须一直等待。 注意采用这种模型的服务器无法同时为多个客户端服务。 3、编程示例 下面实现 TCP ECHO 服务器端和客户端。服务器端接收到客户端数据后原封不动发送回去回射服务客户端运行时用户从键盘输入字符串发送给服务器端并接受返回的数据直到用户输入quit 后退出。 server.c [cpp] view plaincopy #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include sys/socket.h #include unistd.h #include netinet/in.h #include arpa/inet.h #define BUFFER_SIZE 128 #define PORT 8888 int main() { int listenfd, clientfd; int n; struct sockaddr_in serveraddr,clientaddr; socklen_t peerlen; char buffer[BUFFER_SIZE]; if((listenfd socket(AF_INET, SOCK_STREAM, 0)) -1) { perror(socket error); exit(-1); } else { printf(listenfd:%d\n,listenfd); } memset(serveraddr,0,sizeof(serveraddr)); serveraddr.sin_family AF_INET; serveraddr.sin_port htons(PORT); serveraddr.sin_addr.s_addr htonl(INADDR_ANY); if(bind(listenfd,(struct sockaddr *)serveraddr,sizeof(serveraddr)) 0) //绑定IP地址和端口号 { perror(bind error); exit(-1); } else { printf(bind successfully!\n); } if(listen(listenfd,10) -1) { perror(listen error); exit(-1); } else { printf(listening....\n); } peerlen sizeof(clientaddr); while(1) { if((clientfd accept(listenfd,(struct sockaddr *)clientaddr,peerlen)) 0) //循环等待客户端的连接 { perror(accept error); exit(-1); } else { printf(connection from [%s:%d]\n,inet_ntoa(clientaddr.sin_addr) ,ntohs(clientaddr.sin_port)); } memset(buffer,0,sizeof(buffer)); while(1) { if((n recv(clientfd,buffer,BUFFER_SIZE,0)) -1) //循环接收客户端发送的数据 { perror(recv error); exit(-1); } else if(n 0) //此时客户端断开连接 { break; } else { printf(Received message:%s\n,buffer); if(send(clientfd, buffer, n, 0) -1) { perror(send error); exit(-1); } else { printf(sendmessage:%s\n,buffer); } } } close(clientfd); //客户端断开连接后服务端也断开 } close(listenfd); return 0; } client.c [cpp] view plaincopy #include stdio.h #include string.h #include stdlib.h #include unistd.h #include sys/socket.h #include sys/types.h #include netinet/in.h #include arpa/inet.h #define BUFFER_SIZE 128 #define PORT 8888 int main() { int n; int serverfd, clientfd; struct sockaddr_in serveraddr; char buffer[BUFFER_SIZE]; if((clientfd socket(AF_INET, SOCK_STREAM, 0)) -1) { perror(socket error); exit(-1); } else { printf(clientfd:%d\n,clientfd); } //设置服务端的IP地址和端口号 memset(serveraddr,0,sizeof(serveraddr)); serveraddr.sin_family AF_INET; serveraddr.sin_port htons(PORT); serveraddr.sin_addr.s_addr htonl(INADDR_ANY); if(connect(clientfd,(struct sockaddr *)serveraddr, sizeof(serveraddr)) -1) { perror(connect error); exit(-1); } else { printf(connect successfully!\n); } while(1) { printf(input ); fgets(buffer,sizeof(buffer),stdin); if(strcmp(buffer,quit\n) 0) //遇到quit退出 break; buffer[strlen(buffer) - 1] \0; //将\n去掉 if(send(clientfd,buffer,sizeof(buffer),0) -1) { perror(send error); exit(-1); } memset(buffer,0,sizeof(buffer)); //清空buffer if((n recv(clientfd,buffer,sizeof(buffer),0)) -1) { perror(recv error); exit(-1); } else if(n 0) { break; //若服务端意外关闭 } else { printf(echo:%s\n,buffer); } } close(clientfd); return 0; } 运行结果如下 server [cpp] view plaincopy fsubuntu:~/qiang/netprogram/2$ ./server listenfd:3 bind successfully! listening.... connection from [127.0.0.1:57366] Received message:xiao sendmessage:xiao Received message:zhi sendmessage:zhi Received message:qiang sendmessage:qiang //继续等待下一个客户端的连接 client [cpp] view plaincopy fsubuntu:~/qiang/netprogram/2$ ./client clientfd:3 connect successfully! input xiao echo:xiao input zhi echo:zhi input qiang echo:qiang input quit fsubuntu:~/qiang/netprogram/2$ 二、并发服务器TCP 1、运行介绍 TCP并发服务器模型在网络通信中被广泛使用既可以采用多进程也可以采用多线程来实现。以多进程为例其工作流程如下 1服务器端父进程从连接请求队列中提取请求建立连接并返回新的已连接套接字。 2服务器端父进程创建子进程为客户端服务。客户端关闭连接时子进程结束。 3服务器端父进程关闭已连接套接字返回步骤1 2、特点分析 通过上面对服务器执行过程的介绍可以得到以下结论。 1服务器端父进程一旦接受到客户端的连接请求建立好连接并创建新的子进程。这意味着每个客户端在服务器端有一个专门的子进程为其服务。 2服务器端的多个子进程同时运行宏观上处理多个客户端。 3服务器端的父进程不具体处理每个客户端的数据请求。 注意采用这种模型的服务器端需要避免僵死进程。 3、编程示例 下面采用并发模型实现 TCP ECHO服务器端。 server.c [cpp] view plaincopy #include stdio.h #include stdlib.h #include string.h #include sys/types.h #include sys/socket.h #include unistd.h #include netinet/in.h #include arpa/inet.h #include signal.h #define BUFFER_SIZE 128 #define PORT 8888 #define IP 192.168.3.51 void handler(int signo); int main() { int listenfd, clientfd; int n; pid_t pid; struct sockaddr_in serveraddr,clientaddr; socklen_t peerlen; char buffer[BUFFER_SIZE]; if((listenfd socket(AF_INET, SOCK_STREAM, 0)) -1) { perror(socket error); exit(-1); } else { printf(Socket successfully!\nlistenfd:%d\n,listenfd); } memset(serveraddr,0,sizeof(serveraddr)); serveraddr.sin_family AF_INET; serveraddr.sin_port htons(PORT); serveraddr.sin_addr.s_addr htonl(INADDR_ANY); if(bind(listenfd,(struct sockaddr *)serveraddr,sizeof(serveraddr)) 0) { perror(bind error); exit(-1); } else { printf(bind successfully!\n); printf(local IP:%s port:%d\n,IP,PORT); } if(listen(listenfd,10) -1) { perror(listen error); exit(-1); } else { printf(listening....\n); } signal(SIGCHLD,handler);//捕捉SIGCHLD信号子进程死亡后调用handler回收 peerlen sizeof(clientaddr); while(1) { if((clientfd accept(listenfd,(struct sockaddr *)clientaddr,peerlen)) 0) { perror(accept error); exit(-1); } else { printf(connection from [%s:%d]\n,inet_ntoa(clientaddr.sin_addr) ,ntohs(clientaddr.sin_port)); } memset(buffer,0,sizeof(buffer)); if((pid fork()) 0) { perror(fork error); exit(-1); } else if(pid 0) { close(listenfd); while(1) { if((n recv(clientfd,buffer,BUFFER_SIZE,0)) -1) { perror(recv error); exit(-1); } else if(n 0) { break; } else { printf(Received message:%s\n,buffer); } if(send(clientfd, buffer, n, 0) -1) { perror(send error); exit(-1); } else { printf(sendmessage:%s\n,buffer); } } printf(client is closed\n); exit(0); } else { close(clientfd); } } close(listenfd); return 0; } //接收到SIFCHLD信号后回收子进程 void handler(int signo) { pid_t pid; while((pid waitpid(-1, NULL, WNOHANG)) 0) { printf(child(%d) is over!\n,pid); } } client [cpp] view plaincopy #include stdio.h #include string.h #include stdlib.h #include unistd.h #include sys/socket.h #include sys/types.h #include netinet/in.h #include arpa/inet.h #define BUFFER_SIZE 128 #define PORT 8888 int main() { int n; int serverfd, clientfd; struct sockaddr_in serveraddr; char buffer[BUFFER_SIZE]; if((clientfd socket(AF_INET, SOCK_STREAM, 0)) -1) { perror(socket error); exit(-1); } else { printf(clientfd:%d\n,clientfd); } memset(serveraddr,0,sizeof(serveraddr)); serveraddr.sin_family AF_INET; serveraddr.sin_port htons(PORT); serveraddr.sin_addr.s_addr htonl(INADDR_ANY); if(connect(clientfd,(struct sockaddr *)serveraddr, sizeof(serveraddr)) -1) { perror(connect error); exit(-1); } else { printf(connect successfully!\n); } while(1) { printf(input ); fgets(buffer,sizeof(buffer),stdin); if(strcmp(buffer,quit\n) 0) break; buffer[strlen(buffer) - 1] \0; if(send(clientfd,buffer,sizeof(buffer),0) -1) { perror(send error); exit(-1); } memset(buffer,0,sizeof(buffer)); if((n recv(clientfd,buffer,sizeof(buffer),0)) -1) { perror(recv error); exit(-1); } else if(n 0) { break; } else { printf(echo:%s\n,buffer); } } close(clientfd); return 0; } 执行结果如下 client1: [cpp] view plaincopy fsubuntu:~/qiang/netprogram/3$ ./client clientfd:3 connect successfully! input I am client1! echo:I am client1! input I am client1, how are you! echo:I am client1, how are you! input quit fsubuntu:~/qiang/netprogram/3$ clinet2: [cpp] view plaincopy fsubuntu:~$ telnet 192.168.3.51 8888 Trying 192.168.3.51... Connected to 192.168.3.51. Escape character is ^]. I am client2! I am telnet I am client2! I am telnet I am telnet, I am still alive! I am telnet, I am still alive! server: [cpp] view plaincopy fsubuntu:~/qiang/netprogram/3$ ./server Socket successfully! listenfd:3 bind successfully! local IP:192.168.3.51 port:8888 listening.... connection from [127.0.0.1:45890] Received message:I am client1! sendmessage:I am client1! connection from [192.168.3.51:35721] Received message:I am client2! I am telnet sendmessage:I am client2! I am telnet Received message:I am client1, how are you! sendmessage:I am client1, how are you! client is closed child(7216) is over! Received message:I am telnet, I am still alive! sendmessage:I am telnet, I am still alive! 可以看到实现了并发 三、I/O多路复用并发服务器 I/O多路复用模型可以解决资源限制的问题。此模型实际上时将循环模型用在了上面。服务器用单进程循环处理请求客户端有限的情况下。 但是其存在同样的问题由于服务器是依次处理客户的请求所以可能会导致有的客户等待时间过长。 server [cpp] view plaincopy #include stdio.h #include string.h #include stdlib.h #include unistd.h #include sys/types.h #include sys/socket.h #include sys/select.h #include netinet/in.h #include arpa/inet.h #define PORT 8888 #define MAXSIZE 128 int main() { int i,nbyte; int listenfd, confd, maxfd; char buffer[MAXSIZE]; fd_set global_rdfs, current_rdfs; struct sockaddr_in addr,clientaddr; int addrlen sizeof(struct sockaddr_in); int caddrlen sizeof(struct sockaddr_in); if((listenfd socket(AF_INET, SOCK_STREAM, 0)) -1) { perror(socket error); exit(-1); } else { printf(socket successfully!\n); printf(listenfd : %d\n,listenfd); } memset(addr, 0 ,addrlen); addr.sin_family AF_INET; addr.sin_port htons(PORT); addr.sin_addr.s_addr htonl(INADDR_ANY); if(bind(listenfd,(struct sockaddr *)addr,addrlen) -1) { perror(bind error); exit(-1); } else { printf(bind successfully!\n); printf(listen port:%d\n,PORT); } if(listen(listenfd,5) -1) { perror(listen error); exit(-1); } else { printf(listening...\n); } maxfd listenfd; FD_ZERO(global_rdfs); FD_SET(listenfd,global_rdfs); while(1) { current_rdfs global_rdfs; if(select(maxfd 1,current_rdfs, NULL, NULL,0) 0) { perror(select error); exit(-1); } for(i 0; i listenfd 1; i) { if(FD_ISSET(i, current_rdfs)) //fd 就绪 { if(i listenfd) //有新的连接 { if((confd accept(listenfd,(struct sockaddr *)clientaddr,caddrlen)) -1) { perror(accept error); exit(-1); } else { printf(Connect from [IP:%s PORT:%d]\n, inet_ntoa(clientaddr.sin_addr),clientaddr.sin_port); FD_SET(confd,global_rdfs); maxfd (maxfd confd ? maxfd : confd); } } else { if((nbyte recv(i, buffer, sizeof(buffer),0)) 0) { perror(recv error); exit(-1); } else if(nbyte 0) //客户端close { close(i); FD_CLR(i,global_rdfs); //将其清出 } else { printf(recv:%s\n,buffer); send(i, buffer, sizeof(buffer),0); } } } } } return 0; } 这里使用多路连接TCP服务器执行结果如下 [cpp] view plaincopy fsubuntu:~/qiang/select$ ./select socket successfully! listenfd : 3 bind error: Address already in use fsubuntu:~/qiang/select$ ./select2 socket successfully! listenfd : 3 bind successfully! listen port:8888 listening... Connect from [IP:192.168.3.51 PORT:40075] recv:xiao Connect from [IP:192.168.3.51 PORT:40331] Connect from [IP:192.168.3.84 PORT:36233] Connect from [IP:192.168.3.33 PORT:2499]