做网络服务的时候并发服务端程序的编写必不可少。前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定.
常见的linux并发服务器模型;
-
多进程并发服务器
-
多线程并发服务器
-
select多路I/O转接服务器
-
poll多路I/O转接服务器
-
epool多路I/O转接服务器.
本次主要讨论poll多路I/转接并发服务器模型:
前几章介绍完了多进程并发服务器, 多线程并发服务器, selete多路I/O转接服务器, 本章开始介绍poll(linux特有)多路I/O转接模型.
由于多进程和多线程模型在实现中相对简单, 但由于其开销和CPU高度中比较大, 所以一般不用多线程和多进程来实现服务模型. select由于其跨平台, 但其最高上限默认为1024, 修改突破1024的话需要重新编译linux内核, poll完美解决了1024的限制.
主要用到API:
poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds: 传入传出结构体数组
nfds: 结构体数组数量
timeout: 监听时间
-1 阻塞等待
0 立刻返回, 不阻塞
>0 等待毫秒数
struct pollfd{
int fd; //监听的文件描述符
int events; //监听的事件 POLLIN监听读 POLLOUT 监听写 POLLERR 监听异常
int revents; // 监听事件中满足条件返回的事件
.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#define CLIENT_MAX 1024 //定义最大客户端监听
#define SERV_PORT 9096 //监听端口
int
main(
int
argc,
char
* argv[]){
int
listenfd, connfd;
struct
sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
struct
pollfd event[CLIENT_MAX];
int
maxi, opt, i, j, nready, n;
char
buf[BUFSIZ];
//创建tcp监听套接字
listenfd = socket(AF_INET, SOCK_STREAM, 0);
//设置端口复用
opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof
(opt));
//初始化为0
bzero(&serv_addr,
sizeof
(serv_addr));
serv_addr.sin_family = AF_INET;
//设置端口并转换为网络字节序
serv_addr.sin_port = htons(SERV_PORT);
//设置本机任意ip
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定listenfd
bind(listenfd, (
struct
sockaddr*)&serv_addr,
sizeof
(serv_addr));
//设置同时连接上限
listen(listenfd, SOMAXCONN);
//#define SOMAXCONN 128
//将监听套接字连接至事件
event[0].fd = listenfd;
event[0].events = POLLIN;
//初始化
for
(i = 1; i < CLIENT_MAX; i++){
event[i].fd = -1;
}
maxi = 0;
for
(;;){
nready = poll(event, maxi+1, -1);
//客户端请求连接
if
(event[0].revents & POLLIN){
clie_addr_len =
sizeof
(clie_addr);
//获取连接
connfd = accept(listenfd, (
struct
sockaddr*)&clie_addr, &clie_addr_len);
//打印提示
printf
(
"%s:%d client connect successfully!\n"
, inet_ntoa(clie_addr.sin_addr), ntohs(clie_addr.sin_port));
//添加至监听
for
(i = 1; i < CLIENT_MAX; i++){
if
(0 > event[i].fd){
event[i].fd = connfd;
break
;
}
}
//判断监听是否已满
if
(CLIENT_MAX == i){
//关闭连接
printf
(
"too many clients!\n"
);
close(connfd);
continue
;
}
//监听读事件
event[i].events = POLLIN;
if
(i > maxi)
maxi = i;
if
(0 == (--nready))
continue
;
}
for
(i = 1; i <= maxi; i++){
if
(0 > event[i].fd)
continue
;
//是否有事件
if
(event[i].revents & POLLIN){
//清空
bzero(buf,
sizeof
(buf));
n = read(event[i].fd, buf,
sizeof
(buf));
//对方是否已关闭
if
(0 == n){
clie_addr_len =
sizeof
(clie_addr);
//获取客户端信息
getpeername(event[i].fd, (
struct
sockaddr*)&clie_addr, &clie_addr_len);
printf
(
"%s:%d client disconnect successfully!\n"
, inet_ntoa(clie_addr.sin_addr), ntohs(clie_addr.sin_port));
//关闭客户端连接
close(event[i].fd);
//将事件数组中初始化为-1
event[i].fd = -1;
}
else
if
(0 < n){
//转换为大写
for
(j = 0; j < n; j++){
buf[j] =
toupper
(buf[j]);
}
//回写给客户端
write(event[i].fd, buf, n);
}
if
(0 == (--nready)){
break
;
}
}
}
}
close(listenfd);
return
0;
}
|
本文转自asd1123509133 51CTO博客,原文链接:http://blog.51cto.com/lisea/1791967,如需转载请自行联系原作者