做网络服务的时候并发服务端程序的编写必不可少。前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定.
常见的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[以下代码都没有做错误判断, 加上错误判断会造成代码翻倍, 实际开发中需要特别注意返回值]
#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; }
时间: 2024-10-13 11:45:17