• Linux高并发网络编程开发——tcp三次握手-并发


    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    10-Linux系统编程-第11天(tcp三次握手-并发)

    目录:
    一、学习目标
    二、复习
    三、TCP三次握手-并发
    1、TCP服务器端和客户端代码实现
    2、socket 函数封装
    3、TCP 3次握手
    4、TCP 数据传输过程
    5、TCP 四次挥手
    6、滑动窗口
    7、多进程并发服务器分析
    8、多进程并发服务器伪代码
    9、多进程并发服务器代码实现
    10、多线程并发服务器实现思路
    11、多线程版服务器端代码实现

    一、学习目标

    1、熟练掌握三次握手建立连接过程
    2、熟练掌握四次挥手断开连接过程
    3、掌握滑动窗口概念
    4、掌握错误处理函数封装
    5、实现多进程并发服务器
    6、实现多线程并发服务

    二、复习

        

    三、TCP三次握手-并发


    》server模板.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <string.h>
     6 #include <sys/socket.h>
     7 #include <arpa/inet.h>
     8 #include <ctype.h>
     9 
    10 //进程回调函数
    11 
    12 //主函数
    13 int main(int argc, const char *argv[])
    14 {
    15     if(argc < 2)
    16     {
    17         printf("eg: ./a.out port
    ");
    18         exit(1);
    19     }
    20     struct sockaddr_in serv_addr;
    21     socklen_t serv_len = sizeof(serv_addr);
    22     int port = atoi(argv[1]);
    23     
    24     //创建套接字
    25     int lfd = socket(AF_INET, SOCK_STREAM, 0);
    26     //初始化服务器 sockaddr_in
    27     memset(&serv_addr, 0, serv_len);
    28     serv_addr.sin_family = AF_INET;//地址族
    29     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP
    30     serv_addr.sin_port = htons(port);//设置端口
    31     //绑定IP 和端口
    32     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
    33     
    34     //设置同时监听的最大个数
    35     listen(lfd, 36);
    36     printf("Start accept ......
    ");
    37     
    38     struct sockaddr_in client_addr;
    39     socklen_t cli_len = sizeof(client_addr);
    40     while(1)
    41     {    
    42         
    43     }
    44     
    45     close(lfd);
    46     return 0;
    47 }

    1、TCP服务器端和客户端代码实现

    》TCP服务器端

    >touch tcp_server.c

    >vi tcp_server.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <sys/socket.h>
     8 #include <arpa/inet.h>
     9 #include <ctype.h>
    10 
    11 
    12 int main(int argc, const char* argv[])
    13 {
    14     // 创建用于监听的套节字
    15     int lfd = socket(AF_INET, SOCK_STREAM, 0);
    16     if(lfd == -1)
    17     {
    18         perror("socket error");
    19         exit(1);
    20     }
    21 
    22     // 绑定
    23     struct sockaddr_in serv_addr;
    24     // init
    25     memset(&serv_addr, 0, sizeof(serv_addr));
    26     // bzero(&serv_addr, sizeof(serv_addr));
    27     serv_addr.sin_family = AF_INET; // 地址族协议  ipv4
    28     serv_addr.sin_port = htons(9999);   // 本地端口, 需要转换为大端
    29     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0 是用本机的任意IP
    30 
    31     int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    32     if(ret == -1)
    33     {
    34         perror("bind error");
    35         exit(1);
    36     }
    37 
    38     // 设置监听
    39     ret = listen(lfd, 64);
    40     if(ret == -1)
    41     {
    42         perror("listen error");
    43         exit(1);
    44     }
    45 
    46     // 等待并接受连接请求
    47     struct sockaddr_in cline_addr;
    48     socklen_t clien_len = sizeof(cline_addr);
    49     int cfd = accept(lfd, (struct sockaddr*)&cline_addr, &clien_len);
    50     if(cfd == -1)
    51     {
    52         perror("accept error");
    53         exit(1);
    54     }
    55     
    56     char ipbuf[64];
    57     // int -> char*
    58     printf("cliient ip: %s, port: %d
    ",
    59            inet_ntop(AF_INET, &cline_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
    60            ntohs(cline_addr.sin_port));
    61 
    62     // 通信
    63     while(1)
    64     {
    65         // 先接收数据
    66         char buf[1024] = {0};
    67         int len = read(cfd, buf, sizeof(buf));
    68         if(len == -1)
    69         {
    70             perror("read error");
    71             break;
    72         }
    73         else if(len > 0)
    74         {
    75             // 顺利读出了数据
    76             printf("read buf = %s
    ", buf);
    77             // 小写 -》 大写
    78             for(int i=0; i<len; ++i)
    79             {
    80                 buf[i] = toupper(buf[i]);
    81             }
    82             printf(" -- toupper: %s
    ", buf);
    83 
    84             // 数据发送给客户端
    85             write(cfd, buf, strlen(buf)+1);
    86         }
    87         else if( len == 0 )
    88         {
    89             printf("client disconnect ...
    ");
    90             break;
    91         }
    92     }
    93 
    94     close(lfd);
    95     close(cfd);
    96 
    97     return 0;
    98 }

    >gcc tcp_server.c -o server

    》TCP客户端

    >touch tcp_client.c

    >vi tcp_client.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <string.h>
     7 #include <arpa/inet.h>
     8 #include <fcntl.h>
     9 
    10 // tcp client
    11 int main(int argc, const char* argv[])
    12 {
    13     if(argc < 2)
    14     {
    15         printf("eg: ./a.out port
    ");
    16         exit(1);
    17     }
    18     
    19     int port = atoi(argv[1]);
    20     // 创建套接字 ( AF_INET为IPv4)
    21     int fd = socket(AF_INET, SOCK_STREAM, 0);//查文档 :! man 'socket'
    22     if(fd == -1)
    23     {
    24         perror("socket error");
    25         exit(1);
    26     }
    27     
    28 
    29     // 连接服务器
    30     struct sockaddr_in serv_addr;
    31     memset(&serv_addr, 0, sizeof(serv_addr));
    32     serv_addr.sin_family = AF_INET;
    33     serv_addr.sin_port = htons(port);
    34     //serv_addr.sin_addr.s_addr = htonl();//htonl括号中只能放整型,所以换用inet_pton
    35     inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    36     int ret = connect(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    37     if(ret == -1)
    38     {
    39         perror("connect error");
    40         exit(1);
    41     }
    42 
    43     // 通信
    44     while(1)
    45     {
    46         // 发送数据
    47         // 接收键盘输入
    48         char buf[1024];
    49         printf("请输入要发送的字符串:
    ");
    50         fgets(buf, sizeof(buf), stdin);
    51         // 发送给服务器
    52         write(fd, buf, strlen(buf)+1);
    53 
    54         // 等待接收服务器端的数据
    55         int len = read(fd, buf, sizeof(buf));
    56         if(len == -1)
    57         {
    58             perror("read error");
    59             exit(1);
    60         }
    61         else if(len == 0)
    62         {
    63             printf("服务器端关闭了连接
    ");
    64             break;
    65         }
    66         else
    67         {
    68             printf("read buf = %s, len = %d
    ", buf, len);
    69         }
    70     }
    71     close(fd);
    72     
    73     return 0;
    74 }

    >gcc tcp_client.c -o client

    >./server

    (打开另一个终端,切换到目录下,运行./client 9999,然后输入要发送的字符串:hello,会收到服务器转换为大写的HELLO;查看原终端server,可以看到client IP:127.0.0.1, port: 34844, 收到字符串:hello,发送HELLO)

    2、socket 函数封装

    函数的封装在wrap.c和wrap.h,函数的调用在client.c和server.c中

    理解

    wrap.h

     1 #ifndef __WRAP_H_
     2 #define __WRAP_H_
     3 
     4 void perr_exit(const char *s);
     5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
     6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
     7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
     8 int Listen(int fd, int backlog);
     9 int Socket(int family, int type, int protocol);
    10 ssize_t Read(int fd, void *ptr, size_t nbytes);
    11 ssize_t Write(int fd, const void *ptr, size_t nbytes);
    12 int Close(int fd);
    13 ssize_t Readn(int fd, void *vptr, size_t n);
    14 ssize_t Writen(int fd, const void *vptr, size_t n);
    15 ssize_t my_read(int fd, char *ptr);
    16 ssize_t Readline(int fd, void *vptr, size_t maxlen);
    17 
    18 #endif

    wrap.c

      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <unistd.h>
      4 #include <errno.h>
      5 #include <sys/socket.h>
      6 //错误输出
      7 void perr_exit(const char *s)
      8 {
      9     perror(s);
     10     exit(-1);
     11 }
     12 //接受
     13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
     14 {
     15     int n;
     16 
     17 again:
     18     if ((n = accept(fd, sa, salenptr)) < 0) 
     19     {
     20         //ECONNABORTED 发生在重传(一定次数)失败后,强制关闭套接字
     21         //EINTR 进程被信号中断
     22         if ((errno == ECONNABORTED) || (errno == EINTR))
     23         {
     24             goto again;
     25         }
     26         else
     27         {
     28             perr_exit("accept error");
     29         }
     30     }
     31     return n;
     32 }
     33 //绑定
     34 int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
     35 {
     36     int n;
     37 
     38     if ((n = bind(fd, sa, salen)) < 0)
     39     {
     40         perr_exit("bind error");
     41     }
     42 
     43     return n;
     44 }
     45 //连接
     46 int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
     47 {
     48     int n;
     49     n = connect(fd, sa, salen);
     50     if (n < 0) 
     51     {
     52         perr_exit("connect error");
     53     }
     54 
     55     return n;
     56 }
     57 
     58 int Listen(int fd, int backlog)
     59 {
     60     int n;
     61 
     62     if ((n = listen(fd, backlog)) < 0)
     63     {
     64         perr_exit("listen error");
     65     }
     66 
     67     return n;
     68 }
     69 
     70 int Socket(int family, int type, int protocol)
     71 {
     72     int n;
     73 
     74     if ((n = socket(family, type, protocol)) < 0)
     75     {
     76         perr_exit("socket error");
     77     }
     78 
     79     return n;
     80 }
     81 
     82 ssize_t Read(int fd, void *ptr, size_t nbytes)
     83 {
     84     ssize_t n;
     85 
     86 again:
     87     if ( (n = read(fd, ptr, nbytes)) == -1) //判断是否阻塞
     88     {
     89         if (errno == EINTR)//判断是否被信号中断
     90             goto again;
     91         else
     92             return -1;
     93     }
     94 
     95     return n;
     96 }
     97 
     98 ssize_t Write(int fd, const void *ptr, size_t nbytes)
     99 {
    100     ssize_t n;
    101 
    102 again:
    103     if ((n = write(fd, ptr, nbytes)) == -1) //有可能写缓冲区满了,阻塞,等待
    104     {
    105         if (errno == EINTR)//判断是否被信号中断
    106             goto again;
    107         else
    108             return -1;
    109     }
    110     return n;
    111 }
    112 
    113 int Close(int fd)
    114 {
    115     int n;
    116     if ((n = close(fd)) == -1)
    117         perr_exit("close error");
    118 
    119     return n;
    120 }
    121 
    122 /*参三: 应该读取的字节数*/                          
    123 //socket 4096  readn(cfd, buf, 4096)   nleft = 4096-1500
    124 ssize_t Readn(int fd, void *vptr, size_t n)
    125 {
    126     size_t  nleft;              //usigned int 剩余未读取的字节数
    127     ssize_t nread;              //int 实际读到的字节数
    128     char   *ptr;
    129 
    130     ptr = vptr;
    131     nleft = n;                  //n 未读取字节数
    132 
    133     while (nleft > 0) 
    134     {
    135         if ((nread = read(fd, ptr, nleft)) < 0) 
    136         {
    137             if (errno == EINTR)
    138             {
    139                 nread = 0;
    140             }
    141             else
    142             {
    143                 return -1;
    144             }
    145         } 
    146         else if (nread == 0)
    147         {
    148             break;
    149         }
    150 
    151         nleft -= nread;   //nleft = nleft - nread 
    152         ptr += nread;
    153     }
    154     return n - nleft;
    155 }
    156 
    157 ssize_t Writen(int fd, const void *vptr, size_t n)
    158 {
    159     size_t nleft;
    160     ssize_t nwritten;
    161     const char *ptr;
    162 
    163     ptr = vptr;
    164     nleft = n;
    165     while (nleft > 0) 
    166     {
    167         if ( (nwritten = write(fd, ptr, nleft)) <= 0) 
    168         {
    169             if (nwritten < 0 && errno == EINTR)
    170                 nwritten = 0;
    171             else
    172                 return -1;
    173         }
    174         nleft -= nwritten;
    175         ptr += nwritten;
    176     }
    177     return n;
    178 }
    179 
    180 static ssize_t my_read(int fd, char *ptr)//静态函数
    181 {
    182     static int read_cnt;//静态变量
    183     static char *read_ptr;
    184     static char read_buf[100];
    185 
    186     if (read_cnt <= 0) {
    187 again:
    188         if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)    //"hello
    "
    189         {
    190             if (errno == EINTR)
    191                 goto again;
    192             return -1;
    193         } 
    194         else if (read_cnt == 0)
    195             return 0;
    196 
    197         read_ptr = read_buf;
    198     }
    199     read_cnt--;
    200     *ptr = *read_ptr++;
    201 
    202     return 1;
    203 }
    204 
    205 /*readline --- fgets*/    
    206 //传出参数 vptr
    207 ssize_t Readline(int fd, void *vptr, size_t maxlen)
    208 {
    209     ssize_t n, rc;
    210     char    c, *ptr;
    211     ptr = vptr;
    212 
    213     for (n = 1; n < maxlen; n++) 
    214     {
    215         if ((rc = my_read(fd, &c)) == 1)    //ptr[] = hello
    
    216         {
    217             *ptr++ = c;
    218             if (c == '
    ')
    219                 break;
    220         } 
    221         else if (rc == 0) 
    222         {
    223             *ptr = 0;
    224             return n-1;
    225         } 
    226         else
    227             return -1;
    228     }
    229     *ptr = 0;
    230 
    231     return n;
    232 }

    server.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 #include <sys/socket.h>
     5 #include <strings.h>
     6 #include <string.h>
     7 #include <ctype.h>
     8 #include <arpa/inet.h>
     9 
    10 #include "wrap.h"
    11 
    12 #define SERV_PORT 6666
    13 
    14 int main(void)
    15 {
    16     int sfd, cfd;
    17     int len, i;
    18     char buf[BUFSIZ], clie_IP[128];
    19 
    20     struct sockaddr_in serv_addr, clie_addr;
    21     socklen_t clie_addr_len;
    22 
    23     sfd = Socket(AF_INET, SOCK_STREAM, 0);
    24 
    25     bzero(&serv_addr, sizeof(serv_addr));           
    26     serv_addr.sin_family = AF_INET;                 
    27     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  
    28     serv_addr.sin_port = htons(SERV_PORT);          
    29 
    30     Bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    31 
    32     Listen(sfd, 2);                                
    33 
    34     printf("wait for client connect ...
    ");
    35 
    36     clie_addr_len = sizeof(clie_addr_len);
    37     cfd = Accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
    38     printf("cfd = ----%d
    ", cfd);
    39 
    40     printf("client IP: %s  port:%d
    ", 
    41             inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 
    42             ntohs(clie_addr.sin_port));
    43 
    44     while (1) 
    45     {
    46         len = Read(cfd, buf, sizeof(buf));
    47         Write(STDOUT_FILENO, buf, len);
    48 
    49         for (i = 0; i < len; i++)
    50             buf[i] = toupper(buf[i]);
    51         Write(cfd, buf, len); 
    52     }
    53 
    54     Close(sfd);
    55     Close(cfd);
    56 
    57     return 0;
    58 }

    client.c

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <string.h>
     4 #include <sys/socket.h>
     5 #include <arpa/inet.h>
     6 
     7 #include "wrap.h"
     8 
     9 #define SERV_IP "127.0.0.1"
    10 #define SERV_PORT 6666
    11 
    12 int main(void)
    13 {
    14     int sfd, len;
    15     struct sockaddr_in serv_addr;
    16     char buf[BUFSIZ]; 
    17 
    18     sfd = Socket(AF_INET, SOCK_STREAM, 0);
    19 
    20     bzero(&serv_addr, sizeof(serv_addr));                       
    21     serv_addr.sin_family = AF_INET;                             
    22     inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);    
    23     serv_addr.sin_port = htons(SERV_PORT);                      
    24 
    25     Connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    26 
    27     while (1) {
    28         fgets(buf, sizeof(buf), stdin);
    29         int r = Write(sfd, buf, strlen(buf));       
    30         printf("Write r ======== %d
    ", r);
    31         len = Read(sfd, buf, sizeof(buf));
    32         printf("Read len ========= %d
    ", len);
    33         Write(STDOUT_FILENO, buf, len);
    34     }
    35 
    36     Close(sfd);
    37 
    38     return 0;
    39 }

    makefile

     1 src = $(wildcard *.c)
     2 obj = $(patsubst %.c, %.o, $(src))
     3 
     4 all: server client
     5 
     6 server: server.o wrap.o
     7     gcc server.o wrap.o -o server -Wall
     8 client: client.o wrap.o
     9     gcc client.o wrap.o -o client -Wall
    10 
    11 %.o:%.c
    12     gcc -c $< -Wall
    13 
    14 .PHONY: clean all
    15 clean: 
    16     -rm -rf server client $(obj)

    3、TCP 3次握手

           

    4、TCP 数据传输过程

    5、TCP 四次挥手

    6、滑动窗口

    7、多进程并发服务器分析

    >之前讲的客户端-服务器的缺点分析?

        

    读时共享,写时复制,如何理解?

    8、多进程并发服务器伪代码

    》伪代码:

     1 void recyle(int num)
     2 {
     3     while(waitpid(-1, NULL, wnohang) > 0);
     4 }
     5 
     6 int main()
     7 {
     8     //监听
     9     int lfd = sock();
    10     //绑定
    11     bind();
    12     //设置监听
    13     listen();
    14     
    15     //信号回收子进程
    16     struct sigaction act;
    17     act.sa_handler = recyle;
    18     act.sa_flages = 0;
    19     sigemptyset(&act.sa_mask);
    20     sigaction(SIGCHLD, &act, NULL);
    21     
    22     //父进程
    23     while(1)
    24     {
    25         int cfd = accept(lfd, &client);
    26         //创建子进程
    27         pid_t pid = fork();
    28         //子进程
    29         if(pid == 0)
    30         {
    31             close(lfd);
    32             //通信
    33             while(1)
    34             {
    35                 int len = read();
    36                 if(len == -1)
    37                 {
    38                     exit(1);
    39                 }
    40                 else if(len == 0)
    41                 {
    42                     close(cfd);
    43                     break;
    44                 }
    45                 else
    46                 {
    47                     write();
    48                 }
    49             }
    50             //退出子进程
    51             return 0;//exit(1);
    52         }
    53         else
    54         {
    55             //父进程
    56             close(cfd);//如果在此处while回收,就会阻塞在这,所以用信号检测
    57         }
    58     }    
    59 }

    9、多进程并发服务器代码实现

    >touch process_server.c

    >vi process_server.c

      1 #include <stdio.h>
      2
      3 #include <arpa/inet.h>
      4 #include <ctype.h>
      5 #include <unistd.h>
      6 #include <stdlib.h>
      7 #include <sys/types.h>
      8 #include <sys/socket.h>
      9 #include <signal.h>
     10 #include <sys/wait.h>
     11 #include <string.h>
     12 #include <errno.h>
     13 
     14 
     15 //进程回调函数
     16 void recyle(int num)
     17 {
     18     pid_t pid;
     19     while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
     20     {
     21         printf("child died, pid=%d
    ", pid);
     22     }
     23 }
     24 
     25 int main(int argc, const char *argv[])
     26 {
     27     if(argc < 2)
     28     {
     29         printf("eg: ./a.out port
    ");
     30         exit(1);
     31     }
     32     struct sockaddr_in serv_addr;
     33     socklen_t serv_len = sizeof(serv_addr);
     34     int port = atoi(argv[1]);
     35     
     36     //创建套接字
     37     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     38     //初始化服务器 sockaddr_in
     39     memset(&serv_addr, 0, serv_len);
     40     serv_addr.sin_family = AF_INET;//地址族
     41     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP
     42     serv_addr.sin_port = htons(port);//设置端口
     43     //绑定IP 和端口
     44     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     45     
     46     //设置同时监听的最大个数
     47     listen(lfd, 36);
     48     printf("Start accept ......
    ");
     49     
     50     //使用信号回收子进程pcb
     51     struct sigaction act;
     52     act.sa_handler = recyle;
     53     act.sa_flags = 0;
     54     sigemptyset(&act.sa_mask);
     55     sigaction(SIGCHLD, &act, NULL);
     56     
     57     struct sockaddr_in client_addr;
     58     socklen_t cli_len = sizeof(client_addr);
     59     while(1)
     60     {
     61         //父进程接收连接请求
     62         //accept阻塞的时候被信号中断,处理信号对应的操作之后
     63         //回来之后不阻塞了,直接返回-1,这时候errno = EINTR
     64         int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     65         while((cfd == -1) && (errno == EINTR))
     66         {
     67             cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
     68         }
     69         /*if(cfd == -1)
     70         {
     71             perror("accept error");
     72             exit(1);
     73         }
     74         */
     75         printf("connect successful
    ");
     76         //创建子进程
     77         pid_t pid = fork();
     78         if(pid == 0)
     79         {
     80             close(lfd);
     81             //child process
     82             //通信
     83             char ip[64];
     84             while(1)
     85             {
     86                 //client ip port
     87                 printf("client IP: %s, port: %d
    ", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port));
     88                 
     89                 char buf[1024];
     90                 int len = read(cfd, buf, sizeof(buf));
     91                 if(len == -1)
     92                 {
     93                     perror("read error");
     94                     exit(1);
     95                 }
     96                 else if(len == 0)
     97                 {
     98                     printf("客户端断开了连接
    ");
     99                     close(cfd);
    100                     break;
    101                 }
    102                 else
    103                 {
    104                     printf("recv buf: %s
    ", buf);
    105                     write(cfd, buf, len);
    106                 }
    107             }
    108             //干掉子进程
    109             return 0;
    110         }
    111         else if(pid > 0)
    112         {
    113             //parent process
    114             close(cfd);
    115             
    116         }
    117         
    118         
    119         
    120     }
    121     
    122     close(lfd);
    123     return 0;
    124 }

    >gcc process_server.c -o server

    >./server 9876

    客户端仍用之前的tcp_client.c,gcc tcp_client.c -o client 编译后为client

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    10、多线程并发服务器实现思路

    》伪代码:

     1 typedef struct sockInfo
     2 {
     3     pthread_t id;
     4     int fd;
     5     struct sockaddr_in addr;
     6 }SockInfo;
     7 //回调函数
     8 void *worker(void *arg)
     9 {
    10     while(1)
    11     {
    12         //打印客户端ip和port
    13         read();
    14         write();
    15     }
    16 }
    17 
    18 int main()
    19 {
    20     //监听
    21     int lfd = sock();
    22     //绑定
    23     bind();
    24     //设置监听
    25     listen();
    26     
    27     SockInfo sock[256];
    28     //父线程
    29     while(1)
    30     {
    31         int sock[i].fd = accept(lfd, &sock[i].addr, &len);
    32         //创建子线程
    33         pthread_create(&sock[i].id, NULL, worker, &sock[i]);
    34         pthread_deatch(sock[i].id);
    35 
    36     }    
    37 }

    11、多线程版服务器端代码实现

    >touch pthread_server.c

    >vi pthread_server.c

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <sys/types.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <arpa/inet.h>
      8 #include <ctype.h>
      9 #include <pthread.h>
     10 
     11 //自定义数据结构
     12 typedef struct SockInfo
     13 {
     14     int fd;//文件描述符
     15     struct sockaddr_in addr;//存放ip地址的结构体
     16     pthread_t id;//线程id
     17 }SockInfo;
     18 
     19 //子线程处理函数
     20 void *worker(void *arg)
     21 {
     22     char ip[64];
     23     char buf[1024];
     24     SockInfo *info = (SockInfo *)arg;
     25     //通信
     26     while(1)
     27     {
     28         printf("Client IP: %s, port: %d
    ", inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(info->addr.sin_port));
     29         int len = read(info->fd, buf, sizeof(buf));
     30         if(len == -1)
     31         {
     32             perror("read error");
     33             pthread_exit(NULL);
     34         }
     35         else if(len == 0)
     36         {
     37             printf("客户端已经断开了连接
    ");
     38             close(info->fd);
     39             break;
     40         }
     41         else
     42         {
     43             printf("recv buf: %s
    ", buf);
     44             write(info->fd, buf, len);
     45         }
     46     }
     47     return NULL;
     48 }
     49 
     50 //主函数
     51 int main(int argc, const char *argv[])
     52 {
     53     if(argc < 2)
     54     {
     55         printf("eg: ./a.out port
    ");
     56         exit(1);
     57     }
     58     struct sockaddr_in serv_addr;
     59     socklen_t serv_len = sizeof(serv_addr);
     60     int port = atoi(argv[1]);
     61     
     62     //创建套接字
     63     int lfd = socket(AF_INET, SOCK_STREAM, 0);
     64     //初始化服务器 sockaddr_in
     65     memset(&serv_addr, 0, serv_len);
     66     serv_addr.sin_family = AF_INET;//地址族
     67     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP
     68     serv_addr.sin_port = htons(port);//设置端口
     69     //绑定IP 和端口
     70     bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
     71     
     72     //设置同时监听的最大个数
     73     listen(lfd, 36);
     74     printf("Start accept ......
    ");
     75     
     76     int i = 0;
     77     SockInfo info[256];
     78     //规定 fd == -1
     79     for(i = 0; i < sizeof(info) / sizeof(info[0]); ++i)
     80     {
     81         info[i].fd = -1;
     82     }
     83     
     84     socklen_t cli_len = sizeof(struct sockaddr_in);
     85     while(1)
     86     {    
     87         //选一个没有被使用的,最小的数组元素
     88         for(i = 0; i < 256; ++i)
     89         {
     90             if(info[i].fd == -1)
     91             {
     92                 break;
     93             }
     94         }
     95         if(i == 256)
     96         {
     97             break;
     98         }
     99         //主线程 - 等待接收连接请求
    100         info[i].fd = accept(lfd, (struct sockaddr*)&info[i].addr, &cli_len);
    101         
    102         //创建子线程 - 通信
    103         pthrad_create(&info[i].i, NULL, worker, &info[i]);
    104         //设置线程分离
    105         pthread_detach(info[i].id);
    106         
    107     }
    108     
    109     close(lfd);
    110     
    111     //只退出主线程
    112     pthread_exit(NULL);
    113     return 0;
    114 }

    >gcc pthread_server.c -lpthread -o server

    >./server 9876

    (打开另外两个终端,执行./client  9876,然后分别输入数据,看原server终端的接收情况)

    在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    股票
    使用广播信道的以太网
    CSMA/CD 3
    可赎回债券
    matlab中的knn函数
    债券 账面值
    最优化作业 共轭梯度法 matlab代码
    债券和股票 溢价公式
    债券和股票
    CSMA/CD 续
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_11.html
Copyright © 2020-2023  润新知