• Linux下多进程服务端客户端模型一(单进程与多进程模型)


    本文将会简单介绍Linux下如何利用C库函数与系统调用编写一个完整的、初级可用的C-S模型。

    一、基本模型:

        1.1   首先服务器调用socket()函数建立一个套接字,然后bind()端口,开始listen()监听,此时,套接字变成了被动的套接字,用于侦听客户端的请求。然后accept(),开始阻塞监听客户端的请求。

      1.2   客户端以服务端的参数为参数,用socket()建立套接字,然后connect()连接服务端。

        1.3  服务端收到连接请求,accept()返回一个标识符,继续执行后面的指令。具体如图:

           1.4  服务端创建套接字与绑定端口代码(打码处为点分十进制的IP字符串,注意,端口号用函数转换成网络传输的大端的Long类型,点分十进制IP转换成二进制。):

              

             1.5 服务端监听与accept,并且循环读写代码:

             

           while结束后close(conn)  close(sockfd)即可。

           1.6 客户端(打码处为服务端的IP与端口)

             

    二、多进程

          2.1

          由于accept是阻塞的函数,如果用单进程进行,那么将会只能接受一个客户端的请求。而对于其他客户端的请求,则代码不会返回去重新执行accept();

         要注意的是,其他客户端连接服务器时,即使服务器只接受一个客户端的业务请求,但是内核会帮我们完成TCP的三次握手连接,放入已连接的队列中等待服务器的accept去取走,不过,由于我们没有开启新的进程,所以,后来的这些连接没有机会被取走了。如图:

           

           2.2  

           针对以上问题,可以将accept()放入while循环中,然后每一次接收到了连接,则创建一个子进程,然后子进程去执行读写操作,而父进程则继续监听。代码如下:

          

     1 #include <unistd.h>
     2 #include <sys/stat.h>
     3 #include <sys/wait.h>
     4 #include <sys/types.h>
     5 #include <fcntl.h>
     6 
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <errno.h>
    10 #include <string.h>
    11 #include <signal.h>
    12 
    13 #include <arpa/inet.h>
    14 #include <sys/socket.h>
    15 #include <netinet/in.h>
    16 
    17 int main(void)
    18 {
    19     int sockfd;
    20     // 创建一个Socket
    21     sockfd = socket(AF_INET,SOCK_STREAM,0);
    22     if(sockfd == -1){
    23         perror("error");
    24         exit(0);
    25     }
    26 
    27 
    28     ///////////////////////////////////////////////////////////
    29 //    struct sockaddr addr; // 这是一个通用结构,一般是用具体到,然后转型
    30     struct sockaddr_in sockdata;
    31     sockdata.sin_family = AF_INET;
    32     sockdata.sin_port = htons(8001);
    33     sockdata.sin_addr.s_addr = inet_addr(我是IP字符串);
    34     
    35     int optval = 1;
    36     if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1)
    37     {
    38         perror("error");
    39         exit(0);
    40     }
    41     if(bind(sockfd,(struct sockaddr *)&sockdata,sizeof(sockdata)) < 0){
    42         perror("error");
    43         exit(0);
    44     }
    45     
    46     ////////////////////////////////////////////////////////////
    47     if(listen(sockfd,5) == -1){
    48         perror("error");
    49         exit(0);
    50     }
    51     
    52     //////////////////////////////////////////////////////////
    53     struct sockaddr_in peeradr;
    54     socklen_t peerlen = sizeof(peeradr); // 得有初始值
    55 
    56 
    57     /////////////////////////////////////////////////////////
    58     char recvBuff[1024]={0};
    59     int conn = 0;
    60     while(1){
    61         conn = accept(sockfd,(struct sockaddr *)&peeradr,&peerlen);
    62         if(conn == -1){
    63             perror("error");
    64             exit(0);
    65         }    
    66         // 每来一个链接fork一个进程。
    67         pid_t pid;
    68         pid = fork();
    69 
    70         if(pid == 0){
    71             int ret = 0;
    72             close (sockfd); // 由于子进程复制来sockfd,所以关掉它。 不干涉父进程来,因为这是一个副本。由父进程继续监听
    73             printf("收到的IP %s
     客户端端口是:%d
    ",inet_ntoa(peeradr.sin_addr),ntohs(peeradr.sin_port));
    74             while((ret = read(conn,recvBuff,sizeof(recvBuff))) && ret > 0){
    75                 // 服务器收到打印数据,然后回发
    76                 fputs(recvBuff,stdout);
    77                 write(conn,recvBuff,ret);
    78             }
    79             exit(0);
    80         }
    81         else if(pid > 0){
    82             close(conn); // 父进程只管监听,不需要链接套接字!!
    83         }
    84         else{
    85             perror("error");
    86             close(conn);
    87             close(sockfd);
    88             exit(0);
    89         }
    90     }
    91     close(conn);
    92     close(sockfd);
    93     return 0;
    94 }
    server.c

           而客户端没有什么变化,代码如下:

     1 #include <unistd.h>
     2 #include <sys/stat.h>
     3 #include <sys/wait.h>
     4 #include <sys/types.h>
     5 #include <fcntl.h>
     6 
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <errno.h>
    10 #include <string.h>
    11 #include <signal.h>
    12 
    13 #include <arpa/inet.h>
    14 #include <sys/socket.h>
    15 #include <netinet/in.h>
    16 
    17 int main(void)
    18 {
    19         int sockfd;
    20     // 创建一个Socket
    21     sockfd = socket(AF_INET,SOCK_STREAM,0);
    22     if(sockfd == -1){
    23         perror("error");
    24         exit(0);
    25     }
    26 
    27 
    28     ///////////////////////////////////////////////////////////
    29 //    struct sockaddr addr; // 这是一个通用结构,一般是用具体到,然后转型
    30     struct sockaddr_in sockdata;
    31     sockdata.sin_family = AF_INET;
    32     sockdata.sin_port = htons(8001);
    33     sockdata.sin_addr.s_addr = inet_addr("我是IP字符串");
    34     if(connect(sockfd,(struct sockaddr *)&sockdata,sizeof(sockdata)) == -1){
    35         perror("error");
    36         exit(0);
    37     }
    38 
    39     char sendBuff[1024] = {0};
    40     char recvBuff[1024] = {0};
    41 
    42     while(fgets(sendBuff,sizeof(sendBuff),stdin) != NULL){
    43         // 将输入到数据送到服务端
    44         write(sockfd,sendBuff,sizeof(sendBuff));
    45         // 从服务器读数据
    46         read(sockfd,recvBuff,sizeof(recvBuff));
    47 
    48         // 显示在屏幕上
    49         fputs(recvBuff,stdout);
    50         // 清零
    51         memset(recvBuff,0,sizeof(recvBuff));
    52         memset(sendBuff,0,sizeof(sendBuff));
    53     }
    54 
    55     close(sockfd);
    56     return 0;    
    57 }
    client.c
  • 相关阅读:
    Java 位运算
    Java 自增运算
    Java 变量命名规范
    Java 数据类型
    Java 环境配置
    SQL Server-语句类别、数据库范式、系统数据库组成(一)
    Socket连接时,端口是怎么分配的
    【转载】SQL执行计划
    前端页面播放 rtmp 流与 flv 格式视频文件
    C# Winform Soket 网络编程 多个客户端连接服务器并返回客户端操作请求
  • 原文地址:https://www.cnblogs.com/tntboom/p/4491342.html
Copyright © 2020-2023  润新知