select:
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
//nfds:文件描述符个数,最大文件描述符加1, readfds、writefds、exceptfds
//分别为要监听的读、写、异常事件。
//timeout:在时间内监听事件
//返回值:表示监听到的发生变化的描述符的个数
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
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);
#include <sys/select.h>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int main(int argc, const char **argv)
{
if (argc < 3)
{
perror("fail : ./a.out ip port");
exit(1);
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server;
bzero(&server, sizeof(server));
//bind
server.sin_family = AF_INET;
server.sin_port = htons(port);
bind(lfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
//listen
int listenLen = 12;
listen(lfd, listenLen);
//select
int maxfd = lfd;
fd_set oldReadFd, newReadFd;
FD_ZERO(&oldReadFd);
FD_ZERO(&newReadFd);
FD_SET(lfd, &oldReadFd);
while(1){
newReadFd = oldReadFd;
int n = select(maxfd+1, &newReadFd, nullptr, nullptr, nullptr);
if(FD_ISSET(lfd, &newReadFd)){
struct sockaddr_in clientAddr;
socklen_t socklen = sizeof(clientAddr);
int newFd = accept(lfd, (struct sockaddr *)&clientAddr, &socklen);
char clientIp[16] = "";
inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, clientIp, sizeof(clientIp));
std::cout << "new connection ip=" << clientIp << " port=" << ntohs(clientAddr.sin_port) << std::endl;
FD_SET(newFd, &oldReadFd);
if(newFd > maxfd){
maxfd = newFd;
}
if(n == 1){
continue ;
}
}
for(size_t i = lfd+1; i <= maxfd; ++i){
if(FD_ISSET(i, &newReadFd)){
char buf[1500] = "";
int ret = read(i, buf, sizeof(buf));
if(ret < 0){
perror("read fail");
close(i);
FD_CLR(i, &oldReadFd);
}else if(ret == 0){
std::cout << "close" << std::endl;
close(i);
FD_CLR(i, &oldReadFd);
}else{
std::cout << "message: " << buf << std::endl;
write(i, buf, sizeof(buf));
}
}
}
}
return 0;
}
poll:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <poll.h>
#include <unistd.h>
#define MAX_OPEN 128
int main(int argc, const char **argv)
{
if(argc != 3){
perror("input like this: ./a.out ip port");
exit(0);
}
const char *ip = argv[1];
short port = atoi(argv[2]);
int lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
bzero(&serverAddr, sizeof(serverAddr));
inet_pton(AF_INET, ip, &serverAddr.sin_addr.s_addr);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = ntohs(port);
//bind
bind(lfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
//listen
int listenLen = 128;
listen(lfd, listenLen);
struct pollfd clientFds[MAX_OPEN];
clientFds[0].fd = lfd;
clientFds[0].events = POLLIN;
for(size_t i = 1; i < MAX_OPEN; ++i){
clientFds[i].fd = -1;
}
int maxi = 0;
struct sockaddr_in client;
socklen_t socklen = sizeof(client);
while(true){
int nready = poll(clientFds, maxi+1, -1);
if(clientFds[0].revents & POLLIN){
int newFd = accept(clientFds[0].fd, (struct sockaddr *)&client, &socklen);
char ip[16] = "";
short port = ntohs(client.sin_port);
inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip));
std::cout << "new connection: ip=" << ip << " port=" << port << std::endl;
size_t i;
for(i = 1; i < MAX_OPEN; ++i){
if(clientFds[i].fd == -1){
clientFds[i].fd = newFd;
clientFds[i].events = POLLIN;
break ;
}
}
//can't handle it
if(i == MAX_OPEN){
perror("max connection number");
exit(0);
}
if(maxi < i){
maxi = i;
}
if(--nready == 0){
continue ;
}
}
for(size_t i = 1; i <= maxi; ++i){
if(clientFds[i].fd == -1){
continue ;
}
if(clientFds[i].revents & POLLIN){
char buf[1500] = "";
int ret = read(clientFds[i].fd, buf, sizeof(buf));
if(ret < 0){
perror("fail to recv");
exit(0);
}else if(ret == 0){
close(clientFds[i].fd);
clientFds[i].fd = -1;
std::cout << "close" << std::endl;
}else{
std::cout << "message: " << buf << std::endl;
write(clientFds[i].fd, buf, sizeof(buf));
if(--nready == 0){
break ;
}
}
}
}
}
return 0;
}
epoll:
int epoll_create(int size); //size表示要监听的文件描述符的上限个数,自从2.6以后超过size会//自动扩容,往往填1。返回值:返回epoll的句柄,也就是epoll的文件描述符。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//在epfd指向的epoll中,对fd,event事件进行op操作,返回值:成功0,失败-1
//op:EPOLL_CTL_ADD、EPOLL_CTL_ADD、EPOLL_CTL_ADD
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */ //EPOLLIN、EPOLLIN等等
epoll_data_t data; /* User data variable */
};
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
//events, maxevents作为传出参数,返回值:监听到的文件描述符个数。
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/epoll.h>
#include <poll.h>
#include <unistd.h>
#define MAXSIZE 128
int main(int argc, const char *argv[])
{
if (argc != 3)
{
perror("input like this : ./a.out ip port");
exit(0);
}
const char *ip = argv[1];
short port = atoi(argv[2]);
int lfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr;
bzero(&serverAddr, sizeof(serverAddr));
inet_pton(AF_INET, ip, &serverAddr.sin_addr.s_addr);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
bind(lfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
listen(lfd, 128);
int epfd = epoll_create(1);
struct epoll_event lfdEvent;
lfdEvent.data.fd = lfd;
lfdEvent.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &lfdEvent);
struct epoll_event clientEpollEvent[MAXSIZE];
while (1)
{
int ret = epoll_wait(epfd, clientEpollEvent, MAXSIZE, -1);
if (ret < 0)
{
perror("epoll_wait wrong");
exit(1);
}
else if (ret == 0)
{
continue;
}
else
{
for (size_t i = 0; i < ret; ++i)
{
if (clientEpollEvent[i].data.fd == lfd && clientEpollEvent[i].events & EPOLLIN)
{
struct sockaddr_in client;
socklen_t clientLen;
int newFd = accept(lfd, (struct sockaddr *)&client, &clientLen);
struct epoll_event newEvent;
newEvent.data.fd = newFd;
newEvent.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, newFd, &newEvent);
std::cout << "new connection " << newFd << std::endl;
}
else if(clientEpollEvent[i].events & EPOLLIN)
{
int fd = clientEpollEvent[i].data.fd;
char buf[1024] = "";
int retNumber = read(fd, buf, sizeof(buf));
if (retNumber < 0)
{
perror("fail to read");
exit(0);
}
else if (retNumber == 0)
{
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &clientEpollEvent[i]);
std::cout << "close " << fd << std::endl;
}
else
{
std::cout << "message: " << buf << std::endl;
}
}
}
}
}
return 0;
}
1.LT水平触发(默认):当缓存区的数据没有被一次性读取完,那么epoll_wait()函数会非阻塞的进行再次读取,直至读写缓存区的数据被读取完成。
2.ET边沿触发:每当进行一次读取操作后,epoll_wait()函数就会堵塞,直至下一次缓存区数据的写入,才会在此的触发读取操作。
区别:对于LT模式,保证了对数据的完整性读取,但是对一些不重要的不必要的数据来说,会加大内核对epoll_wait()函数的调用,加大开销。对于ET模式,可以在程序中设定读取的关键信息段,从而来减轻内核的开销,效率而言相比LT来说较高。但是值得注意的是读取缓存区需要及时将多余的信息清理掉,保证下一次读取的可靠性 ---需要1.配合while循环读取缓冲区。2.用fcntl设置非阻塞式读取缓冲区。3.用error的EAGAIN判断是否把数据读干净。4.注意跳出while循环。
epoll反应堆:epoll原理详解及epoll反应堆模型_~青萍之末~的博客-CSDN博客_epoll
//感觉是通过自定义结构体来在挂、删除红黑树上的节点。同时能通过回调函数进行特定的操作。