吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 951|回复: 3
收起左侧

[讨论] poll小例子

[复制链接]
古月不傲 发表于 2021-1-11 16:10
[C++] 纯文本查看 复制代码
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <poll.h>
#include <vector>
#include <netinet/in.h> 	// INADDR_ANY
#include <errno.h>
#include <arpa/inet.h>		// inet_ntoa
#include <cstring>
#include <unistd.h>

int main(void)
{
	// 防止服务器关闭
	signal(SIGPIPE, SIG_IGN);
	// 防止产生僵尸进程
	signal(SIGCHLD, SIG_IGN);

	// 创建socket	非阻塞 | 子进程中关闭该套接字
	int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
	if (listen_fd == -1)
		return -1;
	
	// 初始化socket信息
	struct sockaddr_in server_addr {};
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(5578);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int on = 1;
	int ret;

	// 防止time_wait
	ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
	if (ret == -1)
		return -1;

	// 绑定端口
	ret = bind(listen_fd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
	if (ret == -1)
		return -1;

	// 监听端口
	ret = listen(listen_fd, 1);
	if (ret == -1)
		return -1;

	// poll读事件,先加入监听客户端连接事件
	struct pollfd read_pollfd;
	read_pollfd.fd = listen_fd;
	read_pollfd.events = POLLIN;
	read_pollfd.revents  = 0;

	std::vector<pollfd> vec_read_poll;
	vec_read_poll.push_back(read_pollfd);

	// poll 服务器写事件
	struct pollfd write_pollfd {};
	std::vector<pollfd> vec_write_poll;

	struct sockaddr_in client_addr {};
	socklen_t addr_len = sizeof(struct sockaddr);
 
	int read_ready;
	int write_read_ready;
	int connected_fd;

	while (1)
	{
		read_ready = poll(vec_read_poll.data(), vec_read_poll.size(), -1);	
		// poll失败
		if (read_ready == -1)
		{
			if (errno == EINTR)
				continue;
			if (errno == EMFILE)
			{
				printf("文件描述符超出上限\n");
				return -1;
			}
			return -1;
		}
		// 没有客户端连接
		else if (read_ready == 0)
			continue;
		// 客户端连接过来了
		else if (vec_read_poll[0].revents & POLLIN)	
		{
			--read_ready;

			// 建立三次握手
			connected_fd = accept4(listen_fd, (sockaddr *)&client_addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC);

			if (connected_fd == -1)
			{
				if (errno == EINTR)
					continue;
				else if (errno == ECONNABORTED)
				{
					close(connected_fd);
					continue;
				}
				else 
					return -1;
			}	

			// 注册读事件
			read_pollfd.fd = connected_fd;
			read_pollfd.events = POLLIN;
			read_pollfd.revents = 0;

			vec_read_poll.push_back(read_pollfd);

			// 三次握手成功,打印客户端信息
			std::cout << "client_ip = " << inet_ntoa(client_addr.sin_addr) << std::endl;
			std::cout << "client_port = " << ntohs(client_addr.sin_port) << std::endl;

			if (read_ready == 0)
				continue;
		}
		// read_ready > 0 说明客户端发来信息了 遍历读事件
		for (auto it = vec_read_poll.begin() + 1; it != vec_read_poll.end() && read_ready; it++)
		{
			// 客户端发来了消息
			if (it->revents & POLLIN)
			{
				--read_ready;

				char buf[1024] {};
				ret = recv(it->fd, buf, sizeof(buf), 0);
				if (ret == -1)
				{
					// 接收缓冲区中没有数据
					if (errno == EAGAIN || errno == EWOULDBLOCK)
						continue;
					if (errno == EINTR)
						continue;
					return -1;
				}
				// 客户端正常调用 close 四次挥手
				if (ret == 0)
				{
					std::cout << "client closed" << std::endl;
					it = vec_read_poll.erase(it);
					--it;	

					close(connected_fd);
					continue;
				}
				std::cout << buf;

				ret = send(it->fd, buf, strlen(buf) + 1, 0);

				if (ret == -1)
				{
					// 如果发送缓冲区满的话,就注册写事件
					if (errno == EAGAIN || errno == EWOULDBLOCK)
					{
						write_read_ready = poll(vec_write_poll.data(), vec_write_poll.size(), -1);	
						// poll失败
						if (write_read_ready == -1)
						{
							if (errno == EINTR)
								continue;
							return -1;
						}

						write_pollfd.fd = it->fd;
						write_pollfd.events = POLLOUT;
						write_pollfd.revents = 0;

						vec_write_poll.push_back(write_pollfd);
						continue;
					}
					if (errno == EINTR)
						continue;
					return -1;
				}

				// 遍历写事件
				for (auto it = vec_write_poll.begin(); it != vec_write_poll.end() && write_read_ready; it++)
				{
					if (write_pollfd.revents & POLLOUT)
					{
						write_read_ready--;

						send(it->fd, buf, strlen(buf) + 1, 0);

						it = vec_read_poll.erase(it);
						--it;		
					}
				}
			}
		}
	}
	return 0;
}

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ayaoko 发表于 2021-1-11 16:24
这个能不能搞到windows上搞阿
pzx521521 发表于 2021-1-11 17:01
ayaoko 发表于 2021-1-11 16:24
这个能不能搞到windows上搞阿

不能 windows 没有poll 有  WSAPoll 替换就好了
ayaoko 发表于 2021-1-13 15:50
pzx521521 发表于 2021-1-11 17:01
不能 windows 没有poll 有  WSAPoll 替换就好了

大侠这个有没有跨平台,有没有例子给一个,多谢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 04:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表