#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() void* handleMessage(void* para)
{
char recvBuf[] = {};
int ret = ;
int socketfd = *(int *)para; while(true)
{
ret = read(socketfd, 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已正常关闭.
break;
}
else
{
pthread_t tid1 = pthread_self();//用户态ID, 与pthread_create创建出来的相同
long int tid2 = gettid();//内核态ID
std::cout << tid1 << " recive data: " << recvBuf;
std::cout << tid2 << " recive data: " << recvBuf << std::endl;
write(socketfd, recvBuf, strlen(recvBuf));
break;
}
}
if(ret == )
return para;
return NULL;
} int main(void)
{
//TCP是全双工的信道, 可以看作两条单工信道, 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; pthread_attr_t attr;
pthread_t threadId; /*初始化属性值,均设为默认值, 把线程设置为系统级线程*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
/* 设置线程为分离属性*/
//pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if(pthread_create(&threadId, &attr, handleMessage, (void*)&(events[i].data.fd)))
{
perror("pthread_creat error!");
exit(-);
}
std::cout << "Create thread: " << threadId << std::endl; void* rmpfd = NULL;
pthread_join(threadId, &rmpfd); if(rmpfd != NULL)
{
int rmfd = *(int*)rmpfd;
std::cout<<"client close"<<std::endl;
close(rmfd);
event = events[i];
epoll_ctl(epollfd, EPOLL_CTL_DEL, rmfd, &event);
clients.erase(std::remove(clients.begin(), clients.end(), rmfd), clients.end());
continue;
}
} }
} return ;
}

服务器三:多线程epoll的更多相关文章

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...

  2. trinitycore 魔兽服务器源码分析(三) 多线程相关

    先看LockedQueue.h template <class T, typename StorageType = std::deque<T> >class LockedQue ...

  3. Linux 高性能服务器编程——多线程编程

    问题聚焦:     在简单地介绍线程的基本知识之后,主要讨论三个方面的内容:    1 创建线程和结束线程:    2 读取和设置线程属性:    3 线程同步方式:POSIX信号量,互斥锁和条件变量 ...

  4. Centos搭建SVN服务器三步曲

    搭建SVN服务,有效的管理代码,以下三步可以快速搞定.1.安装 #yum install subversion 判断是否安装成功#subversion -v svnserve, version 1.6 ...

  5. 服务器 libevent中epoll使用实例demo

    名词解释:man epoll之后,得到如下结果: NAME       epoll - I/O event notification facility SYNOPSIS       #include ...

  6. Java多线程(三) 多线程间的基本通信

    多条线程在操作同一份数据的时候,一般需要程序去控制好变量.在多条线程同时运行的前提下控制变量,涉及到线程通信及变量保护等. 本博文主要总结:①线程是如何通信  ②如何保护线程变量 1.Java里的线程 ...

  7. Nginx源码研究三:Epoll在NGINX中的使用

    Web服务器在面对高并发的情况下,网络的IO一般选择IO复用,像apache选择的Select/poll.Nginx在linux 2.6后选择Epoll做网路IO,提高了WEB服务的并发能力. 在本章 ...

  8. 服务器二:epoll

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

  9. 并发服务器三种实现方式之进程、线程和select

    前言:刚开始学网络编程,都会先写一个客户端和服务端,不知道你们有没有试一下:再打开一下客户端,是连不上服务端的.还有一个问题不知道你们发现没:有时启服务器,会提示“Address already in ...

随机推荐

  1. 使用 ASP.NET Core MVC 创建 Web API(五)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

  2. [PHP] ubuntu下使用uuid扩展获取uuid

    1.php生成uuid网上大部分是使用随机数md5截取的,很有可能会重复冲突 2.uuid的组成中最重要的一个是机器码,大部分是网卡MAC地址, php无法获取到机器码,因此不能直接使用代码来生成一个 ...

  3. C++结构体与排列三平台出售

    结构将不同的数据类型整合在一起构成一个新的类型,排列三平台出售(企 娥:217 1793 408)相当于数据中一条记录,比如学生结构体,整合了学好,姓名等信息.结构体的好处就是可以对这些信息进行整体管 ...

  4. vue中路由按需加载的几种方式

    使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,如: import Hello from '@/components/Hello' import ...

  5. c/c++ 动态库与静态库的制作和使用

    静态库的用法 静态库的文件名 libxxx.a -->对应windows的.lib文件 做静态库的命令: ar rcs libxxx.a file1.o file2.o file.o 使用静态库 ...

  6. 今年开搞了,搭建一下vue开发环境

    首先-->搞了几天的SpringBoot玩的差不多了,领导直接说, 别项目组需要做前后端分离,说前端缺少人手,没有办法咯,只能硬着头皮去了, 说先学一下'vue',给我个文档让我学学,说是前半年 ...

  7. x宝23大洋包邮的老式大朝华MP3播放器简单评测

    (纯兴趣测评,非广告) 最近逛X宝,看到了这个古董级MP3播放器居然还在售,于是脑抽+情怀泛滥买了一个. 然后呢,从遥远的深圳跨越好几千公里邮过来了这个玩意: 那节南孚5号电池是我自己的,是为了对比一 ...

  8. spring framework体系结构及内部各模块jar之间的maven依赖关系

    很多人都在用spring开发java项目,但是配置maven依赖的时候并不能明确要配置哪些spring的jar,经常是胡乱添加一堆,编译或运行报错就继续配置jar依赖,导致spring依赖混乱,甚至下 ...

  9. Quartz+ssm注解方式的最最最最简单使用

    Maven配置 <!-- quartz监控 --> <dependency> <groupId>org.quartz-scheduler</groupId&g ...

  10. JVM 学习(一)反射、垃圾回收、异常处理--- 2019年4月

    1.JVM 基础知识点 JVM 虚拟机包含了:自动内存管理器.垃圾回收(垃圾回收调优). 执行顺序:Java 代码 --- .class 字节码文件(加载到虚拟机中) --- Java 类放在方法区中 ...