网络编程——C10K简述

C10K问题

网络服务在处理数以万计的客户端连接时,往往出现效率底下甚至完全瘫痪,这被成为C10K问题。

(C10K = connection 10 kilo 问题)。k 表示 kilo,即 1000 比如:kilometer(千米), kilogram(千克)。

非阻塞I/O,最关键的部分是readiness notification(when ready, then notify!)和找出哪一个socket上面发生了I/O事件。

一般我们首先会想到用select来实现。
int select(int n, fd_set *rd_fds; fd_set *wr_fds, fd_set *ex_fds, struct timeval * timeout);

其中用到了fd_set结构,而fd_set不能大于FD_SETSIZE,默认是1024,很容易导致数组越界。

针对fd_set的问题,*nix提供了poll函数作为select的一个替代品:

int poll(struct pollfd *ufds, unsigned int nfds, int timeout)

第一个参数ufds是用户提供的一个pollfd数组,大小由用户自行决定,因此避免了FD_SETSIZE带来的麻烦。

然而select和poll在连接数增加时,性能急剧下降。这有两方面的原因:

  《1》首先操作系统面对每次的select/poll操作,都需要重新建立一个当前线程的关心事件链表,并把线程挂在这个复杂的等待队列上,这是相当耗时的。

  《2》其次,应用软件在select/poll返回后也需要对传入的句柄链表做一次循环扫描来dispatch,这也是很耗时的。这两件事都是和并发数相关,而I/O事件的密度也和并发数相关,导致CPU占用率和并发数近似成O(n2)的关系。

基于以上原因,*nix的hacker们开发了epoll, kqueue, /dev/poll这3套利器。epoll是Linux的方案,kqueue是freebsd的方案,/dev/poll是solaris的方案。
简单的说,这些api做了两件事:

  《1》避免了每次调用select/poll时kernel分析参素建立事件等结构的开销,kernel维护一个长期的时间关注列表,应用程序通过句柄修改这个链表和捕获I/P事件
  《2》避免了select/poll返回后,应用程序扫描整个句柄表的开销,kernel直接返回具体的链表给应用程序。

在接触具体api之前,先了解一下边缘触发(edge trigger)和条件触发(level trigger)的概念。边缘触发是指每当状态变化时发生一个io事件,假定经过长时间的沉默后,现在来了100个字节,这是无论边缘触发和条件触发都会产生一个read
ready notification通知应用程序可读。应用程序在读完来的50个字节,然后重新调用api等待io事件。这时条件触发的api会因为还有50个字节可读从而立即返回用户一个read ready notification。而边缘触发的api会因为可读这个状态没有发生变化而陷入长期等待。

因此在使用边缘触发的api时,要注意每次都要读到socket返回EWOULDBLOCK为止,
否则这个socket就算废了。而使用条件触发的api时,如果应用程序不需要写就不要关注socket可写的事件,否则会无限次的立即返回一个
write ready nitification.
大家常用的select就是属于条件触发这一类,以前本人翻过长期关注socket写事件从而CPU
100%的毛病。

epoll相关调用:

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll_create 创建kernel中的关注事件表,相当于创建fd_set.
epoll_ctl 修改这个表,相当与FD_SET等操作。
epoll_wait 完全是select/poll的升级版,支持的事件完全一致。并且epoll同时支持边缘触发和条件触发,一般来讲边缘触发的性能要好一些。

简单的例子:

[cpp] view plaincopy

        strut epoll_event ev, *events;
        int kdpfd = epoll_create(100); //创建kernel中的关注事件表,返回一个kernel事件表的句柄  

        ev.events = EPOLLIN | EPOLLET; //边缘触发
        ev.data.fd = listener;
        epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev); //将事件ev加入到kernel关注的事件表中  

        for(;;){  

           nfds = epoll_wait(kdpfd, events, maxevents, -1); //等待被通知
           for(n = 0; n < nfds; n++){
              if(events[n].data.fd == listener){
                 client = accept(listener, (struct sockaddr*)&local, &addrlen);
                 if(client < 0){
                    peror("accept");
                    continue;
                 }  

                 setnonblocking(client);
                 ev.events = EPOLLIN | EPOLLET;
                 ev.data.fd = client;
                 if(epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0){
                    fprintf(stderr, "epoll set insertion error: fd = %d, client);
                    return -1;
             }
             }else
                 do_use_fd(events[n].data.fd);
           }
        } 
时间: 2024-12-28 20:21:09

网络编程——C10K简述的相关文章

网络编程——The C10K Problem(C10K = connection 10 kilo 问题)。k 表示 kilo,即 1000

The C10K problem翻译 (C10K = connection 10 kilo 问题).k 表示 kilo,即 1000 比如:kilometer(千米), kilogram(千克). 如今的web服务器需要同时处理一万个以上的客户端了,难道不是吗?毕竟如今的网络是个big place了. 现在的计算机也很强大了,你只需要花大概$1200就可以买一个1000MHz的处理器,2G的内存, 1000Mbit/sec的网卡的机器.让我们来看看--20000个客户,每个为50KHz,100K

[转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么? http://www.52im.net/thread-1732-1-1.html 1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机网络编程的基础,TCP/UDP收发消息都靠它.我们熟悉的web服务器底层依赖它,我们用到的MySQL关系数据库.Redis内存数据库底层依赖它.我们用微信和别

[转帖]脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手 http://www.52im.net/thread-1729-1-1.html 1.引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道"三次"和"四次",但是如果问深入一点,他们往往都无法作出准确回答. 本篇文章尝试使用动画图片的方式,来对这个知识点进行"脑残式"讲解(哈哈),期望读者们可以更加简单.直观地理解TCP网络通信交互的本

XMPP-05Socket编程之网络编程篇

要学习XMPP,就要先了解Socket编程,在学习Socket之前,还要先了解一下网络编程 一.网络编程基本概念 通过使用套接字来达到进程间通信目的的编程就是网络编程. 网络编程从大的方面说就是对信息的发送到接收,中间传输为物理线路的作用,编程人员可以不用考虑…… 网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的!中间最主要的就是数据包的组装,数据包的过滤,数据包的捕获,数据包的分析,当然最后再做一些处理

全栈必备 网络编程基础

我们是幸运的,因为我们拥有网络.网络是一个神奇的东西,它改变了你和我的生活方式,改变了整个世界. 然而,网络的无标度和小世界特性使得它又是复杂的,无所不在,无所不能,以致于我们无法区分甚至无法描述. 对于一个码农而言,了解网络的基础知识可能还是从了解定义开始,认识OSI的七层协议模型,深入Socket内部,进而熟练地进行网络编程. 关于网络 关于网络,在词典中的定义是这样的: 在电的系统中,由若干元件组成的用来使电信号按一定要求传输的电路或这种电路的部分,叫网络. 作为一名从事过TMN开发的通信

网络编程释疑之:单台服务器上的并发TCP连接数可以有多少

曾几何时我们还在寻求网络编程中C10K问题的解决方案,但是现在从硬件和操作系统支持来看单台服务器支持上万并发连接已经没有多少挑战性了.我们先假设单台服务器最多只能支持万级并发连接,其实对绝大多数应用来说已经远远足够了,但是对于一些拥有很大用户基数的互联网公司,往往面临的并发连接数是百万,千万,甚至腾讯的上亿(注:QQ默认用的UDP协议).虽然现在的集群,分布式技术可以为我们将并发负载分担在多台服务器上,那我们只需要扩展出数十台电脑就可以解决问题,但是我们更希望能更大的挖掘单台服务器的资源,先努力

浅谈TCP/IP网络编程中socket的行为

我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: . TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) . Socket I/O系统调用(重点如read/write),这是TCP/IP协议在应用层表现出来的行为. . 编写Performant, Scalable的服务器程序.包括多线程.IO Multiplexing.非阻塞.异步等各种技术. 关于TCP/IP协议,建议参考Richard Stevens的<TCP/IP Illust

网络编程中的read,write函数

关于TCP/IP协议,建议参考Richard Stevens的<TCP/IP Illustrated,vol1>(TCP/IP详解卷1). 关于第二层面,依然建议Richard Stevens的<Unix network proggramming,vol1>(Unix网络编程卷1),这两本书公认是Unix网络编程的圣经. 至于第三个层面,UNP的书中有所提及,也有著名的C10K问题,业界也有各种各样的框架和解决方案,本人才疏学浅,在这里就不一一敷述. 本文的重点在于第二个层面,主要

黑马程序员_JavaSE学习总结第26天_网络编程

------- android培训.java培训.期待与您交流! ----------  26.01  网络编程概述 网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换. 26.02  网络模型概述和图解 计算机网络之间以何种规则进行通信,就是网络模型研究问题. 网络模型一般是指 OSI(Open System Interconnection开放系统互连)参考模型 TCP/IP参考模型 OSI参考模型七层简述: 1.物理层:主要定义物理设备标准,如网线的接口类型.光纤的接口