1.select
(1).函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
(2).返回值:返回客户端连接的数量
(3).参数:nfds:最大的fd号,客户端fd会从4开始,0为stdin, 1为stdout, 2为stderr, 3为listen;
readfds:可读端口的集合, fd_set为长度1024的bit位;
writefds:可写端口的集合;
exceptfds:出错端口的集合;
timeout:每次检测的时间间隔。
(4).4个FD函数:
FD_ZERO(fd_set* a):将a置0;
FD_SET(index, fd_set* a):将a中index位置1;
FD_CLR(index, fd_set* a):将a中index位置0;
FD_ISSET(index, fd_set* a): 判断a中index位是否为1;
(5).代码
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/select.h>
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //0.0.0.0
servaddr.sin_port = htons(2048);
if(-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)))
{
perror("bind failed!");
}
listen(sockfd, 10);
fd_set nfds, rset;
FD_ZERO(&nfds);
FD_SET(sockfd, &nfds);
int maxfd = sockfd;
int clientfd = 0;
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
while(1)
{
rset = nfds;
int nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if(FD_ISSET(sockfd, &rset))
{
clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
FD_SET(clientfd, &nfds);
if(clientfd > maxfd)
maxfd = clientfd;
printf("clientfd: %d\n", clientfd);
}
int i = 0;
for(i = sockfd+1; i <= maxfd; i++)
{
if(FD_ISSET(i, &rset))
{
char buf[128] = {0};
int count = recv(i, buf, 128, 0);
if(count == 0)
{
FD_CLR(i, &nfds);
close(i);
break;
}
printf("clientfd: %d, count: %d, recv: %s\n", i, count, buf);
send(i, buf, count, 0);
}
}
}
getchar();
close(sockfd);
}
(6).总结:
<1>.参数太多;<2>.每次都要将待检测的io拷贝进内核;<3>.fd_set 决定了io数量有限
2.poll
(1).函数:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
(2).返回值:返回连接的io数量, 超时返回0;
(3).fds:存储连接的客户端, nfds最大的io的fd
(4).struct pollfd结构体:包含三个成员,fd、events、revents,fd和events在poll函数签传入fds,revents为返回事件,可根据代码理解。
(5).代码:
#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/select.h>
#include <poll.h>
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(struct sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //0.0.0.0
servaddr.sin_port = htons(2048);
if(-1 == bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr)))
{
perror("bind failed!");
printf("bind failed!");
}
listen(sockfd, 10);
struct sockaddr_in* clientaddr;
socklen_t len = sizeof(clientaddr);
printf("111\n");
struct pollfd fds[1024] = {0};
fds[sockfd].fd = sockfd;
fds[sockfd].events = POLLIN;
int maxfd = sockfd;
int clientfd = 0;
while(1)
{
int nready = poll(fds, maxfd+1, -1);
printf("nready:%d\n", nready);
if(fds[sockfd].revents & POLLIN)
{
clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
fds[clientfd].fd = clientfd;
fds[clientfd].events = POLLIN;
if(maxfd < clientfd)
maxfd = clientfd;
}
int i = 0;
for (i = sockfd+1;i <= maxfd;i ++) {
if (fds[i].revents & POLLIN) {
char buffer[128] = {0};
int ret = recv(i, buffer, 128, 0);
if (ret == 0) {
fds[i].fd = -1;
fds[i].events = 0;
close(i);
break;
}
printf("clientfd: %d, ret: %d, buffer: %s\n",clientfd, ret, buffer);
send(i, buffer, ret, 0);
}
}
}
getchar();
close(sockfd);
}
总结:poll和select并无太多差异,只是在参数上有所改善。
3.epoll
(1).三个API
int epoll_create(1)
创建一个epoll,参数大于0即可,没有实际意义;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
将fd加入到epoll或者从epoll中删除fd;
第一个参数为epfd;op为控制fd的模式,添加或者删除;event为当前fd对应的事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
等待就绪的fd,第一个参数为epfd; 第二个参数为epoll对应的所有events;第三个参数是可供连接的最大fd;最后一个参数为耗时操作,-1就是一直等待。
int epfd = epoll_create(1);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sockfd;
if(epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1)
{
perror("epoll_ctl: listen_sock");
}
struct epoll_event events[1024] = {0};
while(1)
{
int nready = epoll_wait(epfd, events, 1024, -1);
int i = 0;
for(i = 0; i < nready; i++)
{
int connfd = events[i].data.fd;
if(sockfd == connfd)
{
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
if(clientfd<=0)
{
continue;
}
printf(" clientfd: %d\n", clientfd);
//printf(" clientfd: %d\n", clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
}
else if(events[i].events & EPOLLIN)
{
char buf[10] = {0};
short len = 0;
recv(connfd, &len, 2, 0);
len = ntohs(len);
int n = recv(connfd, buf, 10, 0);
if(n == 0)
{
epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);
close(connfd);
}
else
{
send(connfd, buf, n, 0);
printf("recv : %s\n", buf);
}
}
}
}
分享一个学习链接,有需要的同学可以看一下: