做网络服务的时候并发服务端程序的编写必不可少。前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定.

    常见的linux并发服务器模型;


  • 多进程并发服务器

  • 多线程并发服务器

  • select多路I/O转接服务器

  • poll多路I/O转接服务器

  • epool多路I/O转接服务器.


    本次主要讨论poll多路I/转接并发服务器模型:

    linux网络编程----->高并发--->poll多路I/O转接服务器_第1张图片


    前几章介绍完了多进程并发服务器,  多线程并发服务器, 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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;	
}