前言
所有的服务器都是高并发的,可以同时为成千上万个客户端提供服务,这一技术又被称为IO复用。
提示:以下是本篇文章正文内容,下面案例可供参考
一、epoll是什么?
IO复用的基本思想是事件驱动,服务器同时保持多个客户端IO连接,当这个IO上有可读或可写事件发生时,表示这个IO对应的客户端在请求服务器的某项服务,此时服务器响应该服务。在Linux系统中,IO复用使用select, poll和epoll来实现。epoll改进了前两者,更加高效、性能更好,是目前几乎所有高并发服务器的基石。
epoll介绍和原理
二、代码详情
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include<sys/select.h>
#include<sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
using namespace std;
#define MAX_EVENTS 1024
#define READ_BUFFER 1024
int setnonblocking(int fd)
{
fcntl(fd,F_SETFL,fcntl(fd,F_GETFL) | O_NONBLOCK);
}
int main()
{
int sockfd;//服务器端套接字
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//创建服务器端套接字
sockaddr_in serveraddr = {};服务器网络地址结构体
serveraddr.sin_family = AF_INET;//设置为IP通信
serveraddr.sin_port =htons(1234);//服务器端口号
serveraddr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址
//serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器IP地址
/*将套接字绑定到服务器的网络地址上*/
bind(sockfd,(sockaddr*)&serveraddr,sizeof(serveraddr));
/*监听连接请求*/
listen(sockfd,SOMAXCONN);
socklen_t clnt_addr_len = sizeof(serveraddr);
int epfd = epoll_create1(0);//创建一个epoll对象epfd
struct epoll_event events[MAX_EVENTS], ev;
bzero(&events,sizeof(events));
bzero(&ev,sizeof(ev));
ev.events = EPOLLIN; //在代码中使用了ET模式,且未处理错误,在day12进行了修复,实际上接受连接最好不要用ET模式
ev.data.fd = sockfd; //该IO口为服务器socket fd
epoll_ctl(epfd,EPOLL_CTL_ADD, sockfd, &ev); //将服务器socket fd添加到epoll
while(true){ // 不断监听epoll上的事件并处理
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); //有nfds个fd发生事件
for(int i = 0; i < nfds; ++i){ //处理这nfds个事件
if(events[i].data.fd == sockfd){ //发生事件的fd是服务器socket fd,表示有新客户端连接
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
socklen_t naddrlen;
naddrlen = sizeof(clientaddr);
int clnt_sockfd = accept(sockfd, (sockaddr*)&clientaddr, &naddrlen);
bzero(&ev,sizeof(ev));
ev.data.fd = clnt_sockfd;
ev.events = EPOLLIN | EPOLLET; //对于客户端连接,使用ET模式,可以让epoll更加高效,支持更多并发
setnonblocking(clnt_sockfd); //ET需要搭配非阻塞式socket使用
epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sockfd, &ev); //将该客户端的socket fd添加到epoll
} else if(events[i].events & EPOLLIN){ //发生事件的是客户端,并且是可读事件(EPOLLIN)
char buf[READ_BUFFER];
while (1)
{
memset(buf,0,READ_BUFFER);
ssize_t byte_red = read(events[i].data.fd,buf,sizeof(buf));
if (byte_red > 0)
{
printf("message client fd %d:%s\n",events[i].data.fd,buf);
write(events[i].data.fd,buf,sizeof(buf));
}
else if (byte_red == -1 && errno == EINTR) //正常中断
{
printf("continue \n");
continue;
}
else if (byte_red == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))//非阻塞io,条件表示数据全部读取完毕
{
break;
}
else if (byte_red == 0)//断开连接
{
printf("disconnected client fd %d\n",events[i].data.fd);
close(events[i].data.fd);
break;
}
}
}
}
}
close(sockfd);
return 0;
}
总结
使用epoll实现高并发的TCP服务器。