对比c++网络编程实现多路io访问的几种方法

1.使用线程池

        优点是简单,易于管理,多线程对运算密集型访问更友好,缺点为线程的上下文切换开销大,需要考虑线程安全,数据访问的竞态条件

        实现方法如下:

    //创建本地服务
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(struct sockaddr_in));

	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(2048);

	if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr))) {
		perror("bind");
		return -1;
	}

	listen(sockfd, 10);
    //对每条连接创建线程
    	while (1) {

		struct sockaddr_in clientaddr;
		socklen_t len = sizeof(clientaddr);
		printf("阻塞在accept");
		int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);

		pthread_t thid;
		pthread_create(&thid, NULL, client_thread, &clientfd);
		

	}
    //针对每条连接做解析
    void *client_thread(void *arg) {

	int clientfd = *(int *)arg;

	while (1) {

		char buffer[128] = {0};
		int count = recv(clientfd, buffer, 128, 0);
		if (count == 0) {
			break;
		}
		
		//
		
		send(clientfd, buffer, count, 0);
		printf("clientfd: %d, count: %d, buffer: %s\n", clientfd, count, buffer);

	}

	close(clientfd);
}

2.使用select、poll

        优点:比线程开销小,适合I/O密集型任务,开关延迟低,不适合运算密集型任务

        缺点:管理的端口数有上限(默认1024个),每次建立连接需要复制所有fp状态到内核,速度慢

       poll 实现方法:

    struct pollfd fds[1024] = {0};//建立poll池

	fds[sockfd].fd = sockfd;//将服务fd加入到poll池中
	fds[sockfd].events = POLLIN;//设置输入事件为POLLIN,表示关注读取

	int maxfd = sockfd;//初始化最大访问的文件描述符

	while (1) {

		int nready = poll(fds, maxfd+1, -1);//等待一个新的连接,-1表示无超时

		if (fds[sockfd].revents & POLLIN) { //如果fd的返回事件是读取

			struct sockaddr_in clientaddr;
			socklen_t len = sizeof(clientaddr);
			
			int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);//建立客户端连接

			printf("sockfd: %d\n", clientfd);
			fds[clientfd].fd = clientfd;//将客户端fd加入poll池
			fds[clientfd].events = POLLIN;//关注读取

			maxfd = clientfd;
		} 

		int i = 0;
		for (i = sockfd+1;i <= maxfd;i ++ ) {//遍历新的链接

			if (fds[i].revents & POLLIN) {//如果fd的返回事件是读取

				char buffer[128] = {0};
				int count = recv(i, buffer, 128, 0);//读取一次数据
				if (count == 0) {
					printf("disconnect\n");

					fds[i].fd = -1;//从poll池中清除本次连接
					fds[i].events = 0;//事件输入为空
			
					close(i);//关闭链接
					
					continue;
				}
				
				send(i, buffer, count, 0);//返回数据
				printf("clientfd: %d, count: %d, buffer: %s\n", i, count, buffer);

			}

		}

	}

3.使用epoll

        优点:I/O速度很快,缺点:可读性不如旧一些的方法

        实现方法:


	int epfd = epoll_create(1); // 初始化信息缓存空间

	//pthread_create();

	struct epoll_event ev; //初始化服务端事件
	ev.events = EPOLLIN; //设置为输入
	ev.data.fd = sockfd; //设置fd

	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev); //将服务端、缓存空间、事件绑定

	struct epoll_event events[1024] = {0}; //建立事件传输的缓存
	while (1) {

		int nready = epoll_wait(epfd, events, 1024, -1); //等待一个新的连接

		int i = 0;
		for (i = 0;i < nready;i ++) {//遍历所有连接

			int connfd = events[i].data.fd;
			if (sockfd == connfd) {//如果连接的是本程序的服务

				struct sockaddr_in clientaddr;
				socklen_t len = sizeof(clientaddr);
				
				int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);//建立服务端的连接

				ev.events = EPOLLIN | EPOLLET; //事件关注读取、边沿触发模式(发生状态变化时触发,默认为水平触发,即读完为止)
				ev.data.fd = clientfd;//事件绑定用户fd
				epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);//绑定用户、事件、缓存空间

				printf("clientfd: %d\n", clientfd);

			} else if (events[i].events & EPOLLIN) {

				
				char buffer[10] = {0};
				int count = recv(connfd, buffer, 10, 0);//将数据读出来
				if (count == 0) {
					printf("disconnect\n");

					epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL); //解绑用户、缓存空间,事件置空
					close(i);//关闭端口
					
					continue;
				}
				
				send(connfd, buffer, count, 0);//向用户返回数据
				printf("clientfd: %d, count: %d, buffer: %s\n", connfd, count, buffer);

			}

		}

	}

        这里强调一下,关于触发模式,对于比较大的数据发送更适合用边沿触发,以保证传输效率,对大部分情况适合用水平触发以保证主句完整及易于解读。

总结:  

        如果处理每个访问需要运算密集数据则适合使用线程池,如果处理I/O密集型任务则适合使用epoll,select和poll的代码更为简洁,易于管理,对轻小型任务可以考虑使用,对于大规模并发任务不如epoll或线程池表现强。

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值