//epoll的四个操作函数:epoll_create,eopll_create1,epoll_ctl,epoll_wait
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define ERR_EXIT(m)do{perror(m);exit(EXIT_FAILURE);}while(0)
typedef std::vector<struct epoll_event> EventList;
void activate_monblock(int fd)//将文件描述符设置成非阻塞模式
{
int ret;
int flags = fcntl(fd,F_GETFL);
if(flags == -1)
ERR_EXIT("fcntl");
flags |= O_NONBLOCK; // 变成阻塞 flags ~= O_NONBLOCK
ret = fcntl(fd,F_SETFL,flags);
if(ret == -1)
ERR_EXIT("fcntl");
}
//MSG_PEEK 只是从缓存区接收到buf,但是未清除掉套接口缓存区数据
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while(1)
{
int ret = recv(sockfd,buf,len,MSG_PEEK);
if(ret == -1 && errno == EINTR)
{
continue;
}
return ret;
}
}
ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;//剩余的字节数
size_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 readline(int sockfd,char *buf,size_t maxline)
{
int ret;
int nread;//接收到的字节数
char * bufp = (char *)buf;
int nleft = maxline;
while(1)
{
ret = recv_peek(sockfd,bufp,nleft);
if(ret <0)
return ret;
else if(ret == 0)
return ret;
nread = ret;
int i;
for(i = 0;i<nread;i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd,bufp,i+1);
if(ret != i+1)
{
exit(EXIT_FAILURE);
}
return ret;
}
}
if(nread > nleft)
{
exit(EXIT_FAILURE);
}
nleft -= nread;
ret = readn(sockfd,bufp,nread);
if(ret != nread)
{
exit(EXIT_FAILURE);
}
bufp += nread; //放在后面
}
return -1;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;//剩余的字节数
size_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;
}
void handle_sigchld(int sig)
{
}
void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n", sig);
}
int main()
{
int count = 0;
signal(SIGPIPE,handle_sigpipe);
signal(SIGCHLD,handle_sigchld);
int listenfd;
if((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0)
{
ERR_EXIT("socket");
}
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int on = 1;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0)
{
ERR_EXIT("setsockopt");
}
if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
ERR_EXIT("bind");
}
if(listen(listenfd,SOMAXCONN)<0)
{
ERR_EXIT("listen");
}
std::vector<int> clients;
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event event;
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;
int conn;
int i;
int nready;
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");
}
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");
printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
clients.push_back(conn);
activate_monblock(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;
char recvbuf[1024] = {0};
int ret = readline(conn,recvbuf,1024);
if(ret == -1)
ERR_EXIT("readline");
//readline返回0的情况,关闭去除这个连接
if(ret == 0)
{
printf("client close\n");
close(conn);
event = events[i];
epoll_ctl(epollfd,EPOLL_CTL_DEL,conn,&event);
clients.erase(std::remove(clients.begin(),clients.end(),conn),clients.end());
}
fputs(recvbuf,stdout);
writen(conn,recvbuf,strlen(recvbuf));
}
}
}
return 0;
}
本文由 Ryan 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2018/10/28 22:30