﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-C++的天空</title><link>http://www.cppblog.com/ecopgm/</link><description /><language>zh-cn</language><lastBuildDate>Sat, 11 Apr 2026 10:36:22 GMT</lastBuildDate><pubDate>Sat, 11 Apr 2026 10:36:22 GMT</pubDate><ttl>60</ttl><item><title>TCP收包小结</title><link>http://www.cppblog.com/ecopgm/archive/2008/03/28/45611.html</link><dc:creator>ecopgm</dc:creator><author>ecopgm</author><pubDate>Fri, 28 Mar 2008 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/ecopgm/archive/2008/03/28/45611.html</guid><wfw:comment>http://www.cppblog.com/ecopgm/comments/45611.html</wfw:comment><comments>http://www.cppblog.com/ecopgm/archive/2008/03/28/45611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ecopgm/comments/commentRss/45611.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ecopgm/services/trackbacks/45611.html</trackback:ping><description><![CDATA[<p>先说说TCP收包的context（不定长包）。一般情况，发送方发送一个包，然后接收方收到一个包，这是最好处理的。第二种情况，当每次发生的包比较小时，发送数据时，TCP会启用优化算法，将多个小包集中起来发送，以提高传输效率。此时接收方的recv buffer中，可能出现不止一个包。第三种情况，recv buffer中每次只一个包，但接收方没及时取包，这时recv buffer中会积累多个包。<br>理所当然，TCP收包要考虑所有这些情况。一般来说有三种方法。第一种，定义好通讯协议，先收包头，然后根据包头中的消息真实大小，接收消息剩余部分。第二种方法，通讯协议规定好每个消息的开始和结束标识符。然后每次recv得到的数据先放到一个大（比如你的最大packet的2倍）buffer中，最后再来分析这个buffer分包。第三种方法，先用recv+MSG_PEEK接收某个固定长度，然后对接收到的"包"进行分析，然后做真正的recv操作。<br></p>
<img src ="http://www.cppblog.com/ecopgm/aggbug/45611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ecopgm/" target="_blank">ecopgm</a> 2008-03-28 11:28 <a href="http://www.cppblog.com/ecopgm/archive/2008/03/28/45611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高并发和高负载</title><link>http://www.cppblog.com/ecopgm/archive/2008/03/24/45314.html</link><dc:creator>ecopgm</dc:creator><author>ecopgm</author><pubDate>Mon, 24 Mar 2008 14:36:00 GMT</pubDate><guid>http://www.cppblog.com/ecopgm/archive/2008/03/24/45314.html</guid><wfw:comment>http://www.cppblog.com/ecopgm/comments/45314.html</wfw:comment><comments>http://www.cppblog.com/ecopgm/archive/2008/03/24/45314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ecopgm/comments/commentRss/45314.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ecopgm/services/trackbacks/45314.html</trackback:ping><description><![CDATA[能不能接受爆发连接（并发度如何），主要是取决于accept的速度。一个TCP连接的建立，要在client和server之间，完成三次握手，然后连接会被放到完成队列中，accept从完成队列中取出连接并返回。任何影响accept取连接的因素，都会影响并发度。一般策略是，1 独立处理accept，2 使用epoll处理accept，这两种情况，并发度都是不错的。<br>并发地接受了这么多连接，并不代表能完全处理。假如有很多连接同时在线，server accept成功并收到了数据，这时消息被放到消息队列中，等待逻辑线程来处理。因为生产者（收数据）的速度总是大于消费者（处理数据）的速度，因此消息队列会有考虑流控，以免系统资源被耗光。这样，有些消息就可能丢失。这就是同时在线连接数的问题。<br>前者是高并发，后者是高负载。设计时会权衡偏向。
<img src ="http://www.cppblog.com/ecopgm/aggbug/45314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ecopgm/" target="_blank">ecopgm</a> 2008-03-24 22:36 <a href="http://www.cppblog.com/ecopgm/archive/2008/03/24/45314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>并发策略总结 </title><link>http://www.cppblog.com/ecopgm/archive/2008/03/24/45269.html</link><dc:creator>ecopgm</dc:creator><author>ecopgm</author><pubDate>Mon, 24 Mar 2008 06:43:00 GMT</pubDate><guid>http://www.cppblog.com/ecopgm/archive/2008/03/24/45269.html</guid><wfw:comment>http://www.cppblog.com/ecopgm/comments/45269.html</wfw:comment><comments>http://www.cppblog.com/ecopgm/archive/2008/03/24/45269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ecopgm/comments/commentRss/45269.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ecopgm/services/trackbacks/45269.html</trackback:ping><description><![CDATA[<p>并发服务器有三种常见的架构：<br>1. 单线程epoll（ET非阻塞I/O) +线程池，也叫半同步半异步模式。这种模型比较常见，而且因为异步层和同步层使用消息队列传递消息，更容易实现对消息的FIFO处理。缺点就是线程的同步和上下文切换开销比较大。<br>2. ConnectionPerThread+阻塞型I/O。这是最古老的服务器并发模型，不太适合现今的这些高并发高负载服务器，当连接数巨大的时候，创建销毁线程的开销会无法承受，并且内核创建线程的速度也会成为瓶颈。这种模型的一种改进型就是领导者/跟随者模式，它吸取第一种模型中，线程数量不会膨胀的优点，使用线程池来处理连接。每当有连接到达时，都使用一个线程阻塞处理，处理完成后，线程再回到线程池中，这样有限的线程模拟出了ConnectionPerThread。一般来说，领导者/跟随者模型比第一种模型更加高效，因为它减少了线程同步和切换的开销，它的缺点就是FIFO很难保证。<br>3. 流水线模型。前面两种模式都有个缺点，它们不能花太长时间处理逻辑，因为在多CPU系统上，某些耗时的长请求可能会不断占住CPU，而导致短请求得不到处理，这会使某些CPU闲置。于是这种模型提出，将请求处理的过程划分步骤，不同的步骤考虑不同的资源处理（比如CPU, DISK I/O等），每个步骤使用单独的线程或线程池。这样比较耗时的操作可能集中在流水线的下级，而短请求也可以在上级得到快速处理。因为各级线程之间使用消息队列传递请求，也很容易实现FIFO。</p>
<img src ="http://www.cppblog.com/ecopgm/aggbug/45269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ecopgm/" target="_blank">ecopgm</a> 2008-03-24 14:43 <a href="http://www.cppblog.com/ecopgm/archive/2008/03/24/45269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>I/O策略小结</title><link>http://www.cppblog.com/ecopgm/archive/2008/03/24/45268.html</link><dc:creator>ecopgm</dc:creator><author>ecopgm</author><pubDate>Mon, 24 Mar 2008 06:40:00 GMT</pubDate><guid>http://www.cppblog.com/ecopgm/archive/2008/03/24/45268.html</guid><wfw:comment>http://www.cppblog.com/ecopgm/comments/45268.html</wfw:comment><comments>http://www.cppblog.com/ecopgm/archive/2008/03/24/45268.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ecopgm/comments/commentRss/45268.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ecopgm/services/trackbacks/45268.html</trackback:ping><description><![CDATA[<span>如何高效处理多个<span>socket I/O</span>的读写，是提高服务器性能的重点问题。unix-like下面，现有机制有<span>select,poll, &nbsp;epoll,kqueue,/dev/poll</span>两大类。</span>
<p><span>Select</span><span>有个缺点，它用<span>fd_set</span>管理所有要监视的<span>I/O</span>句柄，但是<span>fd_set</span>是一个位数组，只能接受句柄号小于<span>FD_SETSIZE</span>（默认<span>1024</span>）的句柄，虽然进程默认句柄号都是小于<span>1024</span>的，但是可以通过<span>ulimit &#8211;n</span>来修改，尤其是连接数超过<span>1024</span>时必需这么做（实际可能更少），如果要将大于<span>1024</span>的句柄放入<span>fd_set</span>，就可能发生数组越界程序崩溃的场面。</span></p>
<p><span>Poll</span><span>虽然解决了<span>FD_SETSIZE</span>问题，但是它和<span>select</span>一样，都有性能上的瓶颈。它们都会随着连接数的增加性能直线下降。这主要有两个原因，其一是每次<span>select/poll</span>操作，<span>kernel</span>都会建立一个当前线程关心的事件列表，并让线程阻塞在这个列表上，这是很耗时的操作。其二是每次<span>select/poll</span>返回后，线程都要扫描所有句柄来<span>dispatch</span>已发生的事件，这也是很耗时的。当连接数巨大时，这种消耗积累起来，就很受不了。</span></p>
<p><span>为了解决<span>select/poll</span>的性能问题，unix-like系统上开发出了三套新的利器<span>epoll</span>，<span>kqueue</span>，<span>/dev/poll</span>，其中<span>epoll</span>是<span>linux</span>的，<span>kqueue</span>是<span>freebsd</span>的，<span>/dev/poll</span>是<span>Solaris上</span>的，它们是select/poll的替代品。它们的设计就是针对<span>select/poll</span>的性能问题，主要避免<span>&nbsp;1。</span>每次调用都建立事件等待列表，取而代之建立长期的事件关注列表，这个列表可通过句柄（比如<span>epfd</span>）来增加和删除事件。<span>2。</span>调用返回之后，不再需要遍历所有句柄进行分发，内核会直接返回当前已发生的事件。不用说，性能在<span>select, poll</span>基础上有了大幅提升。<br><br>要注意的是，凡是使用readiness notification（LT)或者readiness change notification（ET)机制，都应该配合非阻塞I/O，因为这种事件通知，并不一定表示文件描述符真正就绪，如果收到通知之后去read，很有可能进入阻塞状态，这会严重影响服务器的并发性能，同时对ET模式，不能漏掉任何事件的处理，并且每次都应该读到socket返回EWOULDBLOCK为止，不然这个socket之后会永远保持沉默。</span></p>
<img src ="http://www.cppblog.com/ecopgm/aggbug/45268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ecopgm/" target="_blank">ecopgm</a> 2008-03-24 14:40 <a href="http://www.cppblog.com/ecopgm/archive/2008/03/24/45268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>