c++ socket 多路复用之poll

1.基础知识

poll机制与select机制类似,通过管理文件描述符来进行轮询,效率更高,并且处理的连接个数不受内核的限制

原理:I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

2.poll函数

# include <poll.h> 
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

第一个参数:即为可读套接字

第二个参数:即为最大描述符

第三个参数:即为超时

 

pollfd的结构体:

struct pollfd 
{

    int fd;         /* 文件描述符 */

    short events;         /* 等待的事件 */

    short revents;       /* 实际发生了的事件 */
    
} ; 

3.服务端

流程:

1、创建套接字,即调用socket(),根据需要选择参数类型

2、根据地址和端口号,绑定服务端,即调用bind()

3、将套接字设为监听套接字,并设置监听数,即调用listen()

4、开始检测监听套接字与客户端套接字信号,即调用poll()

4、阻塞等待等待客户端连接请求,即调用accept()

5、接受发送消息,即调用recv(),send()函数

6、关闭套接字,即调用close()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;
#define backlog 10


class Server
{
	private:
		int port;   // 定义端口号
		int listenfd;	// 定义监听套接字
		int clientfd;	// 定义客户端套接字
		int i;		
		int maxfd;	// 定义套接字最大描述符
		struct sockaddr_in clientaddr;	// 定义客户端地址结构体
		struct sockaddr_in listenaddr;	// 定义监听服务端地址结构体
		struct pollfd readfds[backlog];	// 定义poll套接字结构体,可理解为是一个数组
		socklen_t clientaddrlen;	// 定义客户端地址长度
	public:
		Server(int port = 10000):port(port),listenfd(-1),clientfd(-1)
		// 初始化端口号,套接字描述符
		{
			
			listenaddr.sin_family = AF_INET;	// 初始化监听套接字协议族为AF_INET
			listenaddr.sin_port = htons(port);	// 初始化监听套接字端口号
			listenaddr.sin_addr.s_addr = htonl(INADDR_ANY);	// 初始化监听套接字地址
			listenfd = socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0); // 创建套接字,并设置为非阻塞模式
			int bind_val = bind(listenfd,(struct sockaddr*)&listenaddr,sizeof(listenaddr));	// 绑定套接字
			bind_result(bind_val);	// 验证绑定结果
			int listen_val = listen(listenfd,backlog);	// 监听套接字
			listen_rsult(listen_val);	// 验证监听结果
		}
		void poll_run()
		{
			init_readfds(); 	// 初始化可读套接字
			while(1)
			{
				maxfd = get_max();	// 获取最大套接字
				int poll_val = poll(readfds,maxfd+1,5); // 检测各个套接字信号变化
				for(i=0;i<backlog;i++)
				{
					if(readfds[i].fd==listenfd)
					{
						clientfd = accept(listenfd,(struct sockaddr*)&clientaddr,&clientaddrlen);
						// 检测客户端连接请求
						if(accept_result(clientfd)==-1)
						{
							continue;
						}else if(accept_result(clientfd)==0)
						{
							continue;
						}else
						{
							add_client(clientfd);
						}
					}else if(readfds[i].fd>0)
					{
						char data[1024];
						int recv_val = recv(readfds[i].fd,data,sizeof(data),0);
						// 接受客户端的数据请求
						int rv = recv_result(recv_val);
						if(rv==-1)
						{
						    continue;
						}else if(rv==0)
						{
						    remove_client(readfds[i].fd);
						    continue;
						}else
						{
						  cout << data << "\n" ; 
						}
					}else
					{
						cout << "";
					}
				}
			}

		}
		void add_client(int c)
		// 添加客户端到可读套接字
		{
			readfds[c].fd = c;
			readfds[c].events = POLLIN;
		}
		void remove_client(int c)
		// 移除客户端套接字
		{
			readfds[c].fd = -1;
		}
		int get_max()
		// 获取最大描述符
		{
			maxfd = -1;
			for(i=0;i<backlog;i++)
			{
				if(readfds[i].fd>maxfd)
					maxfd = readfds[i].fd;
			}
			return maxfd;
		}
		
		int recv_result(int recv_val)
		// 校验recv结果
		{
			if(recv_val==-1)
			{
				cout << "Recv error..." << "\n";
				sleep(3);
				return -1;
			}else if(recv_val==0)
			{
				
				cout << "Client connect close..." << "\n";
				return 0;
			}else
			{
				return 1;
			}
		}
		int accept_result(int accept_val)
		// 校验accept结果
		{
			if(accept_val==-1)
			{
				//cout << "Accept error..." << "\n";
				return -1;
			}else if(accept_val==0)
			{
				 return 0;
			}else
			{
				return 1;
			}
		}
		int poll_result(int poll_val)
		// 校验poll结果
		{
			if(poll_val==-1)
			{
				cout << "Poll error..." << "\n";
				return -1;
			}else if(poll_val==0)
			{
				return 0;
			}else
			{
				return 1;
			}
		}
		void init_readfds()
		// 初始化可读套接字
		{
			for(i=0;i<backlog;i++)
			{
				readfds[i].fd = -1;
				readfds[i].events = POLLIN;
			}
			readfds[listenfd].fd = listenfd;
			readfds[listenfd].events = POLLIN;
		}
		void bind_result(int bind_val)
		// 校验绑定结果
		{
			if(bind_val<0)
			{
				cout << "Bind error..." << "\n";
				exit(0);
			}else
			{
				cout << "Bind successful" << "\n";
			}
		}
		void listen_result(int listen_val)
		// 校验监听结果
		{
			if(listen_val<0)
			{
				cout << "Listen error..." << "\n";
				exit(0);
			}else
			{
				cout << "Listen successful"<< "\n";
			}
		}
		~Server()
		{
			cout << "close serve..." << "\n";
			for(i=0;i<backlog;i++)
			{
				close(readfds[i].fd);	// 退出程序时,关闭所有套接字
			}
		}
};

int test()
{
	Server *s = NULL;	// 定义Server指针
	try
	{
		s = new Server; // 为指针分配内存空间
		s->poll_run(); // 运行poll
	}catch(const char* e)
	{
		cout << e << "\n";
	}
	delete s;  // 删除指针
}

int main()
{
	test();
}

 

 

4、客户端

流程:

1、创建套接字,即调用socket()

2、根据地址,端口号绑定地址,即调用bind()

3、发起连接服务端请求,即调用connect()

4、接收发消息,即recv(),send()

5、关闭套接字,close()

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <cstring>
#include <cstdio>
#include <signal.h>

using namespace std;

class Client
{
        private:
                int port;
                int serverfd;
                struct sockaddr_in serveraddr;
        public:
                Client(int port = 10000):port(port),serverfd(-1)
                {
                        serverfd = socket(AF_INET, SOCK_STREAM, 0);
                        if(serverfd == -1)
                        {
                                throw("create socket error");
                        }
                        serveraddr.sin_family = AF_INET;
                        serveraddr.sin_port = htons(port);
                        inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr.s_addr);
                        int ret1;
                        ret1 = connect(serverfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
                }
                void run()
                {
                        char* data;
                        int ret2;
                        data = "hello";
                        ret2 = send(serverfd, data, strlen(data), 0);
                }
                ~Client()
                {
                        cout << "close client..." << "\n";
                        if (serverfd == -1)
                        {
                                close(serverfd);
                        }
                }
};
int test()
{
        try
        {
                Client *c = NULL;
                c = new Client;
                c->run();
                delete c;
        }catch(const char* e)
        {
                cout << e << "\n";
        }
}

int main(int argc, char *argv[])
{
        test();
}

以上代码亲测有效

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值