参考:epoll server/client以及tcp close wait状态 - 知乎
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IP_ADDRESS "127.0.0.1"
#define LISTENQ 20
static void set_nonblocking(const int sock)
{
int opts;
opts = fcntl(sock,F_GETFL);
if(opts < 0)
{
perror("fcntl(sock,GETFL)");
exit(1);
}
opts = opts|O_NONBLOCK;
if(fcntl(sock, F_SETFL, opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
exit(1);
}
}
static void add_event(int epoll_fd, int fd, int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
}
static void delete_event(int epoll_fd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,&ev);
}
static void modify_event(int epoll_fd,int fd,int state)
{
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epoll_fd,EPOLL_CTL_MOD,fd,&ev);
}
static int bind_socket(const char* ip, int port)
{
int listen_fd;
struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_fd == -1)
{
perror("socket error:");
exit(1);
}
int32_t on = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET,ip,&servaddr.sin_addr);
servaddr.sin_port = htons(port);
if(bind(listen_fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
printf("bind ip:%s port:%d.err:%s\r\n", inet_ntoa(servaddr.sin_addr), ntohs(servaddr.sin_port), strerror(errno));
return listen_fd;
}
static void accept_process(int epoll_fd, int listen_fd)
{
int client_fd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr);
client_fd = accept(listen_fd, (struct sockaddr*)&cliaddr, &cliaddrlen);
if (client_fd == -1)
{
perror("accpet error:");
}
else
{
printf("accept a new client, ip:%s port:%d\n",inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
set_nonblocking(client_fd);
//添加一个客户描述符和事件
add_event(epoll_fd, client_fd, EPOLLIN);
}
}
static void read_socket(int epoll_fd, int fd, char *buf, int buf_size)
{
int nread;
printf("[%s][%d] read fd:%d start\r\n", __func__, __LINE__, fd);
while((nread=read(fd, buf, buf_size-1)) > 0)
{
buf[nread] = '\0';
printf("[%s][%d] read:%s\r\n", __func__, __LINE__, buf);
};
printf("[%s][%d] read fd:%d end\r\n", __func__, __LINE__, fd);
printf("[%s][%d] read len=%d. err:%s\r\n", __func__, __LINE__, nread, strerror(errno));
if(nread == 0)
{ // 对端关闭
printf("[%s][%d] %d closed.\r\n", __func__, __LINE__, fd);
close(fd);
delete_event(epoll_fd, fd, EPOLLIN);
}
}
int main (int argc, char * argv[])
{
int listen_fd;
if (2 != argc)
{
printf("Usage:%s portnumber\r\n", argv[0]);
return 1;
}
listen_fd = bind_socket(IP_ADDRESS, atoi(argv[1]));
listen(listen_fd, LISTENQ);
int epoll_fd;
struct epoll_event events[10];
int nfds, i, fd;
char buf[20];
//创建一个描述符
epoll_fd = epoll_create1(EPOLL_CLOEXEC); // EPOLL_CLOEXEC:这是这个参数唯一的有效值,如果这个参数设置为这个。那么当进程替换映像的时候会关闭这个文件描述符,这样新的映像中就无法对这个文件描述符操作,适用于多进程编程+映像替换的环境里
//添加监听描述符事件
add_event(epoll_fd,listen_fd, EPOLLIN); // 将listen_fd添加到epoll_fd内 EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
for ( ; ; )
{
nfds = epoll_wait(epoll_fd, events, sizeof(events)/sizeof(events[0]), 1000);
for(i=0; i
使用网络调试助手作为tcp client测试