最近一直在看UNP,2020年8月的暑假初学的网络编程,最开始接触的是windows的select函数。尽管select函数(我的博客首页置顶的小QQ就拿select做的,所以不打算写关于select函数的使用说明)和poll函数在实际开发并不会用到,但还是想学一下,也不是很难使用,花两三个小时做一下实验还是值的的!
参数解释
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
第一个参数是一个结构体,结构体里的成员变量fd也可以理解为存放客户端的套接字,events是给这个对象设置什么样子的事件,revents是返回事件的结果。第三个参数是超时,-1是一直阻塞直到有事件产生。0是非阻塞,>0就是设置一个超时时间。
返回值
-1表示出错了,0表示超时了,>0表示产生时间的个数
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
思路讲解
当accept成功产生一个套接字的时候,我们可以遍历1——OPEN_MAX,那里有空地就插入到那里。在设置一下他的事件类型,然后就break。后面的循环用一个max_client来记录最大的套接字的下标,稍微提一下速度。
代码部分
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
#include <poll.h>
#include <arpa/inet.h>
#include <sys/wait.h>
using namespace std;
constexpr int OPEN_MAX = 64;
#ifndef INFTIM
#define INFTIM -1
#endif
int main(int argc,char** argv){
int server_socket = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);
if(server_socket == -1){
cout<<"服务器套接字创建失败"<<endl;
}
sockaddr_in addr;
addr.sin_family = AF_INET;
int port = 9996;
if(argc > 1){
port = atoi(argv[1]);
}
addr.sin_port = htons(port);
inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
if (bind(server_socket,(sockaddr*)&addr,sizeof(addr) ) < 0 ) {
cout << "bind绑定失败" << endl;
return -1;
}
if (listen(server_socket, SOMAXCONN) < 0) {
cout << "监听失败" << endl;
return -1;
}
struct pollfd client_fd[OPEN_MAX];
client_fd[0].fd = server_socket;
client_fd[0].events = POLLRDNORM;
for(int i = 1;i< OPEN_MAX;++i){
client_fd[i].fd = -1;
}
cout<<"服务器启动成功!"<<endl;
char buff[1024] = { 0 };
int max_client = 0;
while(1){
int nready = poll(client_fd,max_client+1,-1);
if(client_fd[0].revents & POLLRDNORM){//说明有新的连接
int client = accept(server_socket,NULL,NULL);
if(client < 0){
if(errno == EINTR){
continue;
}
continue;
}
for(int i = 1;i < OPEN_MAX;++i){
if(client_fd[i].fd != -1)continue;
client_fd[i].fd = client;
client_fd[i].events = POLLIN;
cout<<"有新的连接,i的下标为"<<i<<endl;
max_client = max(i,max_client);
break;
}
}
for(int i = 1;i <= max_client;++i){
if(client_fd[i].fd == -1){
continue;
}
if(client_fd[i].revents & POLLIN){
// cout<<"接收数据"<<endl;
int r = recv(client_fd[i].fd,buff,sizeof(buff),0);
if(r == 0)
{
cout<<"关闭连接"<<endl;
close(client_fd[i].fd);
client_fd[i].fd = -1;
continue;
}
buff[r] = '\0';
cout<<"服务器收到一条消息"<<buff<<endl;
--nready;
}
}
}
return 0;
}
makefile
我的makefile并没有自动执行,因为有时候如果先关闭服务器在关闭客户端,该端口会进入TIME_WAIT状态,端口绑定会失败,所以我一般都会手动./poll_server 执行,如果默认9996失败,我可以./poll_server 9997换一个端口绑定
poll_server:poll.cpp
g++ $^ -o $@
clean:
rm -f *.o