为了优化 <一个Linux下的TCP服务1> 的效率, 本篇用epoll多路IO复用技术, 并设置ET模式, 文件名为epoll.c.
该文件不仅包含了服务端代码, 还包含客户端代码, 这可以通过编译参数COMPILE_SERVER来控制. 同时可以指定客户端数量, 具体代码如下:
// epoll.c
#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int setNonblock(int fd)
{
int flags = 0;
if ((flags = fcntl(fd, F_GETFL)) == -1)
{
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1)
{
return -1;
}
return 0;
}
int addFD(int efd, int fd, int mode)
{
struct epoll_event event;
event.events = mode;
event.data.fd = fd;
return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event);
}
int epollETAdd(int efd, int fd, int mode)
{
return setNonblock(fd) | addFD(efd, fd, mode);
}
void epollETDel(int efd, int fd)
{
close(fd);
epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL);
}
void epollETModifyMode(int efd, int fd, int mode)
{
struct epoll_event event;
event.events = mode;
event.data.fd = fd;
epoll_ctl(efd, EPOLL_CTL_MOD, fd, &event);
}
void connFunc(int efd, int fd)
{
int connFD = accept(fd, NULL, NULL);
if (connFD == -1)
{
return;
}
epollETAdd(efd, connFD, EPOLLIN | EPOLLET);
}
void readFunc(int efd, int fd)
{
char buf[32];
memset(buf, 0x00, sizeof(buf));
int readLen = read(fd, buf, sizeof(buf));
if (readLen == -1)
{
if (errno == EAGAIN)
{}
else
{
printf("Something wrong with read.\n");
epollETDel(efd, fd);
}
return;
}
else if (readLen == 0)
{
printf("Remote unregister.\n");
epollETDel(efd, fd);
return;
}
#ifndef COMPILE_SERVER
printf("Client recv msg: %s\n", buf);
#else
printf("Server recv msg: %s\n", buf);
#endif
epollETModifyMode(efd, fd, EPOLLOUT | EPOLLET);
}
void writeFunc(int efd, int fd)
{
char *buf;
#ifndef COMPILE_SERVER
buf = "ping";
printf("Client write msg: %s\n", buf);
#else
buf = "pong";
printf("Server write msg: %s\n", buf);
#endif
write(fd, buf, strlen(buf));
epollETModifyMode(efd, fd, EPOLLIN | EPOLLET);
}
void errFunc(int efd, int fd)
{
printf("Something wrong with epoll_wait.\n");
epollETDel(efd, fd);
}
int binding(char *ip, int port, int sfd)
{
struct sockaddr_in addr;
memset(&addr, 0x00, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
return bind(sfd, (struct sockaddr*)(void*)(&addr), sizeof(struct sockaddr_in));
}
int connecting(char *ip, int port, int sfd)
{
struct sockaddr_in addr;
memset(&addr, 0x00, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
return connect(sfd, (struct sockaddr*)(void*)(&addr), sizeof(struct sockaddr_in));
}
int socketing(char *ip, int port)
{
int sfd = 0, val = 0;
if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("socket, errMsg: %s.\n", strerror(errno));
return -1;
}
#ifndef COMPILE_SERVER
if (connecting(ip, port, sfd) == -1)
{
printf("connecting failed.\n");
goto FAIELD;
}
#else
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&val, sizeof(int)) == -1)
{
printf("set SO_REUSEADDR failed, errMsg: %s.\n", strerror(errno));
goto FAIELD;
}
if (binding(ip, port, sfd) == -1)
{
printf("bind failed, errMsg: %s.\n", strerror(errno));
goto FAIELD;
}
if (listen(sfd, 512) == -1)
{
printf("listen failed, errMsg: %s.\n", strerror(errno));
goto FAIELD;
}
#endif
return sfd;
FAIELD:
close(sfd);
return -1;
}
int Server(char *ip, int port)
{
int epollFD = 0, sockFD = 0;
struct epoll_event activeEvents[1024];
if ((epollFD = epoll_create(1024)) == -1)
{
return -1;
}
if ((sockFD = socketing(ip, port)) == -1)
{
close(epollFD);
return -1;
}
if (epollETAdd(epollFD, sockFD, EPOLLIN | EPOLLET) == -1)
{
close(epollFD);
close(sockFD);
return -1;
}
printf("Epoll server start success.\n");
for(;;)
{
int count = epoll_wait(epollFD, activeEvents, 1024, -1);
if (count == -1)
{
printf("epoll_wait failed, errMsg: %s.\n", strerror(errno));
continue;
}
for (int index = 0; index < count; ++index)
{
int tmpFD = activeEvents[index].data.fd;
if (tmpFD == sockFD)
{
connFunc(epollFD, tmpFD);
}
else if(activeEvents[index].events & EPOLLIN)
{
readFunc(epollFD, tmpFD);
}
else if (activeEvents[index].events & EPOLLOUT)
{
writeFunc(epollFD, tmpFD);
}
else
{
errFunc(epollFD, tmpFD);
}
}
}
close(epollFD);
close(sockFD);
return 0;
}
int Client(char *ip, int port, int nums)
{
int epollFD = 0, sockFD = 0;
struct epoll_event activeEvents[1024];
if ((epollFD = epoll_create(1024)) == -1)
{
return -1;
}
for (int i = 0; i < nums; ++i)
{
if ((sockFD = socketing(ip, port)) == -1)
{
close(epollFD);
return -1;
}
if (epollETAdd(epollFD, sockFD, EPOLLOUT | EPOLLET) == -1)
{
close(epollFD);
close(sockFD);
return -1;
}
}
printf("Epoll client start success.\n");
for(;;)
{
int count = epoll_wait(epollFD, activeEvents, 1024, -1);
if (count == -1)
{
printf("epoll_wait failed, errMsg: %s.\n", strerror(errno));
continue;
}
for (int index = 0; index < count; ++index)
{
int tmpFD = activeEvents[index].data.fd;
if(activeEvents[index].events & EPOLLIN)
{
readFunc(epollFD, tmpFD);
}
else if (activeEvents[index].events & EPOLLOUT)
{
writeFunc(epollFD, tmpFD);
}
else
{
errFunc(epollFD, tmpFD);
}
}
}
close(epollFD);
close(sockFD);
return 0;
}
int main(int argc, char *argv[])
{
#ifndef COMPILE_SERVER
if (argc != 4)
{
printf("argc != 4; [client: ./a.out ip port nums]\n");
return -1;
}
Client(argv[1], atoi(argv[2]), atoi(argv[3]));
#else
if (argc != 3)
{
printf("argc != 3; [server: ./a.out ip port]\n");
return -1;
}
Server(argv[1], atoi(argv[2]));
#endif
return 0;
}
先编译epoll_server, 编译并启动, 命令如下:
gcc epoll.c -o epoll_server -DCOMPILE_SERVER -std=c99
./epoll_server 127.0.0.1 6688
再编译epoll_client, 编译并启动, 命令如下:
gcc epoll.c -o epoll_client -std=c99
./epoll_client 127.0.0.1 6688 1024
因为是在同一台机器, 所有有2048个ESTABLISHED, 结果如下, :