select函数原型
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
int nfds : 监听的文件描述符中最大的文件描述符+1
fd_set *readfds, 读
fd_set *writefds, 写
fd_set *exceptfds, 异常
struct timeval *timeout :超时时间
NULL 阻塞监听
>0 设置监听超时时长
0 非阻塞监听 轮询
返回值: > 0 返回满足对应时间的总数
0 没有满足监听条件的文件描述符
-1 error
优缺点:
缺点:存在数量上限 (一般为1024)
返回后需要轮询查看是否就绪,为提高效率需要编写其他代码
如:使用数组存储监听的描述符,减少select之后处理时的循环次数
优点:具有良好的跨平台性
相关函数:
void FD_CLR(int fd, fd_set *set); 将一个文件描述符从集合中移除
int FD_ISSET(int fd, fd_set *set); 判断文件描述符是否在集合中
void FD_SET(int fd, fd_set *set); 将待监听的文件描述符,添加到监听集合中
void FD_ZERO(fd_set *set); 清空一个文件描述符集合
使用select实现简单服务器
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int serv_fd, con_fd;
//创建socket
serv_fd = socket(AF_INET, SOCK_STREAM, 0);
if (serv_fd < 0)
{
printf("socket create error");
return -1;
}
//绑定 IP + 端口
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8000);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(serv_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
//设置监听上限
listen(serv_fd, 5);
//创建监听集合
fd_set rset, allset;
int maxfd = serv_fd;
FD_ZERO(&allset);
FD_SET(serv_fd,&allset);
struct sockaddr_in client_addr;
int client_len = sizeof(client_addr);
int ready = 0;
//存储读到的数据
char rbuf[BUFSIZ];
char wbuf[BUFSIZ] = { "HELLO" };
int rlen = 0;
while (1)
{
rset = allset;
if ((ready = select(maxfd + 1, &rset, NULL, NULL, NULL)) > 0)
{
//判断有新的连接
if (FD_ISSET(serv_fd, &rset))
{
con_fd = accept(serv_fd,(struct sockaddr *)&client_addr ,&client_len );
//将新连接的加入监听集合
FD_SET(con_fd, &allset);
if (con_fd > maxfd)
{
maxfd = con_fd;
}
//仅有连接就绪
if (0 == --ready)
{
continue;
}
}
//已有的连接进行交流
for (int i = serv_fd + 1; i <= maxfd; i++)
{
if (FD_ISSET(i, &rset))
{
memset(rbuf, 0, BUFSIZ);
rlen = read(i,rbuf,BUFSIZ);
if (rlen <= 0)
{
//关闭并移除监听集合
printf("client close\n");
FD_CLR(i, &allset);
close(i);
}
else
{
printf("%s", rbuf);
write(i, wbuf, BUFSIZ);
}
}
}
}
}
return 0;
}
poll函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd *fds:监听的集合
nfds_t nfds :监听的数量
int timeout :超时时间 与select不同在-1 为阻塞
相关内容:
结构体
struct pollfd {
int fd; /* file descriptor 对应的文件描述符*/
short events; /* requested events 监听事件 */ POLLIN :读事件
short revents; /* returned events 监听返回的结果*/
};
判断事件使用 if(client[i].revents & POLLIN)
优缺点:
缺点:不能跨平台
无法直接定位事件描述符
优点:自带数组结构,将监听集合和返回集合分离
扩展监听上限
使用poll实现简单服务器
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
int main()
{
int listenfd, connfd;
struct sockaddr_in serv_addr;
int client_len;
struct sockaddr_in client_addr;
//创建监听套接字
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
printf("listenfd create fail");
return -1;
}
printf("listenfd create success!\n");
//绑定IP 端口
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8000);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
//设置监听
listen(listenfd, 5);
//创建pool
struct pollfd client[1024];
int maxi = -1;
for (int i = 0; i < 1024; i++)
{
client[i].fd = -1;
}
int nready = 0;
client[0].fd = listenfd;
client[0].events = POLLIN;
maxi++;
int i = 0;
int nread;
char rBuf[BUFSIZ];
char wBuf[1024] = { "HELLO\n" };
while (1)
{
//使用poll监听 -1阻塞监听
printf("poll start !\n");
nready = poll(client, maxi + 1, -1);
printf("poll have %d ready!\n",nready);
//判断是否为连接请求
if (client[0].revents & POLLIN)
{
client_len = sizeof(client_addr);
connfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
printf("port %d\n",ntohs(client_addr.sin_port));
for (i = 0; i < 1024; i++)
{
if (client[i].fd < 0)
{
client[i].fd = connfd;
break;
}
}
//已达最大监听数
if (i == 1024)
{
printf("too many client");
continue;
}
client[i].events = POLLIN;
if (i > maxi)
{
maxi = i;
}
if (--nready == 0)
{
continue;
}
}
//处理请求
for (i = 1; i < maxi+1; i++)
{
if (client[i].fd < 0)
{
continue;
}
if (client[i].revents & POLLIN)
{
memset(rBuf, 0, sizeof(rBuf));
nread = read(client[i].fd, rBuf, BUFSIZ);
if (nread == 0)
{
printf("client close\n");
close(client[i].fd);
client[i].fd = -1;
}
else if(nread < 0)
{
printf("read error\n");
return -1;
}
else
{
printf("%s", rBuf);
write(client[i].fd, wBuf, sizeof(wBuf));
}
if (--nready == 0)
{
continue;
}
}
}
}
}
epoll函数
1.创建一个epoll文件描述符
int epoll_create(int size)
传入:size 提供给内核的参考大小
返回:返回一个epfd 文件描述符
2.控制epoll
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd :epoll_create 的返回值
op: EPOLL_CTL_ADD :添加fd到监听队列
EPOLL_CTL_MOD:修改监听事件
EPOLL_CTL_DEL:从监听中删除
fd:待操作的fd
event:结构体 需要设置
.data.fd :待操作的fd
.events : EPOLLIN / EPOLLOUT / EPOLLERR
删除传NULL即可
3.监听epoll内容
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
传入:epfd:epoll_create的返回值
*events:数组指针
maxevents:监听的数量
timeout :超时时间 同poll
传出:*events:所有满足监听条件的文件描述符
返回值:满足监听条件的数量
events[i] 可以直接访问满足条件的文件描述符
优缺点:
缺点:不能跨平台
优点:能够返回具体的满足监听条件的集合,IO效率不随FD数目增加而线性下降
能够扩展监听上限
使用epoll实现简单服务器
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
int main()
{
//创建socket
int listenfd, connfd;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
//绑定IP 端口
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8000);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
//设置监听
listen(listenfd, 5);
//创建epoll
int epfd = epoll_create(100);
//操作epfd 将listenfd加入
struct epoll_event epevent;
epevent.events = EPOLLIN;
epevent.data.fd = listenfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &epevent);
int maxevent = 1;
struct epoll_event ready_event[1024];
int nready = 0;
int nread = 0;
struct sockaddr_in client_addr;
int client_len = 0;
char rBuf[BUFSIZ];
char wBuf[10] = { "HELLO" };
while(1)
{
//epoll开始监听
printf("epoll wait start\n");
nready = epoll_wait(epfd, ready_event, maxevent, -1);
printf("epoll have %d ready\n",nready);
for (int i = 0; i < nready; i++)
{
//处理新的连接请求
if (ready_event[i].data.fd == listenfd)
{
client_len = sizeof(client_addr);
connfd = accept(listenfd,(struct sockaddr *)&client_addr ,&client_len );
//将新的连接对象加入到监听集合中
epevent.events = EPOLLIN;
epevent.data.fd = connfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &epevent);
maxevent++;
}
else
{
//处理信息
memset(rBuf, 0, BUFSIZ);
nread = read(ready_event[i].data.fd, rBuf, BUFSIZ);
if (0 == nread)
{
printf("client close!! \n");
epoll_ctl(epfd, EPOLL_CTL_DEL, ready_event[i].data.fd, NULL);
close(ready_event[i].data.fd);
maxevent--;
}
else if (nread < 0)
{
printf("read error!!\n");
epoll_ctl(epfd, EPOLL_CTL_DEL, ready_event[i].data.fd, NULL);
close(ready_event[i].data.fd);
maxevent--;
}
else
{
printf("%s", rBuf);
write(ready_event[i].data.fd, wBuf, 10);
}
}
}
}
}