#include <cerrno>
#include <cstdlib>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/socket.h>
#include <cstdio>
#include <fcntl.h>
#include <netinet/in.h>
#include <csignal>
#include <cstring>
int main()
{
//创建监听套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd == -1)
{
perror("socket");
exit(EXIT_FAILURE);
}
//设置端口复用
int opt = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))==-1)
{
perror("setsockopt");
close(listenfd);
return EXIT_FAILURE;
}
//将监听套接字设置为非阻塞
int flag = fcntl(listenfd,F_GETFL,0);
if (flag == -1) {
perror("fcntl F_GETFL");
close(listenfd);
return EXIT_FAILURE;
}
if (fcntl(listenfd, F_SETFL, flag | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL");
close(listenfd);
return EXIT_FAILURE;
}
//绑定端口
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(12332);
if (bind(listenfd, reinterpret_cast<sockaddr*>(&serverAddr), sizeof(serverAddr)) == -1) {
perror("bind");
close(listenfd);
return EXIT_FAILURE;
}
//开始监听
if (listen(listenfd, SOMAXCONN) == -1) {
perror("listen");
close(listenfd);
return EXIT_FAILURE;
}
//创建epoll实例
int epollfd = epoll_create1(0);
if(epollfd == -1)
{
perror("epoll_create");
return EXIT_FAILURE;
}
//将监听描述服放到epoll树上
epoll_event event;
event.events = EPOLLIN|EPOLLET;
event.data.fd = listenfd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) == -1) {
perror("epoll_ctl listenfd");
close(listenfd);
close(epollfd);
return EXIT_FAILURE;
}
//创建事件数组
epoll_event events[1024];
int count = 0;
while(true)
{
count = epoll_wait(epollfd,events,1024,-1);
for(int i = 0; i < count; ++i)
{
if(events[i].data.fd == listenfd)
{
//建立连接
sockaddr_in clientAddr{};
socklen_t clientsize = sizeof(clientAddr);
int clientfd = accept(listenfd,reinterpret_cast<sockaddr*>(&clientAddr),&clientsize);
if (clientfd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
perror("accept");
continue;
}
//将客户端的文件描述服设置为非阻塞
int clientFlags = fcntl(clientfd, F_GETFL,0);
if (clientFlags == -1) {
perror("fcntl F_GETFL");
close(listenfd);
continue;
}
if (fcntl(clientfd, F_SETFL, clientFlags | O_NONBLOCK) == -1) {
perror("fcntl client");
close(clientfd);
continue;
}
//将监听描述服上树
epoll_event clientEvent{};
clientEvent.events = EPOLLIN|EPOLLET|EPOLLRDHUP;
clientEvent.data.fd = clientfd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &clientEvent) == -1) {
perror("epoll_ctl client");
close(clientfd);
}
}
else{
int clientfd = events[i].data.fd;
//EPOLLERR 和 EPOLLHUP 事件如果发生,总是会被报告,无论这些事件是否在 events 中被指定
if(events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
printf("Client %d disconnected abnormally\n", clientfd);
epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, nullptr);
close(clientfd);
continue;
}
// 循环读取全部数据(边缘触发模式必须)
while(1) {
char buffer[1024];
ssize_t num_bytes = recv(clientfd, buffer, sizeof(buffer), 0);
if(num_bytes > 0) {
buffer[num_bytes] = '\0';
printf("Received %zd bytes from client %d: %s\n",
num_bytes, clientfd, buffer);
}
else if(num_bytes == 0) {
printf("Connection closed by client: %d\n", clientfd);
epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, nullptr);
close(clientfd);
break;
}
else { // num_bytes == -1
if(errno == EAGAIN || errno == EWOULDBLOCK) {
// 数据读取完毕
printf("EAGAIN/EWOULDBLOCK encountered for client %d - no more data to read\n", clientfd);
break;
}
perror("recv");
epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, nullptr);
close(clientfd);
break;
}
}
}
}
}
close(listenfd);
close(epollfd);
return 0;
}
测试:
方式1:
nc localhost 12332
方式2:
telnet localhost 12332