zoukankan      html  css  js  c++  java
  • Socket编程--并发server

    • Socket地址复用
      • int getsockopt(int sockfd, int level, int optname,
                       void *optval, socklen_t *optlen);
        int setsockopt(int sockfd, int level, int optname,
                       const void *optval, socklen_t optlen);

        服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).

        可以在bind之前添加代码(完整代码请参照博文最后):

        int on = 1;
        if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
        &on,sizeof(on)) == -1)
        err_exit("setsockopt SO_REUSEADDR error");

        这个可以用来支持地址的复用

    • 多线程连接
      •   当我们在进行多客户连接的时候会发现,对一个只能连接一个客户端的服务器,你可以用多个客户端连接这个服务器,客户端也不会发出出错返回,而服务器也不会发出出错返回,但是服务器不会为该客户端服务,虽然 看起来客户端确实是连接上了服务器。所以我们可以用一条连接一个线程,提供更高的并发量。
      •   这里我们可以写一个server/多个client来学习多线程并发操作。
        •   TCP通信图
        •   UDP通信图
        •   功能设计实现图
        •   代码实现图
        •   实现代码思想
          •   server:创建服务器和客户端的简单的信息,利用地址复用技术充分利用地址;绑定端口;监听端口;循环的与客户端建立连接:
            建立连接;创建子进程用于接收客户端消息并发送出去 :在子进程中再次创建子进程用来接收消息(当接收的消息不为空的时候,将客户端发来的消息处理进制之后再发回给客户);
              1 服务器端代码
              2 #include <sys/types.h>
              3 #include <sys/socket.h>                         // 包含套接字函数库
              4 #include <stdio.h>
              5 #include <netinet/in.h>                         // 包含AF_INET相关结构
              6 #include <arpa/inet.h>                      // 包含AF_INET相关操作的函数
              7 #include <unistd.h>
              8 #include<string.h>
              9 #include<stdlib.h>
             10 #include<fcntl.h>
             11 #include<sys/shm.h>
             12 
             13 #define MYPORT    8887  
             14 #define MYKEY   12345
             15 #define SIZE    10240
             16 
             17 int main()
             18 {    
             19     char buf[100];
             20     memset(buf,0,100);   //初始化100个0
             21    //创建服务器,客户端的信息
             22     int server_sockfd,client_sockfd;//socket函数返回的套接字   
             23     socklen_t server_len,client_len;   
             24     struct sockaddr_in server_sockaddr,client_sockaddr;   
             25     printf("
            ======================server initialization======================
            "); 
             26 
             27     server_sockfd = socket(AF_INET,SOCK_STREAM, 0); // 定义套接字类型     TCP 
             28     server_sockaddr.sin_family = AF_INET;
             29     server_sockaddr.sin_port = htons(MYPORT);
             30     server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
             31     server_len = sizeof(server_sockaddr);
             32    
             33     //允许重复使用本地地址和套接字绑定   地址复用:在bind()函数前面加上上面这段代码即可
             34     int on = 1;
             35     setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
             36    
             37     //绑定端口
             38     if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,server_len)==-1)
             39     {
             40         perror("bind");
             41         exit(1);
             42     }   
             43    
             44     //监听端口
             45     if(listen(server_sockfd,5) == -1)
             46     {
             47         perror("listen");
             48         exit(1);   
             49     }
             50       
             51     client_len = sizeof(client_sockaddr);   
             52     pid_t ppid,pid;//这里pid_t  相当于int
             53   
             54 //循环的接收客户端发来的连接
             55     while(1)
             56     {  
             57         //当客户端发来的套接字返回  -1   接收失败   
             58         if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&client_sockaddr,&client_len))==-1)
             59         {
             60              perror("accept error");
             61              exit(1);
             62         }
             63         //建立连接
             64         else
             65         {
             66             send(client_sockfd,"You have connect Server!",strlen("You have connect Server!"),0);
             67         }
             68         //将十进制ip地址转换成一个32位的二进制ip序列
             69         printf("
            %s:%d Login server!
            
            ",inet_ntoa(client_sockaddr.sin_addr), ntohs(client_sockaddr.sin_port));
             70         
             71         ppid = fork();    //创建子进程    fork:返回-1出错   返回0子进程   返回其他数字表示返回子进程id
             72    
             73         if(ppid == -1)
             74         {
             75             printf("fork 1 failed:");
             76         }
             77         else if(ppid == 0)    //子进程用于接收客户端信息并发送
             78         {           
             79             int recvbytes;
             80             pid = fork();    //再次创建子进程
             81             
             82             if(pid == -1)
             83             {
             84                 printf("fork 2 failed:");
             85                 exit(1);
             86             }
             87             else if(pid == 0) //子进程的子进程用于接收消息
             88             {              
             89                 while(1)
             90                 {          
             91                     bzero(buf,100);
             92                     if((recvbytes = recv(client_sockfd,buf,100,0))==-1)
             93                     {
             94                         perror("read client_sockfd failed:");
             95                     }
             96                     else if(recvbytes != 0)
             97                     {                        
             98                         buf[recvbytes] = '';
             99                         usleep(10000);
            100                         printf("%s:%d said:%s
            ",inet_ntoa(client_sockaddr.sin_addr), ntohs(client_sockaddr.sin_port), buf);
            101 
            102                         //将客户端发送过来的消息发回给客户
            103                         if(send(client_sockfd,buf,recvbytes,0)==-1){ 
            104                             perror("send error");
            105                             break;
            106                         }
            107                     }
            108                 }
            109             }
            110             else if(pid>0)  //此时的id为子进程id 
            111             {    
            112 
            113             }    
            114         }
            115          else if(ppid>0)
            116         {             
            117             //总父进程中关闭client_sockfd(因为有另一个副本在子进程中运行了)返回等待接收消息
            118             close(client_sockfd);
            119         }    
            120     }
            121     return 0;
            122 }
            View Code
          •   client:创建客户端的基本信息并且完善;建立连接;收到服务器发回的数据,当数据字节大小不为空时,开始输入要发送的数据,并且发送至服务器
             1 客户端代码
             2 #include <stdio.h> 
             3 #include <stdlib.h> 
             4 #include <errno.h> 
             5 #include <string.h> 
             6 #include <sys/types.h> 
             7 #include <netinet/in.h> 
             8 #include <sys/socket.h> 
             9 #include <sys/wait.h> 
            10 #include<unistd.h> 
            11 #include <arpa/inet.h> 
            12 #define SERVER_PORT 8887 /* 客户机连接远程主机的端口 */ 
            13 #define MAXDATASIZE 100 /* 每次可以接收的最大字节 */ 
            14 #define SERVER_IP "192.168.11.8" /* 服务器的IP地址 */
            15 
            16 int main()
            17 { 
            18     int sockfd, numbytes; 
            19     char buf[MAXDATASIZE]; 
            20     struct sockaddr_in server_addr;
            21 
            22     printf("
            ======================client initialization======================
            "); 
            23     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
            24     { 
            25         perror("socket"); 
            26         exit(1); 
            27     } 
            28     server_addr.sin_family = AF_INET; 
            29     server_addr.sin_port = htons(SERVER_PORT); 
            30     server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
            31     bzero(&(server_addr.sin_zero),sizeof(server_addr.sin_zero)); 
            32     
            33     //客户端请求建立连接
            34     if (connect(sockfd, (struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) == -1) 
            35     { 
            36         perror("connect");
            37         exit(1); 
            38     }
            39 
            40     //循环输入文字
            41     while(1)
            42     {        
            43         bzero(buf,MAXDATASIZE);//相当于memset函数
            44         printf("
            Begin receive...
            ");
            45         if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1)        
            46         { 
            47             perror("recv");
            48             exit(1); 
            49         }
            50         else if (numbytes > 0)        
            51         {
            52             int len, bytes_sent;
            53             buf[numbytes] = '';
            54             printf("Received: %s
            ",buf);
            55             
            56             printf("Send:");
            57             char *msg;
            58             scanf("%s",msg);            
            59             len = strlen(msg);
            60 
            61             //发送至服务器
            62             if(send(sockfd,msg,len,0) == -1)
            63             { 
            64                 perror("send error");
            65             }
            66         }
            67         else
            68         {
            69             //numbytes=0,表示socket已断开
            70             printf("soket end!
            ");
            71         }
            72         
            73     }
            74 
            75     close(sockfd); 
            76     return 0;
            77 }
            View Code
      • Linux下进行编译:

            gcc Server.c -o server

            gcc Client.c -o client


  • 相关阅读:
    揭秘:如何为 Kubernetes 实现原地升级
    阿里云叔同:以容器为代表的云原生技术,已经成为释放云价值的最短路径
    如何画好一张架构图?(内含知识图谱)
    K8s 资源全汇总 | K8s 大咖带你 31 堂课从零入门 K8s
    15-16年总结——拨开云雾终见青天
    从一个程序员的角度看——微信小应用(第一弹 初学)
    h5直播开发之旅总结
    初探和实现websocket心跳重连(npm: websocket-heartbeat-js)
    组件化h5活动模板的实现
    总结JavaScript事件机制
  • 原文地址:https://www.cnblogs.com/Kobe10/p/5770365.html
Copyright © 2011-2022 走看看