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错误。代码截图和运行结果如下:

代码截图:

 

运行结果:

 

 

      分享如上!如有错误,望指正!愿共同进步!

阅读全文
0 0
原创粉丝点击