TCP 服务器三种模式的简单实现
来源:互联网 发布:科技产品销售网络 编辑:程序博客网 时间:2024/06/05 02:58
TCP 服务器三种模式的简单实现
今天我们来介绍TCP 服务器的三种模式的实现:单用户服务器,多进程多用户服务器,多线程多用户服务器。
要实现一个服务器,不仅要有服务端,还要有客户端,所以本文三种服务器由服务器端代码和客户端这样两块代码构成。
三种服务器的特点和相互之间的联系如下:
单用户服务器:虽然编写起来简单,代码逻辑不复杂,但是服务器一次只能与一个客户端进行数据传输,有一定的局限性。
多进程多用户服务器:利用fork()函数,让线程去进行与客户端的数据传输,使得服务器可以连接多个客户端并进行数据传输。但是由于fork()产生的线程占一定的系统资源,产生的线程数目有上限,使得服务器连接客户端的数量有一定的限制。
多线程多用户服务器:线程相对于进程占用系统资源数目小,而且实现线程分离后,主进程就不用去管理,可以更好进行自己的工作,而线程所做的工作与多线程多进程服务器中进程所做工作一致;即,在实现服务器可以连接多个客户端并进行数据传输的基本功能后,还能在不降低系统性能的情况下,尽可能提高服务器连接多个客户端的数量,这也算是弥补了多线程多进程服务器连接客户端的数量上的限制。
首先介绍单用户服务器的实现步骤,并展示源代码和相应运行实例。
服务器端
1>Main函数传参 argc argv[],包含服务器的端口号8080和IP地址
2>构建服务器listen_socket套接字:
a.用socket函数创建套接字*
b.用bind函数绑定套接字和端口号以及IP
c.用listen函数设置监听,了解是否有客服端要连接服务器
循环体:
3>用accept函数建立链接的new_socket套接字,准备提供服务。
4>服务内容:随时检查对方是否断开连接,并确定自己是否断开(close)连接。(避免空闲连接占用系统资源)
客户端
1>Main函数传参 argc argv[],包含服务器的端口号8080和IP地址
2>用socket函数创建套接字
3>用connect链接服务器
4>服务内容,随时检查对方是否断开连接,并确定自己是否断开(close)连接
源代码:
Servers.c
1#include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 #include<arpa/inet.h> 7 #include<string.h> 8 9 int tcpserve(char* ip, char* proc){ 10 int sock = socket(AF_INET,SOCK_STREAM,0); 11 if(sock<0){ 12 perror("socket"); 13 exit(2); 14 } 15 16 struct sockaddr_in local; 17 local.sin_family = AF_INET; 18 local.sin_port = htons(atoi(proc)); 19 local.sin_addr.s_addr = inet_addr(ip); 20 int len=sizeof(local); 21 22 if(bind(sock,(struct sockaddr*)&local,len)<0){ 23 perror("bind"); 24 exit(3); 25 } 26 27 if(listen(sock,10)<0){ 28 perror("listen"); 29 exit(4); 30 } 31 32 return sock; 33 } 34 35 void usage(char* proc){ 36 printf("usage %s:[local_ip],[local_port]\n", proc); 37 } 38 39 int main(int argc,char* argv[]){ 40 if(argc != 3){ 41 usage(argv[0]); 42 exit(1); 43 } 44 45 int listensock = tcpserve(argv[1],argv[2]); 46 47 while(1){ 48 struct sockaddr_in client; 49 socklen_t len = sizeof(client); 50 int new_socket = accept(listensock, (struct sockaddr*)&cli ent, &len); 51 if(new_socket<0){ 52 perror("accept"); 53 continue; 54 } 55 56 printf("add a client ip:%s,port:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); 57 58 char buf[1024]; 59 while(1){ 60 ssize_t s=read(new_socket, buf, sizeof(buf)); 61 if(s>0){ 62 buf[s]=0; 63 printf("client say#%s\n", buf); 64 write(new_socket, buf, strlen(buf)); 65 } 66 else if(s==0){ 67 printf("client leave!\n"); 68 break; 69 } 70 else{ 71 perror("read"); 72 break; 73 } 74 } 75 close(new_socket); 76 77 } 78 79 80 return 0; 81 }
client.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 #include<arpa/inet.h> 7 #include<string.h> 8 9 void usage(char* proc){ 10 printf("usage %s:[server_ip],[server_port]\n", proc); 11 } 12 13 int main(int argc,char* argv[]){ 14 if(argc != 3){ 15 usage(argv[0]); 16 exit(1); 17 } 18 19 int sock = socket(AF_INET,SOCK_STREAM,0); 20 if(sock<0){ 21 perror("socket"); 22 exit(2); 23 } 24 25 struct sockaddr_in peer; 26 peer.sin_family = AF_INET; 27 peer.sin_port = htons(atoi(argv[2])); 28 peer.sin_addr.s_addr = inet_addr(argv[1]); 29 30 if(connect(sock,(struct sockaddr*)&peer, sizeof(peer))<0){ 31 perror("connect"); 32 exit(3); 33 } 34 35 char buf[1024]; 36 while(1){ 37 printf("please say#"); 38 fflush(stdout); 39 ssize_t s =read(0,buf,sizeof(buf)-1); 40 if(s>0){ 41 buf[s-1]=0; 42 write(sock,buf,strlen(buf)); 43 ssize_t _s=read(sock, buf, sizeof(buf)-1); 44 if(_s>0){ 45 buf[_s]=0; 46 printf("server say#%s\n", buf); 47 continue; 48 } 49 else if(_s == 0){ 50 printf("serve leave!\n"); 51 break; 52 } 53 else{ 54 perror("read"); 55 break; 56 } 57 } 58 59 } 60 close(sock); 61 return 0; 62 }
Makefile
1 .PHONY:all 2 all:rclient rserver 3 4 rclient:client.c 5 gcc -o $@ $^ 6 rserver:server.c 7 gcc -o $@ $^ 8 9 .PHONY:clean 10 clean: 11 rm -f rclient rserver~
运行界面:
至此,单用户服务器编写完毕。
************************************************************************************************************
其次,介绍多进程多用户服务器的实现步骤,并展示源代码和相应运行实例。
服务器端
1>Main函数传参 argc argv[],包含服务器的端口号8080和IP地址
2>构建服务器listen_socket套接字:
a.用socket函数创建套接字*
b.用bind函数绑定套接字和端口号以及IP
c.用listen函数设置监听,了解是否有客服端要连接服务器
循环体:
3>用accept函数建立链接的new_socket套接字,准备提供服务。
4>用fork()产生父子进程。
5>子进程执行服务内容:随时检查对方是否断开连接,并确定自己是否断开(close)连接。
6>父进程非阻塞式等待子进程的结果。
客户端的布置与单用户服务器一致,so略。
简单来说,多进程多用户服务器就是建立监听机制的listen_socket和执行套接字new_socket后,父进程fork子进程,并非阻塞式等待子进程;子进程完成new_socket的相应服务。
源代码:
server.h
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/wait.h> 5 #include<sys/socket.h> 6 #include<netinet/in.h> 7 #include<arpa/inet.h> 8 #include<string.h> 9 #include<unistd.h> 10 11 int tcpserve(char* ip, char* proc){ 12 int sock = socket(AF_INET,SOCK_STREAM,0); 13 if(sock<0){ 14 perror("socket"); 15 exit(2); 16 } 17 18 struct sockaddr_in local; 19 local.sin_family = AF_INET; 20 local.sin_port = htons(atoi(proc)); 21 local.sin_addr.s_addr = inet_addr(ip); 22 int len=sizeof(local); 23 24 if(bind(sock,(struct sockaddr*)&local,len)<0){ 25 perror("bind"); 26 exit(3); 27 } 28 29 30 if(listen(sock,10)<0){ 31 perror("listen"); 32 exit(4); 33 } 34 35 return sock; 36 } 37 38 void usage(char* proc){ 39 printf("usage %s:[local_ip],[local_port]\n", proc); 40 } 41 42 int main(int argc,char* argv[]){ 43 if(argc != 3){ 44 usage(argv[0]); 45 exit(1); 46 } 47 48 int num=0; 49 int listensock = tcpserve(argv[1],argv[2]); 50 while(1){ 51 struct sockaddr_in client; 52 socklen_t len = sizeof(client); 53 int new_socket = accept(listensock,\ 54 (struct sockaddr*)&client, &len); 55 if(new_socket<0){ 56 perror("accept"); 57 close(new_socket); 58 exit(0); 59 //continue; 60 } 61 62 printf("add a client ip:%s,port:%d\n", \ 63 inet_ntoa(client.sin_addr), \ 64 ntohs(client.sin_port)); 65 num++; 66 67 pid_t id=fork(); 68 if(id <0){ 69 perror("fork"); 70 return 6; 71 } 72 else if(id==0){//child 73 char buf[1024]; 74 while(1){ 75 ssize_t s=read(new_socket, buf, sizeof(buf)); 76 if(s>0){ 77 buf[s]=0; 78 printf("client %d say#%s\n", num, buf); 79 write(new_socket, buf, strlen(buf)); 80 } 81 else if(s==0){ 82 printf("client %d leave!\n", num); 83 break; 84 } 85 else{ 86 perror("read"); 87 break; 88 } 89 } 90 close(new_socket); 91 exit(0); 92 } 93 else{//parent 94 if(waitpid(id, NULL, WNOHANG)>0){ 95 //if(waitpid(id, NULL, 0)>0){ 96 printf("wait successed!\n"); 97 } 98 } 99 }100101 close(listensock);102 return 0;103 }
client.h和Makefile与单用户服务器一致,源代码略。
运行界面:
至此,多进程多用户服务器编写完毕。
******************************************************************************************
最后介绍多线程多用户服务器的实现步骤,并展示源代码和相应运行实例。
服务器端
1>2>与单用户服务器和多进程多用户服务器一致,so略。
循环体:
3>用accept函数建立链接的new_socket套接字,准备提供服务。
4>用pthread_create()产生线程。
5>线程执行服务内容:随时检查对方是否断开连接,并确定自己是否断开(close)连接。
6>并分离该线程,所以主进程不用非阻塞式等待线程的结果。
客服端的布置与单用户服务器一致,so略。
简单来说,多线程多用户服务器就是在建立监听机制的套接字listen_socket和执行套接字new_socket后,主进程建立线程,并非阻塞式等待线程最后利用线程分离避免主进程等待;而线程完成new_socket的相应服务即可。
源代码:
server.h
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<netinet/in.h> 6 #include<arpa/inet.h> 7 #include<string.h> 8 #include<pthread.h> 9 10 11 int tcpserve(char* ip, char* proc){ 12 int sock = socket(AF_INET,SOCK_STREAM,0); 13 if(sock<0){ 14 perror("socket"); 15 exit(2); 16 } 17 18 struct sockaddr_in local; 19 local.sin_family = AF_INET; 20 local.sin_port = htons(atoi(proc)); 21 local.sin_addr.s_addr = inet_addr(ip); 22 int len=sizeof(local); 23 24 if(bind(sock,(struct sockaddr*)&local,len)<0){ 25 perror("bind"); 26 exit(3); 27 } 28 29 if(listen(sock,10)<0){ 30 perror("listen"); 31 exit(4); 32 } 33 34 return sock; 35 } 36 37 void* operate(void* proc){ 38 int new_socket = (int)proc; 39 char buf[1024]; 40 while(1){ 41 ssize_t s=read(new_socket, buf, sizeof(buf)); 42 if(s>0){ 43 buf[s]=0; 44 printf("client say#%s\n", buf); 45 write(new_socket, buf, strlen(buf)); 46 } 47 else if(s==0){ 48 printf("client leave!\n"); 49 break; 50 } 51 else{ 52 perror("read"); 53 break; 54 } 55 } 56 close(new_socket); 57 58 return (void*)21; 59 } 60 61 void usage(char* proc){ 62 printf("usage %s:[local_ip],[local_port]\n", proc); 63 } 64 65 int main(int argc,char* argv[]){ 66 if(argc != 3){ 67 usage(argv[0]); 68 exit(1); 69 } 70 71 int listensock = tcpserve(argv[1],argv[2]); 72 while(1){ 73 struct sockaddr_in client; 74 socklen_t len = sizeof(client); 75 int new_socket = accept(listensock, \ 76 (struct sockaddr*)&client, &len); 77 if(new_socket<0){ 78 perror("accept"); 79 continue; 80 } 81 82 83 pthread_t pid; 84 if(pthread_create(&pid, NULL,operate, (void*)new_socket)<0){ 85 perror("pthread_create"); 86 return 5; 87 } 88 89 printf("add a client ip:%s,port:%d\n", \ 90 inet_ntoa(client.sin_addr), \ 91 ntohs(client.sin_port)); 92 93 pthread_detach(pid); 94 } 95 return 0; 96 }
client.h与单用户服务器一致,源代码略。
Makefile也一点不一样,代码如下:
1 .PHONY:all 2 all:rclient rserver 3 4 rclient:client.c 5 gcc -o $@ $^ 6 rserver:server.c 7 gcc -o $@ $^ -lpthread 8 9 .PHONY:clean 10 clean: 11 rm -f rclient rserver~
运行界面:
至此,多线程多用户服务器编写完毕。
*****************************************************************************************
另外,可利用netstat -nltp在命令行上查看所有TCP服务。运行结果如下:
但是上面的服务器会出现server bind错误,主要是由TIME_WAIT状态导致,当服务器先与客户端退出时,当服务器重启时,就会因为客户端还占用着端口导致server bind错误,就像下面截图这样。
其实等一分钟,系统也会关闭客户端占用的端口号,服务器也能重启。如下面截图所示:
但是很多时候,时间就是金钱,服务器没法等一分钟再重启,所以在人们通常在bind()之前利用setsocketopt()函数进行相应设定,来避免出现server bind错误。代码截图和运行结果如下:
代码截图:
运行结果:
分享如上!如有错误,望指正!愿共同进步!
- TCP 服务器三种模式的简单实现
- 基于TCP的客户服务器模式的三种通信
- 简单工厂模式的三种实现
- TCP服务器、客户端的简单实现
- <网络编程培训之三> 实现TCP/UDP的简单Echo服务器
- 实现采用客户/服务器通信模式,基于TCP网络通信协议的多客户端简单应用
- 简单的TCP服务器
- 简单的TCP服务器
- 实现采用客户/服务器通信模式,基于TCP网络通信协议的多客户端简单应用之案例分析
- 【网络】实现简单的TCP、UDP服务器、TCP多进程/多线程服务器
- 用Reactor框架实现一个简单的tcp服务器
- 基于tcp/ip的简单web服务器实现
- TCP套接字编程实现简单的并发服务器
- erlang tcp服务器和客户端的简单实现
- python socket编程实现的简单tcp迭代服务器
- 使用select系统调用实现简单的TCP服务器
- 网络编程之:TCP服务器的简单实现
- python socket实现简单的(TCP/UDP)服务器/客户端
- new/delete表达式/类型转换(待补充)
- 追根朔源java中的集合的toString
- react native 全局变量的使用
- php判断是手机访问还是电脑访问
- Python中方法的缺省参数问题分析
- TCP 服务器三种模式的简单实现
- JVET-E0021
- 4.5打开动态工具窗口
- Android 屏幕适配方案
- 36/37. Sudoku Solver 数独问题
- Switch Vlan的3种访问模式:Access、Hybrid和Trunk
- iOS Xcode报错:cannot find code object on disk
- 8.19风筝图
- 在ubuntu上安装最新稳定版本的node及npm