在写epoll回显服务器代码之前,可以先看看上一篇文章:select poll epoll三者之间的比较。最近在继续学习网络编程中的服务端编程中,了解到很多网游服务器是在IOMP(IO完成端口)框架下写的,但是这种方式只能在 Windows 下使用,奇了怪了,这么好的东西为什么不在Linux下也实现一套呢?这个问题我继续学习IOMP再来谈一谈!

  epoll是linux下高并发服务器的完美方案,因为是基于事件触发的,所以比select快的不只是一个数量级。单线程epoll,触发量可达到15000,但是加上业务后,因为大多数业务都与数据库打交道,所以就会存在阻塞的情况,这个时候就必须用多线程来提速。业务在线程池内,这里要加锁才行。测试结果2300个/s

  在Linux下,一个高效率一点的多线程并发服务器可以使用epoll来实现,我记得有一个库 libevent 好像就是在 epoll 基础上实现的。

  epoll的两种工作方式:

  1. LT

  LT(level triggered)是 epoll 缺省的工作方式,并且同时支持 block 和 no-block socket.

  在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的 select/poll 都是这种模型的代表

  2. ET

  ET (edge-triggered)是高速工作方式,只支持no-block socket,它效率要比LT更高。

  ET与LT的区别在于,当 一个新的事件到来时,ET模式下当然可以从 epoll_wait 调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的

  而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。

  因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

  好,废话不多说,为了方便以后使用基于 epoll 的服务器,把代码贴在这里,方便以后查找!

/*************************************************************************
> File Name: epoll_echosrv.c
> Author: huabo
> Mail: bohua1@126.com
> Created Time: Sun 05 Oct 2014 08:27:06 PM HKT
************************************************************************/ #include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h> #include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h> #include <pthread.h> #define MAXSIZE 5000
#define MAXLINE 10
#define OPEN_MAX 10
#define LISTENQ 20
#define INFTIM 1000 #define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while() //record the socket descriptor which need to read or write.
struct task
{
int fd;
struct task *next;
}; struct user_data
{
int fd;
unsigned int n_size;
char line[MAXLINE];
}; void * readtask(void *args);
void * writetask(void *args); struct epoll_event ev, events[];
int epfd; pthread_mutex_t mutex;
pthread_cond_t cond1; struct task *readhead = NULL, *readtail = NULL, *writehead = NULL; void setnonblocking(int sockfd)
{
int opts;
opts = fcntl(sockfd, F_GETFL);
if(opts < )
{
ERR_EXIT("fcntl");
}
opts = (opts | O_NONBLOCK);
opts = fcntl(sockfd, F_SETFL, opts);
if(opts < )
{
ERR_EXIT("fcntl");
}
} int main()
{
int listenfd;
//read and write thread ID
pthread_t tid1, tid2;
struct task *new_task = NULL;
struct user_data *rdata = NULL; pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond1, NULL); //initial id of read thread and write thread
pthread_create(&tid1, NULL, readtask, NULL);
pthread_create(&tid2, NULL, writetask, NULL); if( (listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
{
ERR_EXIT("socket");
}
setnonblocking(listenfd); 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); //reuse address
int on = ;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
{
ERR_EXIT("setsockopt");
} //bind port
if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
{
ERR_EXIT("bind");
} //listen
if(listen(listenfd, SOMAXCONN) < )
{
ERR_EXIT("listen");
} struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int connfd;
int sockfd; //epoll server
epfd = epoll_create(); //step 1: create descriptor for events
struct epoll_event ev, events[];
ev.data.fd = listenfd;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); //step 2: add descriptor and events int nfds;
int i;
while()
{
nfds = epoll_wait(epfd, events, , -);
for(i = ; i < nfds; ++i)
{
if(events[i].data.fd == listenfd)
{
printf("listen = %d\n", events[i].data.fd);
connfd = accept(listenfd, (struct sockaddr *)(&peeraddr), &peerlen);
if(connfd < )
{
ERR_EXIT("accept");
}
//set non-block when use epoll
setnonblocking(connfd); char *str = inet_ntoa(peeraddr.sin_addr);
printf("connect from >> %s %d\n", str, connfd); //add new file descriptor
ev.data.fd = connfd;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); //reset the listen descriptor
ev.data.fd = listenfd;
ev.events = EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, listenfd, &ev);
}
else if(events[i].events & EPOLLIN) //there is data to read.
{
if((sockfd = events[i].data.fd) <= )
{
printf("file descriptor error\n");
continue;
} new_task = (struct task*)malloc(sizeof(struct task));
new_task->fd = sockfd;
new_task->next = NULL; //add to the read task queue.
pthread_mutex_lock(&mutex);
if(readhead == NULL)
{
readhead = new_task;
readtail = new_task;
}
else
{
readtail->next = new_task;
readtail = new_task;
} //wake up the thread wait for cond1
pthread_cond_broadcast(&cond1);
pthread_mutex_unlock(&mutex);
}
else if(events[i].events & EPOLLOUT) //there is data to write
{
//something needed to be done here.
rdata = (struct user_data*)events[i].data.ptr;
sockfd = rdata->fd;
write(sockfd, rdata->line, rdata->n_size);
free(rdata); ev.data.fd = sockfd;
ev.events = EPOLLIN|EPOLLET;
epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
}
else if( (events[i].events & EPOLLHUP) || (events[i].events & EPOLLERR) || !(events[i].events & EPOLLIN))
{
//An error has occured on this fd, or the socket is not ready for reading(why were we notified then?)
fprintf(stderr, "epoll error\n");
close(events[i].data.fd);
continue;
}
}
}
return ;
} static int count111 = ;
static time_t oldtime = , nowtime = ; void* readtask(void *args)
{
printf("read task begin\n");
int fd = -;
unsigned int n;
struct user_data *data = NULL;
while()
{
pthread_mutex_lock(&mutex);
while(readhead == NULL)
pthread_cond_wait(&cond1, &mutex); fd = readhead->fd; struct task *tmp = readhead;
readhead = readhead->next;
free(tmp); pthread_mutex_unlock(&mutex);
data = (struct user_data*)malloc(sizeof(struct user_data));
data->fd = fd; char recvBuf[] = {};
int ret = ;
int rs = ; while(rs)
{
ret = recv(fd, recvBuf, , );
if(ret < )
{
if(errno == EAGAIN)
{
printf("EAGAIN\n");
break;
}
else
{
printf("recv error!\n");
close(fd);
break;
}
}
else if(ret == )
{
printf("client close.\n");
rs = ;
} if(ret == sizeof(recvBuf))
{
//need to recv again.
rs = ;
write(fd, recvBuf, ret);
fputs(recvBuf, stdout);
}
else
{
write(fd, recvBuf, ret);
fputs(recvBuf, stdout);
rs = ;
} } if(ret > )
{
data->n_size = n;
count111++; struct tm *today;
time_t ltime;
time(&nowtime); if(nowtime != oldtime)
{
printf("%d\n", count111);
oldtime = nowtime;
count111 = ;
} char buf[] = {};
sprintf(buf, "HTTP/1.0 200 OK\r\nContent-type: text/plain\r\n\r\n%s", "Hello world!\n");
send(fd, buf, strlen(buf), );
close(fd);
}
}
} void * writetask(void *args)
{ }

epoll 回显服务器源码的更多相关文章

  1. HashSet其实就那么一回事儿之源码浅析

    上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap,  本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...

  2. HashMap其实就那么一回事儿之源码浅析

    上篇文章<LinkedList其实就那么一回事儿之源码分析>介绍了LinkedList, 本次将为大家介绍HashMap. 在介绍HashMap之前,为了方便更清楚地理解源码,先大致说说H ...

  3. epoll(2) 使用及源码分析的引子

    epoll(2) 使用及源码分析的引子 本文代码取自内核版本 4.17 epoll(2) - I/O 事件通知设施. epoll 是内核在2.6版本后实现的,是对 select(2)/poll(2) ...

  4. tiny web服务器源码分析

    tiny web服务器源码分析 正如csapp书中所记,在短短250行代码中,它结合了许多我们已经学习到的思想,如进程控制,unix I/O,套接字接口和HTTP.虽然它缺乏一个实际服务器所具备的功能 ...

  5. rsyn实现服务器源码同步

    近期技术总监提出,要建立预生产环境,代码实现灰度发布.需要多台服务器源码保持一致. 实施步骤 1.安装rsyn服务端并添加环境变量. 2.安装客户端并配置环境变量. 3.更改配置文件并开放防火墙端口. ...

  6. Epoll详解及源码分析【转】

    转自:http://blog.csdn.net/chen19870707/article/details/42525887 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] ...

  7. 【转】ArrayList其实就那么一回事儿之源码浅析

    转自:http://www.cnblogs.com/dongying/p/4013271.html?utm_source=tuicool&utm_medium=referral ArrayLi ...

  8. LinkedList其实就那么一回事儿之源码分析

    上篇文章<ArrayList其实就那么一回儿事儿之源码分析>,给大家谈了ArrayList, 那么本次,就给大家一起看看同为List 家族的LinkedList. 下面就直接看源码吧: p ...

  9. ArrayList其实就那么一回事儿之源码浅析

    ArrayList 算是常用的集合之一了,不知作为javaner的你有没在百忙之中抽出一点时间看看ArrayList的源码呢. 如果看了,你会觉得其实ArrayList其实就那么一回事儿,对吧,下面就 ...

随机推荐

  1. PHP 设计模式之适配器模式

    <?php //[主要角色] //目标(Target)角色:定义客户端使用的与特定领域相关的接口,这也就是我们所期待得到的 //源(Adaptee)角色:需要进行适配的接口 //适配器(Adap ...

  2. jquery中的ajax方法详解

    定义和用法ajax() 方法通过 HTTP 请求加载远程数据.该方法是 jQuery 底层 AJAX 实现.简单易用的高层实现见 $.get, $.post 等.$.ajax() 返回其创建的 XML ...

  3. JQuery树形目录制作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DT ...

  4. 一个小知识,shell如何输出换行符号

    一般来说如果在echo里直接写上\n,他不会被转义,必须加上-e参数 echo "hello\n morning" # 输出为 hello\n morning echo -e &q ...

  5. Unity3D RPC调用顺序问题

    使用Unity自带的Network实现多人协同任务时,因为使用RPC传递消息.RPC即远程过程调用,对于它的使用,第一反应的问题就是如果连续两次调用RPC,RPC的函数会顺序执行吗?还是只要RPC的消 ...

  6. BZOJ 1588 营业额统计

    Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...

  7. 【Java】对Web Service的理解

    WSDL(Web Service Description Language)是描述Web Service的语言. 你会怎样向别人介绍你的Web service有什么功能,以及每个函数调用时的参数呢?你 ...

  8. WIN版的Jenkins Master加入LINUX的SLAVE节点,并作C++程序的集成交付

    这次深撸了一下JENKINS的配置,不敢说完全通了. 但对于整个体系,有了更新认识. 将LINUX作为SLAVE节点加入WIN的JENKINS里,网上有很多教程,依作即可. 在将相关任务分配给这个节点 ...

  9. 马云专访二:点评阿里雅虎交易、BAT三家、互联网巨头与政府关系

    记者:我们不得不要说到你和雅虎之间的事情了.你知道,雅虎对整个互联网业的意义不只是一家公司,它有它象征的意义,重要的是,雅虎对阿里巴巴的意义更加非同寻常,当你最后决定用76亿美元从雅虎“赎身”的时候, ...

  10. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...