#include <fcntl.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>
#include <iostream>
#include <pthread.h>
#include <sys/types.h>//包含pthread_self
#include <unistd.h>//包含syscall
#include <sys/syscall.h> #define gettid() syscall(__NR_gettid) typedef std::vector<struct epoll_event> EventList; #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() int main(void)
{
//CP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道,
//但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制,
//一个端点无法获知对端的socket是调用了close还是shutdown.
//对一个已经收到FIN包的socket调用read方法,
//如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送).
//但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以,
//第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.
//为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:
//这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.
signal(SIGPIPE, SIG_IGN);//防止进程退出
//忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧
//因为并发服务器常常fork很多子进程,子进程终结之后需要
//服务器进程去wait清理资源。如果将此信号的处理方式设为
//忽略,可让内核把僵尸子进程转交给init进程去处理,省去了
//大量僵尸进程占用系统资源。(Linux Only)
signal(SIGCHLD, SIG_IGN); int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
int listenfd;
//if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < )
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); //create epoll object
int epollfd;
epollfd = epoll_create1(EPOLL_CLOEXEC); //add EPOLLIN event to epoll
struct epoll_event event;
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event); EventList events();
struct sockaddr_in peeraddr;
socklen_t peerlen;
int connfd; std::vector<int> clients; int nready;
while ()
{
//return active event
nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -);
if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("epoll_wait");
}
if (nready == ) // nothing happended
continue; //double capacity
if ((size_t)nready == events.size())
events.resize(events.size()*); //treat all active event
for (int i = ; i < nready; ++i)
{
//如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理
if (events[i].data.fd == listenfd)
{
peerlen = sizeof(peeraddr);
connfd = ::accept4(listenfd, (struct sockaddr*)&peeraddr,
&peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC); if (connfd == -)
{
//防止文件句柄超过最大数量
if (errno == EMFILE)
{
close(idlefd);
idlefd = accept(listenfd, NULL, NULL);
close(idlefd);
idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
continue;
}
else
ERR_EXIT("accept4");
} std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<
" port="<<ntohs(peeraddr.sin_port)<<std::endl; clients.push_back(connfd); event.data.fd = connfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
}
else if (events[i].events & EPOLLIN)
{
connfd = events[i].data.fd;
if (connfd < )
continue; pid_t pid = fork();
if(pid == )
{
char recvBuf[] = {};
int ret = ; while(true)
{
ret = read(connfd, recvBuf, );// 接受客户端消息
if(ret < )
{
break;// 这里表示读取数据出问题.
//由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可//读在这里就当作是该次事件已处理过。
if(errno == EAGAIN)
{
printf("EAGAIN\n");
break;
}
else
{
printf("read error! errno:%d\n", errno);
ret = ;
break;
}
}
else if(ret == )
{
// 这里表示对端的socket已正常关闭.
std::cout<<"client close"<<std::endl;
close(connfd);
event = events[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
break;
}
else
{
pid_t pid = getpid();
std::cout << pid << " recive data: " << recvBuf;
write(connfd, recvBuf, strlen(recvBuf));
break;
}
}
}
else
{
continue;
}
} }
} return ;
}

服务器四:多进程epoll的更多相关文章

  1. 深入理解NIO(四)—— epoll的实现原理

    深入理解NIO(四)—— epoll的实现原理 本文链接:https://www.cnblogs.com/fatmanhappycode/p/12362423.html 终于来到最后了,万里长征只差最 ...

  2. Linux 高性能服务器编程——多进程编程

    问题聚焦:     进程是Linux操作系统环境的基础.     本篇讨论以下几个内容,同时也是面试经常被问到的一些问题:     1 复制进程映像的fork系统调用和替换进程映像的exec系列系统调 ...

  3. 服务器二:epoll

    #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <sys/soc ...

  4. Vcenter一次性将服务器四个网卡从端口组迁移到分布式交换机的方法

    如果你的服务器已经在清单列表里了,那么可以先从分布式交换机将这台服务器删除,然后再添加一次.这个时候的添加就可以选择四个网卡(包括端口组,包括管理端口组),一次性加入分布式交换机

  5. 从零开始一个http服务器(四)-动态返回

    从零开始一个http服务器(四) 代码地址 : https://github.com/flamedancer/cserver git checkout step4 运行: make clean &am ...

  6. 开源流媒体服务器EasyDarwin支持epoll网络模型,大大提升流媒体服务器网络并发性能

    经过春节前后将近2个月的开发和稳定调试.测试,EasyDarwin开源流媒体服务器终于成功将底层select网络模型修改优化成epoll网络模型,将EasyDarwin流媒体服务器在网络处理的效率上提 ...

  7. why ftp服务器采用多进程模式

    为什么没有采用多线程或者IO复用,原因是在多线程或IO复用的情况下,当前目录是共享的,无法根据每一个连接来拥有自己的当前目录. 多进程模式下,一个连接拥有2个进程,一个是nobody进程,一个是服务进 ...

  8. Linux 服务器IO模型 epoll

    epoll模型 #include <unistd.h> #include <sys/types.h> /* basic system data types */ #includ ...

  9. Linux客户/服务器程序设计范式1——并发服务器(多进程)

    引言 本文会写一个并发服务器(concurrent server)程序,它为每个客户请求fork出一个子进程. 注意 1. 信号处理问题 对于相同信号,按信号的先后顺序依次处理.可能会产生的问题是,正 ...

随机推荐

  1. 我们一起来排序——使用Java语言优雅地实现常用排序算法

    破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...

  2. Java KeyStore 用命令生成keystore文件

    1.生成keyStore文件 在命令行下执行以下命令: Shell代码 收藏代码 keytool -genkey -validity 36000 -alias www.zlex.org -keyalg ...

  3. Python函数的定义、参数传入与函数的调用

    作为计算机代码的一种抽象方式,函数在Python中扮演了极为重要的角色.今天给大家介绍Python函数的定义.参数的传入以及调用方式.其中函数参数的传入方式为本节重点内容.Python函数的参数形式包 ...

  4. 适配器模式(Adapter Pattern)

    适配器模式概述 定义:将一个类的接口转化成客户希望的另一个接口,适配器模式让那些接口不兼容的类可以一起工作.别名(包装器[Wrapper]模式) 它属于创建型模式的成员,何为创建型模式:就是关注如何将 ...

  5. jqgrid three 树形结构

    最近我们的表格中,需要更改数据的显示方式,就是jqgrid中以树形的方式显示.请先看图: 就是简单的这种从属方式.但是因为这个没有找到官方的文档(后来找到了,但是发现是翻译过来的,也不是很清楚),所以 ...

  6. Dynamics 365 CE命令栏按钮点击后刷新表单页面方法

    微软动态CRM专家罗勇 ,回复326或者20190428可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! Dynamics 365 Customer Engagement ...

  7. Android6.0 源码修改之Settings音量调节界面增加通话音量调节

    前言 今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话音量seekBar,很不方便,于是乎需求就来了.需要优化两个地方 1.在正常情 ...

  8. Redis- 简单操作命令

    1.进入. 退出 redis-cli exit 2.五种类型: 字符串: 字符串基本操作: 设置: SET key value 获取: GET key 多重赋值: MSET k1 v1 k2 v2 . ...

  9. 单元测试过多,导致The configured user limit (128) on the number of inotify instances has been reached.

    最近在一个asp.net core web项目中使用TDD的方式开发,结果单元测试超过128个之后,在CI中报错了:"The configured user limit (128) on t ...

  10. 视频直播 object 标签属性详解

    最近在做视频直播这一块的,html5的video不能实现此功能,在网上查找了资料,觉得很有用. 一.介绍: 我们要在网页中正常显示flash内容,那么页面中必须要有指定flash路径的标签.也就是OB ...