http://www.cnblogs.com/mingda/archive/2010/11/12/1875465.htmlhtml
http://www.cnblogs.com/mingda/archive/2010/11/14/1876961.html算法
http://www.ibm.com/developerworks/cn/aix/library/au-libev/编程
http://blog.csdn.net/hguisu/article/details/7445768/ socket编程,讲的比较清楚,看的比较明白
http://acm.tzc.edu.cn/acmhome/projectList.do?method=projectNewsDetail&nid=2
http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx服务器
构建现代的服务器应用程序须要以某种方法同时接收数百、数千甚至数万个事件,不管它们是内部请求仍是网络链接,都要有效地处理它们的操做。有许多解决方案,可是 libevent 库和 libev 库可以大大提升性能和事件处理能力。网络
在 Linux 下一切资源皆文件,普通文件是文件,磁盘打印机是文件,socket 固然也是文件。在 Linux 下建立一个新的 socket 链接,实际上就是建立一个新的文件描述符。
所以,咱们能够经过使用 ulimit -n 来限制程序所能打开的最大文件描述符数量,从而达到限制 socket 建立的数量。多线程
在阅读这一系列的文章时,我但愿你们始终记住如下几点:架构
1. 软件开发没有银弹,人们老是试图找到问题的惟一解和最优解,但事实是每一个问题都有N种解,而且在不一样状况下最优解是不同的,若是非要说软件开发有银弹, 那么这颗银弹就是人的心,是否找能到最优解,在于你是否能把握住了全部事情的平衡点。因此,请不要说某某机制最好、某某算法最优、某某架构万能、不须要再 了解其余了。也请不要自觉得目空一切技术,商业和盈利至上,实现途径和方式无所谓。请抱着一切皆有可能的心态看待全部事物,才有更多机会看到平衡点。框架
2. 语言、平台、API只是迷人眼的东西,它们好像什么都是,其实什么都不是。解决问题的关键在于设计者的心,设计者是否对要解决的问题和问题的上下文了然于 心。不要把学习语言、学习平台、学习API看成目标,它们只是泥沙和工具,最终咱们要建造的是房屋,因此请把目标放得更远。也不要把它们当成阻碍,由于它 们生来就是要让人使用的东西,不可能造成阻碍,若是你以为它们是阻碍,那实际上那个阻碍只在你内心。socket
3. 记得:Do one thing, and do it well。一次只作一件事,而且作好它。这一系列文章的基本开发和测试环境是Linux,编译器是gcc。若是你以前对Linux不是很熟悉,我建议你安 装一个VMware,并安装Ubuntu桌面系统,而后apt-get install build-essential,这样你的系统里就有完整的开发环境了,gnome自带的gedit很好用,我也是这么作的。不要一上来就Linux命令 行界面加vi编辑器,不必为难本身也不要搞得本身像黑客同样。请记住,咱们当前要作的是高性能socket服务器,只作这一件事,而且作好它!不是研究 Linux命令行或者vi编辑器,那些等有空再慢慢研究还来的及。编辑器
4. 师傅请进门修行靠我的。文章和教程的内容其实都是转之由转的东西,若是要了解原汁原味的内容,首选应该去阅读操做系统的代码,其次是系统文档,再次才是网 络教程。而经验是世界上最难传达的东西,文字只能让你造成记忆,不能让你得到经验,它只是像买彩票同样给你提供一个机会,让在你实践过程当中可能会有那么一 下的灵光一现,而后得出本身的结论,那才是你的真正经验。若是把生活比喻成RPG,造物主怎么可能让经验能够在玩家之间传递呢?那不是乱了套了,打RPG 咱们能够学各类技能,可是要获得经验就得打怪作任务,生活其实也是同样的道理。
本章演示了一个简单的echo服务器,它只支持一次处理一个链接,而且在链接退出时,服务器端也跟着关闭。
经过这个简单的例子,咱们学习了基本的socket初始化过程和链接的响应方式。
固然这些知识对于咱们的远大目标“高性能socket服务器"来讲是远远不够的,可是这些是基础的基础,至少咱们已经迈开了第一步。
下一章我将向你们介绍如何利用IO重用,在一个进程中同时处理多个链接的请求,敬请期待。
上一章,我向你们演示了一个最基本的socket服务器结构,它一次只能响应一个链接请求,而“能同时响应多个链接和请求”无疑是现实生活中对 socket服务器的最基本要求。要如何让socket服务器能够同时响应多个链接和请求呢?多线和多进程程确定是大部分人首先想到的,可能不少人不必定 真正清楚多线程和多进程的socket服务器架构具体意味着什么,可是至少你们都或多或少据说过这两种技术。不过本章中,咱们暂时还不会涉及到多线程和多 进程的服务器架构,我它们归类为设计范畴,而咱们暂时尚未脱离泥水匠身份,因此还要继续学习“泥沙之用途“,设计的事情须要等到咱们泥水匠毕业,升级建 筑设计师的时候再说。
那么本章具体的内容是什么呢?真是没有悬念,在上一章中我已经提早透露了:IO重用。下面就正式进入主题吧。
IO重用技术有不少种,有些是夸平台的,有些是平台独有的,这里就列举一些我知道的:
名称 平台
select Linux, *BSD, Mac OS X, Solaris, Windows
poll Linux, *BSD, Mac OS X
epoll Linux
/dev/poll Solaris
kqueue FreeBSD
IOCP Windows
每一种IO重用技术都是经过操做系统提供的一组特定的API函数调用来提供支持的,咱们所要学的就是学会怎么用这些API而且了解每种技术背后的原理和前因后果。
本章就从最通用应该也是最先出现的IO重用技术select开始。
补充说明一点,IO重用并不局限于用在socket编程上,只要涉及到IO的编程均可以应用。
后续我可能会继续花一到两章来描述poll和epoll,但也可能直接就介绍如何使用夸平台的libev和libevent库。特别是poll实际 上它对select来讲没有改进的地方,属于同一水平,实际上它们只是起源不同,可是时代差很少因此水平也就差很少,然后续的epoll、 dev/poll、iocp则是百家争鸣时期各个操做系统平台为了进一步提升io重用效率而设计的新机制,它们才本质上对select和poll等老模式 进行了改进,因此我可能会跳过poll介绍epoll。
libev的实验代码我其实已经作好了,poll和epoll有点懒得重复了,具体怎么样还不肯定就留个悬念吧,呵呵。
libev是libevent以后的一个事件驱动的编程框架,其接口和libevent基本相似。据官方介绍,其性能比libevent还要高,bug比libevent还少。
libevent
Chromium、Memcached、NTP、HTTPSQS等著名的开源程序都使用libevent库
/* File Name: server.c */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define DEFAULT_PORT 8000 #define MAXLINE 4096 int main(int argc, char** argv) { int socket_fd, connect_fd; struct sockaddr_in servaddr; char buff[4096]; int n; //初始化Socket if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //初始化 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。 servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT //将本地地址绑定到所建立的套接字上 if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //开始监听是否有客户端链接 if( listen(socket_fd, 10) == -1){ printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("======waiting for client's request======\n"); while(1){ //阻塞直到有客户端链接,否则多浪费CPU资源。 if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } //接受客户端传过来的数据 n = recv(connect_fd, buff, MAXLINE, 0); //向客户端发送回应数据 if(!fork()){ /*紫禁城*/ if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1) perror("send error"); close(connect_fd); exit(0); } buff[n] = '\0'; printf("recv msg from client: %s\n", buff); close(connect_fd); } close(socket_fd); }