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\n");
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 ......\n");
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\n",
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\n", buf);
77             // 小写 -》 大写
78             for(int i=0; i<len; ++i)
79             {
80                 buf[i] = toupper(buf[i]);
81             }
82             printf(" -- toupper: %s\n", buf);
83 
84             // 数据发送给客户端
85             write(cfd, buf, strlen(buf)+1);
86         }
87         else if( len == 0 )
88         {
89             printf("client disconnect ...\n");
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\n");
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("请输入要发送的字符串:\n");
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("服务器端关闭了连接\n");
64             break;
65         }
66         else
67         {
68             printf("read buf = %s, len = %d\n", 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\n"
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\n
216         {
217             *ptr++ = c;
218             if (c == '\n')
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 ...\n");
35 
36     clie_addr_len = sizeof(clie_addr_len);
37     cfd = Accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
38     printf("cfd = ----%d\n", cfd);
39 
40     printf("client IP: %s  port:%d\n", 
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\n", r);
31         len = Read(sfd, buf, sizeof(buf));
32         printf("Read len ========= %d\n", 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\n", pid);
 22     }
 23 }
 24 
 25 int main(int argc, const char *argv[])
 26 {
 27     if(argc < 2)
 28     {
 29         printf("eg: ./a.out port\n");
 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 ......\n");
 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\n");
 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\n", 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("客户端断开了连接\n");
 99                     close(cfd);
100                     break;
101                 }
102                 else
103                 {
104                     printf("recv buf: %s\n", 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\n", 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("客户端已经断开了连接\n");
 38             close(info->fd);
 39             break;
 40         }
 41         else
 42         {
 43             printf("recv buf: %s\n", 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\n");
 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 ......\n");
 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,转载请注明出处。

posted on 2020-07-06 12:17  Alliswell_WP  阅读(314)  评论(0编辑  收藏  举报

导航