Linux epoll服务端
源代码:epollServer.cpp
/*******************************************************************************
* epollServer.cpp
*
* Create on 2018-10-6
* Author: yanxinchun
*
* g++ -o epollServer epollServer.cpp
*******************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <vector>
#include <algorithm>
#include <assert.h>
#define POLL_MAX_CN (1024)
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
typedef std::vector<struct epoll_event> EventList;
typedef struct _packet_st {
int len;
char buf[1024];
} PACKET_st;
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;
while(nleft > 0){
if(nread = read(fd, bufp, nleft) < 0) {
if(errno == EINTR) {
continue;
}
return -1;
}else if(nread == 0) {
return count - nleft;
}
bufp += nread;
nleft -= nread;
}
return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;
while(nleft > 0) {
if(nwritten = write(fd, bufp, nleft) < 0) {
if(errno == EINTR) {
continue;
}
return -1;
}else if(nwritten == 0) {
continue;
}
bufp += nwritten;
nleft -= nwritten;
}
return count;
}
/**
* * activate_nonblock - 设置I/O为非阻塞模式
* * @fd: 文件描述符
* */
void activate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if(flags == -1) {
ERR_EXIT("fcntl fail!");
}
flags |= O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1) {
ERR_EXIT("fcntl fail!");
}
}
/**
* * deactivate_nonblock - 设置I/O为阻塞模式
* * @fd: 文件描述符
* */
void deactivate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if(flags == -1) {
ERR_EXIT("fcntl fail!");
}
flags &= ~O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1) {
ERR_EXIT("fcntl fail!");
}
}
void handle_sigpipe(int sig)
{
printf("[sigpipe] recv a sig=%d\n", sig);
}
void handle_sigchld(int sig)
{
while(waitpid(-1, NULL, WNOHANG) > 0) ;
}
int main()
{
int listenfd = -1;
int epollfd = -1;
int reuse = 0;
struct sockaddr_in servaddr;
std::vector<int> clients;
struct epoll_event event;
//往一个已经关闭的socket写入数据会触发SIGPIPE
signal(SIGPIPE, handle_sigpipe);
/* 在一个进程停止或终止时,将SIGPIPE信号发送给其父进程,按系统默认将忽略此信号,
如果父进程希望被告知其子系统的这种状态,则应捕捉此信号。*/
signal(SIGCHLD, handle_sigchld);
if((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
ERR_EXIT("socket fail!");
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8914);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
reuse = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
ERR_EXIT("setsockopt fail!");
}
if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
ERR_EXIT("bind fail!");
}
if(listen(listenfd, SOMAXCONN) < 0) {
ERR_EXIT("listen fail!");
}
epollfd = epoll_create1(EPOLL_CLOEXEC); // 用于设置改描述符的close-on-exec(FD_CLOEXEC)标志
assert(epollfd != -1); // 成功是返回非负的文件描述符,失败是返回-1
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
EventList events(16);
struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int i;
PACKET_st recvbuf;
int nready = 0;
int conn;
while(1){
nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
if(nready == -1) {
if(errno == EINTR) {
continue;
}
ERR_EXIT("epoll_wait fail!");
}
if(nready == 0) {
continue;
}
if((size_t)nready == events.size()) {
events.resize(events.size() * 2);
}
for(i = 0; i < nready; ++i) {
if(events[i].data.fd == listenfd) {
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if(conn == -1) {
ERR_EXIT("accept fail!");
}
printf("accept ip=%s, port=%d, conn=%d\n",
inet_ntoa(peeraddr.sin_addr),
ntohs(peeraddr.sin_port),
conn);
clients.push_back(conn);
activate_nonblock(conn);
event.data.fd = conn;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event);
} else if(events[i].events & EPOLLIN) {
conn = events[i].data.fd;
if(conn < 0) {
continue;
}
int n;
int ret;
memset(&recvbuf, 0, sizeof(recvbuf));
ret = read(conn, &recvbuf.len, 4);
if(ret == -1) {
ERR_EXIT("ser read fail 11!");
} else if(ret < 4) {
printf("client close, ret = %d\n", ret);
shutdown(conn, SHUT_WR);
event = events[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());
} else {
n = ntohl(recvbuf.len);
ret = read(conn, &recvbuf.buf, n);
if(ret == -1) {
ERR_EXIT("ser read fail 22!");
} else if(ret < n) {
printf("client close 22, ret = %d\n", ret);
shutdown(conn, SHUT_WR);
event = events[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);
clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());
}
printf("recv: len=%d, ctx=%s", ntohl(recvbuf.len), recvbuf.buf);
//fputs(recvbuf.buf, stdout);
printf("reply: len=%d, ctx=%s\n", ntohl(recvbuf.len), recvbuf.buf);
write(conn, &recvbuf, 4 + n);
}
}
}
}
close(listenfd);
return 0;
}
编译运行:
[root@QIANZI-BASE keke]# g++ -o epollServer epollServer.cpp