我是靠谱客的博主 现实砖头,这篇文章主要介绍Linux系统编程(35)—— socket编程之TCP服务器的并发处理,现在分享给大家,希望可以做个参考。


我们知道,服务器通常是要同时服务多个客户端的,如果我们运行上一篇实现的server和client之后,再开一个终端运行client试试,新的client就不能能得到服务了。因为服务器之支持一个连接。

网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端。但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程。

下面是代码框架:

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
listenfd = socket(...); bind(listenfd, ...); listen(listenfd, ...); while (1) { connfd= accept(listenfd, ...); n= fork(); if(n == -1) { perror("callto fork"); exit(1); }else if (n == 0) { close(listenfd); while(1) { read(connfd,...); ... write(connfd,...); } close(connfd); exit(0); }else close(connfd); }

 

现在做一个测试,首先启动server,然后启动client,然后用Ctrl-C使server终止,这时马上再运行server,结果是:

 binderror: Address already in use

这是因为,虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监听同样的server端口。server终止时,socket描述符会自动关闭并发FIN段给client,client收到FIN后处于CLOSE_WAIT状态,但是client并没有终止,也没有关闭socket描述符,因此不会发FIN给server,因此server的TCP连接处于FIN_WAIT2状态。

 

现在用Ctrl-C把client也终止掉,再观察现象结果是:

 binderror: Address already in useclient

终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后就可以再次启动server了。

在server的TCP连接没有完全断开之前不允许重新监听是不合理的,因为,TCP连接没有完全断开指的是connfd(127.0.0.1:8000)没有完全断开,而我们重新监听的是listenfd(0.0.0.0:8000),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。在server代码的socket()和bind()调用之间插入如下代码: 

复制代码
1
2
int opt = 1; setsockopt(listenfd, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt));

select是网络程序中很常用的一个系统调用,它可以同时监听多个阻塞的文件描述符(例如多个网络连接),哪个有数据到达就处理哪个,这样,不需要fork和多进程就可以实现并发服务的server。

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* server.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 8000 int main(int argc, char **argv) { inti, maxi, maxfd, listenfd, connfd, sockfd; intnready, client[FD_SETSIZE]; ssize_tn; fd_setrset, allset; charbuf[MAXLINE]; charstr[INET_ADDRSTRLEN]; socklen_tcliaddr_len; structsockaddr_in cliaddr, servaddr; listenfd= Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr= htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr)); Listen(listenfd,20); maxfd= listenfd; /* initialize */ maxi= -1; /* indexinto client[] array */ for(i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* -1 indicates available entry */ FD_ZERO(&allset); FD_SET(listenfd,&allset); for( ; ; ) { rset= allset; /* structure assignment */ nready= select(maxfd+1, &rset, NULL, NULL, NULL); if(nready < 0) perr_exit("selecterror"); if(FD_ISSET(listenfd, &rset)) { /* new client connection */ cliaddr_len= sizeof(cliaddr); connfd= Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); printf("receivedfrom %s at PORT %dn", inet_ntop(AF_INET, &cliaddr.sin_addr,str, sizeof(str)), ntohs(cliaddr.sin_port)); for(i = 0; i < FD_SETSIZE; i++) if(client[i] < 0) { client[i]= connfd; /* save descriptor */ break; } if(i == FD_SETSIZE) { fputs("toomany clientsn", stderr); exit(1); } FD_SET(connfd,&allset); /* add newdescriptor to set */ if(connfd > maxfd) maxfd= connfd; /* for select */ if(i > maxi) maxi= i; /* max index in client[] array */ if(--nready == 0) continue; /* no more readable descriptors */ } for(i = 0; i <= maxi; i++) { /* check allclients for data */ if( (sockfd = client[i]) < 0) continue; if(FD_ISSET(sockfd, &rset)) { if( (n = Read(sockfd, buf, MAXLINE)) == 0) { /*connection closed by client */ Close(sockfd); FD_CLR(sockfd,&allset); client[i]= -1; }else { intj; for(j = 0; j < n; j++) buf[j]= toupper(buf[j]); Write(sockfd,buf, n); } if(--nready == 0) break; /* no more readable descriptors */ } } } }


 

 

转载于:https://www.cnblogs.com/new0801/p/6176967.html

最后

以上就是现实砖头最近收集整理的关于Linux系统编程(35)—— socket编程之TCP服务器的并发处理的全部内容,更多相关Linux系统编程(35)——内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部