流程 |
poll |
select |
1.建立一个文件描述符的表 |
建立结构体数组 struct pollfd fds[n]; |
fd_set线性表 |
2.将关心的文件描述符加到表中 |
结构体填充 fds[m].fd=fd; fds[m].events=POLLIN |
FD_SET(fd,&readfds) |
3. 然后调用一个函数。 select / poll 4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候 该函数才返回(阻塞)。 |
poll |
select |
5.判断 |
fds[m].revents == POLLIN |
FD_ISSET |
6.相关操作 |
1. 优化文件描述符个数的限制;(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组元素个数为1,如果想监听100个,那么这个结构体数组的元素个数就为100,由程序员自己来决定)
2. poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低
3. poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
struct pollfd *fds
参数:
关心的文件描述符数组struct pollfd fds[N];
nfds:个数
timeout: 超时检测
毫秒级的:如果填1000,1秒
如果-1,阻塞
struct pollfd
{
int fd; /* 检测的文件描述符 */
short events; /* 检测事件 */
short revents; /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */
};
事件: POLLIN :读事件
POLLOUT : 写事件
POLLERR:异常事件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc < 2)
{
printf("plase input \n" );
return -1;
}
//1.创建套接字,用于链接
int sockfd;
int acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.绑定 ip+port 填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;//协议族ipv4
saddr.sin_port = htons(atoi(argv[1]));//端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
socklen_t len = sizeof(saddr); //结构体大小
//bind绑定ip和端口
if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
{
perror("bind err");
return -1;
}
printf("bind success\n");
//3.启动监听,把主动套接子变为被动套接字
if (listen(sockfd, 1) < 0)
{
perror("listen err");
return -1;
}
printf("listen success\n");
char buf[64] = {0};
//引入poll
//创建表
int last = -1;
struct pollfd fds[100] = {0};
fds[++last].fd = 0;
fds[last].events = POLLIN;
fds[++last].fd = sockfd;
fds[last].events = POLLIN;
while (1)
{
int ret = poll(fds, last + 1, -1);
if (ret < 0)
{
perror("poll err");
return -1;
}
//判断
for (int i = 0; i <= last; i++)
{
if (fds[i].revents == POLLIN)
{
if (fds[i].fd == 0)
{
//5.执行操作
fgets(buf, sizeof(buf), stdin);
if (strncmp(buf, "quit", 4) == 0) //接收到quit退出
{
break;
}
if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
{
buf[strlen(buf) - 1] = '\0';
}
for (int j = 2; j <= last; j++) //遍历向链接的客户端发送信息
{
send(fds[j].fd, buf, sizeof(buf), 0); //发送
}
}
else if (fds[i].fd == sockfd)//链接客户端
{
//阻塞等待客户端的链接请求
acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
//获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
if (acceptfd < 0)
{
perror("accept err");
return -1;
}
printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
printf("connect success\n");
fds[++last].fd = acceptfd;
fds[last].events = POLLIN;
}
else
{//接收信号并转发给除当前客户端的其他客户端
int ret = recv(fds[i].fd, buf, sizeof(buf), 0); //接收的信号
if (ret < 0)
{
perror("recv err.");
return -1;
}
else if (ret == 0)
{
printf("%d client exit\n", i);
close(fds[i].fd); //关闭描述符
fds[i--] = fds[last--]; //将最后一个有 效数据与当前描述符位置置换
}
else
{
printf("client %d:%s\n", fds[i].fd, buf);
for (int j = 2; j <= last; j++)
{
if (i == j)
{
continue;
}
send(fds[j].fd, buf, sizeof(buf), 0); //发送
}
}
}
}
}
}
close(sockfd);
close(acceptfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* superset of previous */
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
if (argc < 2)
{
printf("plase input \n" );
return -1;
}
//1.创建套接字,用于链接
int sockfd;
int acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("sockfd:%d\n", sockfd);
//2.绑定 ip+port 填充结构体
struct sockaddr_in saddr;
saddr.sin_family = AF_INET; //协议族ipv4
saddr.sin_port = htons(atoi(argv[2])); //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示
socklen_t len = sizeof(saddr); //结构体大小
//3用于连接服务器;
if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)
{
perror("connect err");
return -1;
}
printf("connect success\n");
char buf[64] = {0};
//引入poll
//创建表
int last = -1;
struct pollfd fds[100] = {0};
fds[++last].fd = 0;
fds[last].events = POLLIN;
fds[++last].fd = sockfd;
fds[last].events = POLLIN;
while (1)
{
int ret = poll(fds, last + 1, -1);
if (ret < 0)
{
perror("poll err");
return -1;
}
//判断
for (int i = 0; i <= last; i++)
{
if (fds[i].revents == POLLIN)
{
if (fds[i].fd == 0)
{
//5.执行操作
fgets(buf, sizeof(buf), stdin);
if (buf[strlen(buf)] == '\0') //去掉fgets补的'\n'
{
buf[strlen(buf) - 1] = '\0';
}
send(sockfd, buf, sizeof(buf), 0); //发送
}
else if(fds[i].fd == sockfd)
{
int ret = recv(fds[i].fd, buf, sizeof(buf), 0); //接收的信号
if (ret < 0)
{
perror("recv err.");
return -1;
}
else if (ret == 0)
{
continue;
}
else
{
printf("client %d:%s\n", fds[i].fd, buf);
}
}
}
}
}
close(sockfd);
return 0;
}