Introduction
An example to learn how to use epoll. In this post, the client fd is set non blocking before calling connect function and EPOLLOUT is monitored. If the connection is successfully, EPOLLOUT event happens. If the connection is failed (e.g. the server is not running), the value of events[0].events (tcp_client.cc, line 71) is 29. EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP=29.
Code
tcp_client.cc
#include <iostream>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h> //for sockaddr_in
#include <arpa/inet.h> //inet_addr
#include "tcp_poll.h"
enum ConnStatus{
CONN_MIN,
CONN_CONNECTING,
CONN_CONNECTED,
CONN_CLOSED,
};
using namespace tcp;
using namespace std;
const int kBufSize=1500;
const int kMaxEvents=64;
static volatile bool g_running=true;
void signal_exit_handler(int sig)
{
g_running=false;
}
int main(int argc, char *argv[]){
signal(SIGTERM, signal_exit_handler);
signal(SIGINT, signal_exit_handler);
signal(SIGHUP, signal_exit_handler);//ctrl+c
signal(SIGTSTP, signal_exit_handler);//ctrl+z
int sock=-1;
int nfds=0;
struct epoll_event events[kMaxEvents];
char wbuf[kBufSize]={0};
char rbuf[kBufSize]={0};
int send_bytes=0;
int recv_bytes=0;
int total=10;
int count=0;
ConnStatus status=CONN_CONNECTING;
int epfd = epoll_create1(0);
const char *serv_ip="10.0.2.2";
const int serv_port=2333;
if(-1==epfd){
return -1;
}
if ((sock= socket(AF_INET, SOCK_STREAM, 0)) < 0){
std::cout<<"Error : Could not create socket"<<std::endl;
return -1;
}
setnonblocking(sock);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serv_ip);
servaddr.sin_port = htons(serv_port);
std::cout<<EPOLLIN<<" "<<EPOLLOUT<<" "<<EPOLLERR<<" "<<EPOLLHUP<<std::endl;
if(connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr)) == -1&& errno != EINPROGRESS){
//connect doesn't work, are we running out of available ports ? if yes, destruct the socket
if (errno == EAGAIN){
close(sock);
sock=-1;
close(epfd);
return -1;
}
}
epoll_ctl_add(epfd,sock,/*EPOLLET|*/EPOLLIN|EPOLLOUT);
//only one sock
while(g_running){
nfds= epoll_wait(epfd, events, kMaxEvents,0);
if(nfds>0){
if(events[0].events & (EPOLLERR|EPOLLHUP)){
if(CONN_CONNECTING==status){
status=CONN_CLOSED;
std::cout<<LOC<<" "<<events[0].events<<std::endl;
}
break;
}
if(CONN_CONNECTED==status&&events[0].events&EPOLLIN){
int n=read(sock,rbuf,kBufSize);
if(-1==n){
if(EINTR==errno||EWOULDBLOCK==errno||EAGAIN==errno){
std::cout<<LOC<<strerror(errno)<<std::endl;
break;
}else{
std::cout<<LOC<<sock<<std::endl;
break;
}
}
if(0==n){
std::cout<<LOC<<sock<<std::endl;
break;
}
if(n>0){
recv_bytes+=n;
if(count>=total){
if(recv_bytes==send_bytes){
break;
}
}
}
}
if(sock>0&&events[0].events&EPOLLOUT){
if(CONN_CONNECTING==status){
status=CONN_CONNECTED;
}
if(CONN_CONNECTED==status&&count<total){
int w=send(sock,wbuf,kBufSize,0);
if(w>0){
std::cout<<"send "<<w<<std::endl;
send_bytes+=w;
count++;
}
}
}
}
}
if(sock>0){
epoll_ctl_del(epfd,sock);
close(sock);
sock=-1;
}
close(epfd);
std::cout<<send_bytes<<" "<<recv_bytes<<std::endl;
return 0;
}
tcp_server.cc
#include <iostream>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include "tcp_poll.h"
#include <set>
const int kBufSize=1500;
const int kMaxEvents=64;
const char *bind_ip=(const char*)"0.0.0.0";
const uint16_t bind_port=2333;
const int kMaxBacklog=128;
using namespace tcp;
using namespace std;
static volatile bool g_running=true;
void signal_exit_handler(int sig)
{
g_running=false;
}
int main(int argc, char *argv[]){
signal(SIGTERM, signal_exit_handler);
signal(SIGINT, signal_exit_handler);
signal(SIGHUP, signal_exit_handler);//ctrl+c
signal(SIGTSTP, signal_exit_handler);//ctrl+z
int sock=-1;
int nfds=0;
struct epoll_event events[kMaxEvents];
int buffer[kBufSize]={0};
std::set<int> fd_db;
int listen_fd=create_listen_fd(bind_ip,bind_port,kMaxBacklog);
std::cout<<"listen_fd = "<<listen_fd<<std::endl;
if(listen_fd<0){
return -1;
}
int epfd = epoll_create1(0);
if(-1==epfd){
return -1;
}
setnonblocking(listen_fd);
epoll_ctl_add(epfd,listen_fd,EPOLLET|EPOLLIN|EPOLLOUT);
while(g_running){
nfds= epoll_wait(epfd, events, kMaxEvents,0);
for(int i=0;i<nfds;i++){
int fd=events[i].data.fd;
if(events[i].events & (EPOLLERR|EPOLLHUP)){
if(fd!=listen_fd){
std::cout<<LOC<<" "<<fd<<std::endl;
fd_db.erase(fd);
epoll_ctl_del(epfd,fd);
close(fd);
continue;
}
}
if(events[i].events &EPOLLIN){
if (listen_fd==fd){
int sock=-1;
sockaddr_storage addr_storage;
socklen_t addr_len = sizeof(addr_storage);
while((sock=accept(listen_fd,(sockaddr*)&addr_storage,&addr_len))>=0){
setnonblocking(sock);
fd_db.insert(sock);
epoll_ctl_add(epfd,sock,EPOLLET|EPOLLIN);
}
}else{
while(true){
int n=read(fd,buffer,kBufSize);
if(-1==n){
if(EINTR==errno||EWOULDBLOCK==errno||EAGAIN==errno){
//std::cout<<LOC<<" "<<strerror(errno)<<std::endl;
break;
}else{
std::cout<<LOC<<" "<<fd<<std::endl;
fd_db.erase(fd);
epoll_ctl_del(epfd,fd);
close(fd);
break;
}
}
if(0==n){
std::cout<<LOC<<" "<<fd<<std::endl;
fd_db.erase(fd);
epoll_ctl_del(epfd,fd);
close(fd);
break;
}
if(n>0){
send(fd,buffer,n,0);
}
}
}
}
}
}
for(auto it=fd_db.begin();it!=fd_db.end();it++){
int fd=(*it);
epoll_ctl_del(epfd,fd);
close(fd);
}
fd_db.clear();
close(listen_fd);
close(epfd);
return 0;
}
tcp_poll.h
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
namespace tcp{
#define LOC __FILE__<<__LINE__
inline uint16_t HostToNet16(uint16_t x) { return __builtin_bswap16(x); }
inline uint32_t HostToNet32(uint32_t x) { return __builtin_bswap32(x); }
inline uint64_t HostToNet64(uint64_t x) { return __builtin_bswap64(x); }
inline uint16_t NetToHost16(uint16_t x) { return HostToNet16(x); }
inline uint32_t NetToHost32(uint32_t x) { return HostToNet32(x); }
inline uint64_t NetToHost64(uint64_t x) { return HostToNet64(x); }
int setnonblocking(int fd);
void epoll_ctl_add(int epfd, int fd,uint32_t events);
void epoll_ctl_mod(int epfd, int fd,uint32_t events);
void epoll_ctl_del(int epfd, int fd);
int create_listen_fd(const char *ip,uint16_t port,int backlog);
}
tcp_poll.cc
#include <iostream>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h> //for sockaddr_in
#include <arpa/inet.h> //inet_addr
#include <netdb.h>
#include "tcp_poll.h"
namespace tcp{
int setnonblocking(int fd)
{
if (-1==fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK)){
return -1;
}
return 0;
}
void epoll_ctl_add(int epfd, int fd,uint32_t events)
{
struct epoll_event ev;
ev.events = events|EPOLLERR|EPOLLHUP;
ev.data.fd=fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)==-1) {
std::cout<<LOC<<strerror(errno)<<std::endl;
exit(1);
}
}
void epoll_ctl_mod(int epfd, int fd,uint32_t events)
{
struct epoll_event ev;
ev.events = events|EPOLLERR|EPOLLHUP;
ev.data.fd=fd;
if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev)==-1) {
std::cout<<LOC<<strerror(errno)<<std::endl;
exit(1);
}
}
void epoll_ctl_del(int epfd, int fd){
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev)==-1) {
std::cout<<"epoll_ctl_del: "<<strerror(errno)<<std::endl;
exit(1);
}
}
int create_listen_fd(const char *ip,uint16_t port,int backlog){
int fd=-1;
int yes=1;
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr =inet_addr(ip);
servaddr.sin_port = htons(port);
size_t addr_size = sizeof(struct sockaddr_in);
fd=socket(AF_INET, SOCK_STREAM, 0);
if(fd<0){
return fd;
}
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))!=0){
close(fd);
fd=-1;
return fd;
}
if(setsockopt(fd,SOL_SOCKET, SO_REUSEPORT,(const void *)&yes ,sizeof(int))!=0){
close(fd);
fd=-1;
return fd;
}
if(bind(fd, (struct sockaddr *)&servaddr, addr_size)<0){
std::cout<<LOC<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
if(-1==listen(fd,backlog)){
std::cout<<LOC<<strerror(errno)<<std::endl;
close(fd);
fd=-1;
return fd;
}
return fd;
}
}
CmakeLists.txt
PROJECT(project)
cmake_minimum_required(VERSION 2.6)
SET(CMAKE_BUILD_TYPE "Debug")
add_definitions(-std=c++14)
add_definitions(-DNDEBUG)
include_directories(${CMAKE_SOURCE_DIR}/)
set(poll_src
tcp_poll.cc
)
add_library(poll STATIC ${poll_src})
set(EXECUTABLE_NAME "tcp_server")
add_executable(${EXECUTABLE_NAME} ${CMAKE_SOURCE_DIR}/tcp_server.cc)
target_link_libraries(${EXECUTABLE_NAME} poll)
set(EXECUTABLE_NAME "tcp_client")
add_executable(${EXECUTABLE_NAME} ${CMAKE_SOURCE_DIR}/tcp_client.cc)
target_link_libraries(${EXECUTABLE_NAME} poll)
网友评论