﻿<?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++博客-decker502-文章分类-源码库</title><link>http://www.cppblog.com/decker502/category/11518.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 02 May 2010 11:37:28 GMT</lastBuildDate><pubDate>Sun, 02 May 2010 11:37:28 GMT</pubDate><ttl>60</ttl><item><title>很好的 nginx ppt </title><link>http://www.cppblog.com/decker502/articles/113630.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Mon, 26 Apr 2010 11:50:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/113630.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/113630.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/113630.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/113630.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/113630.html</trackback:ping><description><![CDATA[<div id="art2215469"><br>非常不错&nbsp; ：<br><a  href="http://agentzh.org/misc/slides/nginx-conf-scripting/nginx-conf-scripting.html#1">http://agentzh.org/misc/slides/nginx-conf-scripting/nginx-conf-scripting.html#1</a><br></div><img src ="http://www.cppblog.com/decker502/aggbug/113630.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2010-04-26 19:50 <a href="http://www.cppblog.com/decker502/articles/113630.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>源码集合</title><link>http://www.cppblog.com/decker502/articles/96262.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Tue, 15 Sep 2009 11:50:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/96262.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/96262.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/96262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/96262.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/96262.html</trackback:ping><description><![CDATA[<font face=宋体><u><font color=#800080>libiconv:&nbsp;&nbsp;&nbsp;</font></u>基于<span lang=EN-US>GNU</span>协议的开源库，主要是解决多语言编码处理转换等应用问题</font><a href="http://www.gnu.org/software/libiconv/"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://www.gnu.org/software/libiconv/</a>
<img src ="http://www.cppblog.com/decker502/aggbug/96262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-09-15 19:50 <a href="http://www.cppblog.com/decker502/articles/96262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>network-programming-using-libevent</title><link>http://www.cppblog.com/decker502/articles/94970.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Tue, 01 Sep 2009 06:06:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/94970.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/94970.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/94970.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/94970.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/94970.html</trackback:ping><description><![CDATA[<div class=postText>from:http://blog.gslin.info/2005/11/network-programming-using-libevent-i.html<br><br>在課堂上學過 Unix Network Programming 後，我們知道在處理多 User 時會有幾種方法解決：<br>
<ol>
    <li>一個新的 Connection 進來，用 <code><font color=#ff6633>fork()</font></code> 產生一個 Process 處理。
    <li>一個新的 Connection 進來，用 <code><font color=#ff6633>pthread_create()</font></code> 產生一個 Thread 處理。
    <li>一個新的 Connection 進來，丟入 Event-based Array，由 Main Process 以 Nonblocking 的方式處理所有的 I/O。<br></li>
</ol>
這三種方法當然也都有各自的缺點：<br>
<ol>
    <li>用 <code><font color=#ff6633>fork()</font></code> 的問題在於每一個 Connection 進來時的成本太高。
    <li>用 Multi-thread 的問題在於 Thread-safe 與 Deadlock 問題難以解決，另外有 Memory-leak 的問題要處理。
    <li>用 Event-based 的方式在於實做上不好寫，尤其是要注意到事件產生時必須 Nonblocking，於是會需要實做 Buffering 的問題，而 Multi-thread 所會遇到的 Memory-leak 問題在這邊會更嚴重。而在多 CPU 的系統上沒有辦法使用到所有的 CPU resource。<br></li>
</ol>
當然，針對前面兩項有各自的解法：<br>
<ol>
    <li>以 Poll 的方式解決：當一個 Process 處理完一個 Connection 後，不直接死掉，而繼續回到 accept() 的狀態繼續處理，但這樣會遇到 Memory-leak 的問題，於是採用這種方式的人通常會再加上「處理過 N 個 Connection 後死掉，由 Parent Process 再 <code><font color=#ff6633>fork()</font></code> 一隻新的」。最有名的例子是 <a href="http://httpd.apache.org/"><u><font color=#6699cc>Apache</font></u></a> 1.3。
    <li>Thread-safe 的問題可以透過自己撰寫，或是尋找其他 Thread-safe Library 直接使用。Memory-leak 的問題可以試著透過 Garbage Collection Library 分析出來。<a href="http://httpd.apache.org/"><u><font color=#6699cc>Apache</font></u></a> 2.0 的 Thread MPM 就是使用這個模式。<br></li>
</ol>
然而，目前高效率的 Server 都偏好採用 Event-based，一方面是沒有 Create Process/Thread 所造成的 Overhead，另外一方面是不需要透過 Shared Memory 或是 Mutex 在不同的 Process/Thread 之間交換資料。<br><br>然而，Event-based 在實做上的幾個複雜的地方在於：<br>
<ol>
    <li><code><font color=#ff6633>select()</font></code> 與 <code><font color=#ff6633>poll()</font></code> 的效率過慢，造成每次要判斷「有哪些 Event 發生」這件事情的成本很高，這在 BSD 支援 <code><font color=#ff6633>kqueue()</font></code>、Linux 支援 <code><font color=#ff6633>epoll()</font></code>、Solaris 支援 <code><font color=#ff6633>/dev/poll</font></code> 後就解決了，但這兩組 Function 都不是 Standard，於是在不同的平台上就必須再改一次。<br>
    <li>因為 Nonblocking，所以在 <code><font color=#ff6633>write()</font></code> 或是 <code><font color=#ff6633>send()</font></code> 時滿了需要自己 Buffering。
    <li>因為 Nonblocking，所以不能使用 <code><font color=#ff6633>fgets()</font></code> 或是其他類似的 function，於是需要自己刻一個 Nonblocking 的 <code><font color=#ff6633>fgets()</font></code>。但是使用者所丟過來的資料又不能保證在一次 <code><font color=#ff6633>read()</font></code> 或 <code><font color=#ff6633>recv()</font></code> 就有一行，於是要自己做 Buffering。 </li>
</ol>
實際上這三件事情在 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 都有 Library 處理掉了。<br><br>另外值得注意的一點在於 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 使用的是 3-clause BSD license 而非 GPL，所以在不想公開程式碼 (像是商業用途) 的情況下會比其他的 Library 適合。 <br><br>
<div class=postText>
<p>from:http://blog.gslin.info/2005/11/network-programming-using-libevent-ii.html</p>
<p>&nbsp;</p>
接下來要談的是 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 要如何使用，不過為了方便起見，我們直接寫一個很簡單的 Time Server 來當作例子：當你連上去以後 Server 端直接提供時間，然後結束連線。<br><br>在這些例子裡面我以 <a href="http://www.tw.freebsd.org/"><u><font color=#6699cc>FreeBSD</font></u></a> 6.0 當作測試的平台，另外使用 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 1.1a 當作 Event-based Library，Compile 時請使用 <code><font color=#ff6633>gcc -I/usr/local/include -o timeserver timeserver.c -L/usr/local/lib -levent</font></code> (如果 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 的 Header 與 Library 放在 <code><font color=#ff6633>/usr/include</font></code> 與 <code><font color=#ff6633>/usr/lib</font></code> 下的話可以省略這兩個參數)。<br><br>原始程式碼在文章的最後頭。<br><br><code><font color=#ff6633>event_init()</font></code> 表示初始化 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 所使用到的變數。<br><br><code><font color=#ff6633>event_set(&amp;ev, s, EV_READ | EV_PERSIST, connection_accept, &amp;ev)</font></code> 把 <code><font color=#ff6633>s</font></code> 這個 File Description 放入 <code><font color=#ff6633>ev</font></code> (第一個參數與第二個參數)，並且告知當事件 (第三個參數的 <code><font color=#ff6633>EV_READ</font></code>) 發生時要呼叫 <code><font color=#ff6633>connection_accept()</font></code> (第四個參數)，呼叫時要把 <code><font color=#ff6633>ev</font></code> 當作參數丟進去 (第五個參數)。<br><br>其中的 <code><font color=#ff6633>EV_PERSIST</font></code> 表示當呼叫進去的時候不要把這個 event 拿掉 (繼續保留在 Event Queue 裡面)，這點可以跟 <code><font color=#ff6633>connection_accept()</font></code> 內在註冊 <code><font color=#ff6633>connection_time()</font></code> 的程式碼做比較。<br><br>而 <code><font color=#ff6633>event_add(&amp;ev, NULL)</font></code> 就是把 <code><font color=#ff6633>ev</font></code> 註冊到 event queue 裡面，第二個參數指定的是 Timeout 時間，設定成 <code><font color=#ff6633>NULL</font></code> 表示忽略這項設定。<br><br>最後的 <code><font color=#ff6633>event_dispatch()</font></code> 表示進入 event loop，當 Queue 裡面的任何一個 File Description 發生事件的時候就會進入 callback function 執行。<br><br>這隻程式非常粗糙，有很多地方沒有注意到 Blocking 的問題，這點我們就先不管了。當跑起來以後你就可以連到 port 7000，就會出現類似下面的結果：gslin@netnews [~] [3:14/W5] t 0 7000<br>
<blockquote><code><br><font color=#ff6633>gslin@netnews [~/work/C] [3:15/W3] t 0 7000<br>Trying 0.0.0.0...<br>Connected to 0.<br>Escape character is '^]'.<br>Fri Nov 25 03:15:10 2005<br>Connection closed by foreign host.<br></font></code></blockquote><br>最基本的使用就是這樣了，你可以 <code><font color=#ff6633>man event</font></code> 看到完整的說明。<br><br>這是 <code><font color=#ff6633>timeserver.c</font></code>：<br>
<pre><code><br><font color=#ff6633>#include &lt;netinet/in.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;event.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;time.h&gt;<br><br>void connection_time(int fd, short event, struct event *arg)<br>{<br>    char buf[32];<br>    struct tm t;<br>    time_t now;<br><br>    time(&amp;now);<br>    localtime_r(&amp;now, &amp;t);<br>    asctime_r(&amp;t, buf);<br><br>    write(fd, buf, strlen(buf));<br>    shutdown(fd, SHUT_RDWR);<br><br>    free(arg);<br>}<br><br>void connection_accept(int fd, short event, void *arg)<br>{<br>    /* for debugging */<br>    fprintf(stderr, "%s(): fd = %d, event = %d.\n", __func__, fd, event);<br><br>    /* Accept a new connection. */<br>    struct sockaddr_in s_in;<br>    socklen_t len = sizeof(s_in);<br>    int ns = accept(fd, (struct sockaddr *) &amp;s_in, &amp;len);<br>    if (ns &lt; 0) {<br>        perror("accept");<br>        return;<br>    }<br><br>    /* Install time server. */<br>    struct event *ev = malloc(sizeof(struct event));<br>    event_set(ev, ns, EV_WRITE, (void *) connection_time, ev);<br>    event_add(ev, NULL);<br>}<br><br>int main(void)<br>{<br>    /* Request socket. */<br>    int s = socket(PF_INET, SOCK_STREAM, 0);<br>    if (s &lt; 0) {<br>        perror("socket");<br>        exit(1);<br>    }<br><br>    /* bind() */<br>    struct sockaddr_in s_in;<br>    bzero(&amp;s_in, sizeof(s_in));<br>    s_in.sin_family = AF_INET;<br>    s_in.sin_port = htons(7000);<br>    s_in.sin_addr.s_addr = INADDR_ANY;<br>    if (bind(s, (struct sockaddr *) &amp;s_in, sizeof(s_in)) &lt; 0) {<br>        perror("bind");<br>        exit(1);<br>    }<br><br>    /* listen() */<br>    if (listen(s, 5) &lt; 0) {<br>        perror("listen");<br>        exit(1);<br>    }<br><br>    /* Initial libevent. */<br>    event_init();<br><br>    /* Create event. */<br>    struct event ev;<br>    event_set(&amp;ev, s, EV_READ | EV_PERSIST, connection_accept, &amp;ev);<br><br>    /* Add event. */<br>    event_add(&amp;ev, NULL);<br><br>    event_dispatch();<br><br>    return 0;<br>}</font></code></pre>
</div>
<br><br>
<div class=postText>from:http://blog.gslin.info/2005/11/network-programming-using-libevent-iii.html<br><br>這次要談的跟 Network Programming 沒有直接的關係。<br><br>在寫 Nonblocking Network Program 通常要處理 Buffering 的問題，但並不好寫，主要是因為 <code><font color=#ff6633>read()</font></code> 或 <code><font color=#ff6633>recv()</font></code> 不保證可以一次讀到一行的份量進來。<br><br>在 <a href="http://www.monkey.org/~provos/libevent/"><u><font color=#6699cc>libevent</font></u></a> 裡面提供相當不錯的 Buffer Library 可以用，完整的說明在 <code><font color=#ff6633>man event</font></code> 的時候可以看到，最常用的應該就是以 <code><font color=#ff6633>evbuffer_add()</font></code>、<code><font color=#ff6633>evbuffer_readline()</font></code> 這兩個 Function，其他的知道存在就可以了，需要的時候再去看詳細的用法。<br><br>下面直接提供 <code><font color=#ff6633>libevent-buff.c</font></code> 當作範例，編譯後看執行結果，再回頭來看 source code 應該就有感覺了：<br>
<p>&nbsp;
<pre><code><br><font color=#ff6633>#include &lt;sys/time.h&gt;<br>#include &lt;event.h&gt;<br>#include &lt;stdio.h&gt;<br><br>void printbuf(struct evbuffer *evbuf)<br>{<br>    for (;;) {<br>        char *buf = evbuffer_readline(evbuf);<br>        printf("* buf = %p, the string = \"\e[1;33m%s\e[m\"\n", buf, buf);<br>        if (buf == NULL)<br>            break;<br>        free(buf);<br>    }<br>}<br><br>int main(void)<br>{<br>    struct evbuffer *evbuf;<br><br>    evbuf = evbuffer_new();<br>    if (evbuf == NULL) {<br>        fprintf(stderr, "%s(): evbuffer_new() failed.\n", __func__);<br>        exit(1);<br>    }<br><br>    /* Add "gslin" into buffer. */<br>    u_char *buf1 = "gslin";<br>    printf("* Add \"\e[1;33m%s\e[m\".\n", buf1);<br>    evbuffer_add(evbuf, buf1, strlen(buf1));<br>    printbuf(evbuf);<br><br>    u_char *buf2 = " is reading.\nAnd he is at home.\nLast.";<br>    printf("* Add \"\e[1;33m%s\e[m\".\n", buf2);<br>    evbuffer_add(evbuf, buf2, strlen(buf2));<br>    printbuf(evbuf);<br><br>    evbuffer_free(evbuf);<br>}</font></code><code><br></code></pre>
</div>
</div>
<img src ="http://www.cppblog.com/decker502/aggbug/94970.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-09-01 14:06 <a href="http://www.cppblog.com/decker502/articles/94970.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 源码分析：evbuffer缓冲</title><link>http://www.cppblog.com/decker502/articles/94968.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Tue, 01 Sep 2009 05:50:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/94968.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/94968.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/94968.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/94968.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/94968.html</trackback:ping><description><![CDATA[原文地址：<a href="http://zhyclt.blog.163.com/blog/static/839359200881210747973/">http://zhyclt.blog.163.com/blog/static/839359200881210747973/</a><br><br>
<p><font size=2>Author : Kevin Lynx</font> </p>
<p><font size=2><strong>前言</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 可以说对于任何网络库(模块)而言，一个缓冲模块都是必不可少的。缓冲模块主要用于缓冲从网络接收到的数据，以及<br>用户提交的数据(用于发送)。很多时候，我们还需要将网络模块层(非TCP层)的这些缓冲数据拷贝到用户层，而这些内存拷贝<br>都会消耗时间。<br>&nbsp;&nbsp;&nbsp; 在这里，我简要分析下libevent的相关代码(event.h和buffer.c)。 </font></p>
<p><font size=2><strong>结构</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 关于libevent的缓冲模块，主要就是围绕evbuffer结构体展开。先看下evbuffer的定义：<br></font></p>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><font size=2><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top none?; Codehighlighter1_0_10_Open_Text.style.display="none" Codehighlighter1_0_10_Closed_Image.style.display="inline" ; Codehighlighter1_0_10_Closed_Text.style.display="inline" ;?><img style="DISPLAY: none" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top none?; Codehighlighter1_0_10_Open_Text.style.display="inline" ; Codehighlighter1_0_10_Closed_Text.style.display="none" ;? Codehighlighter1_0_10_Open_Image.style.display="inline"><span style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">/**/</span><span><span style="COLOR: rgb(0,128,0)">/*</span><span style="COLOR: rgb(0,128,0)">event.h</span><span style="COLOR: rgb(0,128,0)">*/</span></span><span style="COLOR: rgb(0,0,0)"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top none?; ; ;? Codehighlighter1_28_213_Open_Text.style.display="none" Codehighlighter1_28_213_Closed_Image.style.display="inline" Codehighlighter1_28_213_Closed_Text.style.display="inline"><img style="DISPLAY: none" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top none?; ; ;? Codehighlighter1_28_213_Open_Text.style.display="inline" Codehighlighter1_28_213_Closed_Text.style.display="none" Codehighlighter1_28_213_Open_Image.style.display="inline"></span><span style="COLOR: rgb(0,0,255)">struct</span><span style="COLOR: rgb(0,0,0)">&nbsp;evbuffer&nbsp;</span><span style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)"><img src="http://www.cppblog.com/Images/dot.gif"></span><span><span style="COLOR: rgb(0,0,0)">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">buffer;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">orig_buffer;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;misalign;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;totallen;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;off;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;(</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">cb)(</span><span style="COLOR: rgb(0,0,255)">struct</span><span style="COLOR: rgb(0,0,0)">&nbsp;evbuffer&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">,&nbsp;size_t,&nbsp;size_t,&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">cbarg;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: rgb(0,0,0)">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></font></div>
<p><font size=2><br>&nbsp;&nbsp;&nbsp; libevent的缓冲是一个连续的内存区域，其处理数据的方式(写数据和读数据)更像一个队列操作方式：从后写入，从前<br>读出。evbuffer分别设置相关指针(一个指标)用于指示读出位置和写入位置。其大致结构如图：</font></p>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str.jpg"><font size=2><img style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=140 alt=evbuffer_str src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str_thumb.jpg" width=290 border=0></font></a><font size=2> <br>&nbsp;&nbsp;&nbsp; orig_buffer指向由realloc分配的连续内存区域，buffer指向有效数据的内存区域，totallen表示orig_buffer指向的内存<br>区域的大小，misalign表示buffer相对于orig_buffer的偏移，off表示有效数据的长度。 </font></p>
<p><font size=2><strong>实际运作</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 这里我将结合具体的代码分析libevent是如何操作上面那个队列式的evbuffer的，先看一些辅助函数： </font></p>
<p><font size=2>evbuffer_drain:<br>&nbsp;&nbsp;&nbsp; 该函数主要操作一些指标，当每次从evbuffer里读取数据时，libevent便会将buffer指针后移，同时增大misalign，减小off，<br>而该函数正是做这件事的。说白了，该函数就是用于调整缓冲队列的前向指标。 </font></p>
<p><font size=2>evbuffer_expand:<br>&nbsp;&nbsp;&nbsp; 该函数用于扩充evbuffer的容量。每次向evbuffer写数据时，都是将数据写到buffer+off后，buffer到buffer+off之间已被<br>使用，保存的是有效数据，而orig_buffer和buffer之间则是因为读取数据移动指标而形成的无效区域。<br>&nbsp;&nbsp;&nbsp; evbuffer_expand的扩充策略在于，首先判断如果让出orig_buffer和buffer之间的空闲区域是否可以容纳添加的数据，如果<br>可以，则移动buffer和buffer+off之间的数据到orig_buffer和orig_buffer+off之间(有可能发生内存重叠，所以这里移动调用的<br>是memmove)，然后把新的数据拷贝到orig_buffer+off之后；如果不可以容纳，那么重新分配更大的空间(realloc)，同样会移动<br>数据。<br>&nbsp;&nbsp;&nbsp; 扩充内存的策略为：确保新的内存区域最小尺寸为256，且以乘以2的方式逐步扩大(256、512、1024、...)。 </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 了解了以上两个函数，看其他函数就比较简单了。可以看看具体的读数据和写数据： </font></p>
<p><font size=2>evbuffer_add:<br>&nbsp;&nbsp;&nbsp; 该函数用于添加一段用户数据到evbuffer中。很简单，就是先判断是否有足够的空闲内存，如果没有则调用evbuffer_expand<br>扩充之，然后直接memcpy，更新off指标。 </font></p>
<p><font size=2>evbuffer_remove:<br>&nbsp;&nbsp;&nbsp; 该函数用于将evbuffer中的数据复制给用户空间(读数据)。简单地将数据memcpy，然后调用evbuffer_drain移动相关指标。 </font></p>
<p><font size=2><strong>其他</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 回过头看看libevent的evbuffer其实是非常简单的(跟我那个kl_net里的buffer一样)，不知道其他人有没有更优的缓冲管理<br>方案。evbuffer还提供了两个函数：evbuffer_write和evbuffer_read，用于直接在套接字(其他文件描述符)上写/读数据。 </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 另外，关于libevent，因为官方提供的VC工程文件有问题，很多人在windows下编译不过。金庆曾提供过一种方法。其实主要<br>就是修改event-config.h文件，修改编译相关配置。这里我也提供一个解决步骤，顺便提供完整包下载： </font></p>
<p><font size=2>1. vs2005打开libevent.dsw，转换四个工程(event_test, libevent, signal_test, time_test)<br>2. 删除libevent项目中所有的文件，重新添加文件，文件列表如下：<br>&nbsp;&nbsp; buffer.c<br>&nbsp;&nbsp; evbuffer.c<br>&nbsp;&nbsp; evdns.c<br>&nbsp;&nbsp; evdns.h<br>&nbsp;&nbsp; event.c<br>&nbsp;&nbsp; event.h<br>&nbsp;&nbsp; event_tagging.c<br>&nbsp;&nbsp; event-config.h<br>&nbsp;&nbsp; event-internal.h<br>&nbsp;&nbsp; evhttp.h<br>&nbsp;&nbsp; evrpc.h<br>&nbsp;&nbsp; evrpc.c<br>&nbsp;&nbsp; evrpc-internal.h<br>&nbsp;&nbsp; evsignal.h<br>&nbsp;&nbsp; evutil.c<br>&nbsp;&nbsp; evutil.h<br>&nbsp;&nbsp; http.c<br>&nbsp;&nbsp; http-internal.h<br>&nbsp;&nbsp; log.c<br>&nbsp;&nbsp; log.h<br>&nbsp;&nbsp; min_heap.h<br>&nbsp;&nbsp; strlcpy.c<br>&nbsp;&nbsp; strlcpy-internal.h<br>&nbsp;&nbsp; tree.h<br>&nbsp;&nbsp; win32.c<br>&nbsp;&nbsp; config.h<br>&nbsp;&nbsp; signal.c<br>3. 替换event-config.h，使用libevent-iocp中的<br>4. 项目设置里添加HAVE_CONFIG_H预处理宏<br>5. 修改win32.c中win32_init函数，加入WSAStartup函数，类似于：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSADATA wd;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int err;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct win32op *winop;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t size;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ( err = WSAStartup( MAKEWORD( 2, 2 ), &amp;wd ) ) != 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event_err( 1, "winsock startup failed : %d", err );<br>6. 修改win32.c中win32_dealloc函数，在函数末尾加上WSACleanup的调用：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSACleanup();<br>6. 至此libevent编译成功；<br>7. 几个例子程序，只需要加入HAVE_CONFIG_H预处理宏，以及连接ws2_32.lib即可；<br>&nbsp;&nbsp; (time_test需要修改time-test.c文件，即在包含event.h前包含windows.h)</font></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/decker502/aggbug/94968.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-09-01 13:50 <a href="http://www.cppblog.com/decker502/articles/94968.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LIBEVENT事件驱动库(跨平台)介绍</title><link>http://www.cppblog.com/decker502/articles/94966.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Tue, 01 Sep 2009 05:49:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/94966.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/94966.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/94966.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/94966.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/94966.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文地址：http://zhyclt.blog.163.com/blog/static/8393592008102810294574/libevent 是一个强大的跨平台的事件通知库，如果不想被多线程困扰，可以考虑这个平台，它从1.2.* 版本开始支持轻量级的http server 开发支持，随后陆续还推出轻量级 DNS server、RPC server 开发支持，网络上目前可参考的代码不多...&nbsp;&nbsp;<a href='http://www.cppblog.com/decker502/articles/94966.html'>阅读全文</a><img src ="http://www.cppblog.com/decker502/aggbug/94966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-09-01 13:49 <a href="http://www.cppblog.com/decker502/articles/94966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 结构分析</title><link>http://www.cppblog.com/decker502/articles/94965.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Tue, 01 Sep 2009 05:46:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/94965.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/94965.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/94965.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/94965.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/94965.html</trackback:ping><description><![CDATA[原文地址：<a href="http://zhyclt.blog.163.com/blog/static/839359200881210428951/">http://zhyclt.blog.163.com/blog/static/839359200881210428951/</a><br><br>&nbsp;libevent是一个针对*nix的高级IO的库（FreeBSD:kqueue, Linux:epoll, Solaris:/dev/poll）的封装（虽然对于windows它也能工作，不过它封装的不是iocp，所以这里就不讨论了）。现在我们看看libevent的实现结构。<br>1、libevent使用的数据结构简介&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> libevent中使用了tail queue、red black tree，现在简单总结一下，免得我自己忘了。<br>&nbsp;<wbr>&nbsp;<wbr> 1、tail queue:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 它的基本特性和单向链表一样，但是在它的head中增加了一个指向末尾的指针，所以它能够直接在链表的尾部插入数据，但是它所付出的代价是：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>它实现的代码量要比同算法的单向链表增加15%,而且运行效率上要增加约20%的开销。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 现在先看一个例子(以下代码只为了说明tail queue的使用，不考虑错误)；<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_HEAD(my_tq, entry) head;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> //just for show<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct my_tq *my_head;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct entry {<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>TAILQ_ENTRY(entry) entry_tq;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> };&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> my_head = malloc(sizeof(my_tq));<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INIT(my_head);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INSERT_HEAD(my_head, p1 = malloc(sizeof(struct entry)), entry_tq);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INSERT_TAIL(my_head, p2 = malloc(sizeof(struct entry)), entry_tq);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INSERT_AFTER(my_head, p2, malloc(sizeof(struct entry)), entry_tq);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> while (my_head-&gt;tqh_first != NULL)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_REMOVE(my_head, my_head-&gt;tqh_first, entry_tq);
<p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> tail queue的操作相关宏：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_HEAD(HEADNAME, TYPE) head ---&gt;定义串连TYPE类型的结构，同时将这种"新"类型的tail queue结构命名为HEADNAME, 最后还定义了一个变量&#8220;head&#8221;。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INIT(&amp;head) ---&gt;初始化tail queue的两个指针，对列的头指针和队列的尾指针<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_ENTRY (TYPE) ---&gt;这个宏使用在TYPE结构体的定义内部，它定义维护结构体对象在tail queue中的位置所需要的指针。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TAILQ_INSERT_HEAD/TAILQ_INSERT_TAIL/TAILQ_INSERT_AFTER/TAILQ_REMOVE,这些宏的作用比较明显，需要注意的是它们都需要传入head指针和TAILQ_ENTRY定义的标识符。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> (我觉得宏最强的地方和最有问题的地方就是它不进行类型检查，最让代码阅读者头痛的就是条件编译^_^)。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2、reb black tree<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> red black tree（rbt） 是一种近似的二叉平衡树,它的特点如下：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 1、根节点是黑色的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2、表示rbt节点结构的空指针都会指向一个空的节点（rchild, lchild, lparent）,而且空节点是黑色的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3、红色节点的rchild/lchild/parent都是红色节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 4、从根节点到空节点的任一路径所包含的黑色节点数是相同的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rbt很重要的一个操作就是子树旋转，它是调整rbt结构的辅助操作。例如：右旋转:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> A&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> B&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 这里需要注意的是旋转前B的右子节点y变成<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> / &nbsp;<wbr> \ &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> /&nbsp;<wbr> \ &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 了A的左子节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> B&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> C&nbsp;<wbr>&nbsp;<wbr> =======&gt;&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>x&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> A<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> /&nbsp;<wbr> \&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr> /&nbsp;<wbr> \<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> x &nbsp;<wbr>&nbsp;<wbr> y&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>y&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> C<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rbt的插入算法：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 按照二叉排序树的流程插入一个新的元素<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 并把该节点设置为红色<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 调整新的"树"，使它满足rbt的条件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果新节点的父节点为黑色，那么<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 调整完成<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果父节点的颜色为红色，那么进行以下的操作：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 假设：cn为当前节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pn 为当前节点的父节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> pun 为当前节点的父节点的兄弟节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cn 是pn的左子节点。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 1、如果pun是红色，那么将pn和pun都变成黑色，同时把cn设为pn-&gt;lparent,重新进入迭代<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2、如果pun是黑色，而且cn是pn的右子节点，那么将cn = pn,然后做一次左旋转LR(cn)操作，这样就<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 可以把调整的树转换到左子树上，这样就把树的结构转换到第三种情况。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3、如果pun是黑色，而且cn是pn的左子节点，那么将pn设置为黑色，并把pn-&gt;lparent设置为红色，<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 同时进行右旋转RR(pn-&gt;lparent)--&gt;这时整个调整流程结束。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rbt的删除算法：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 按照二叉排序树的删除流程删除一个元素<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果删除的节点是红色的，那么它不会破坏rbt的条件，如果删除的节点是黑色的，那么就需要对新的树进行调整，使它满足tbt的条件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 设rn 为替代被删除节点的节点（由于所有"逻辑上悬空"的指针都会指向"特殊的空节点"，所以比如存在替代被删除节点的替代节点），且它是rn-&gt;lparent的左子节点（对于右子节点的情况处理方式是对称的）<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rcn 为rn的兄弟节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果cn是红色，那么只要把它变成黑色就满足rbt条件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果cn是黑色，那么就要根据rcn的颜色分别进行处理：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 1、rcn是红色，那么可以知道cn-&gt;lparent是黑色，rcn的两个子节点都是黑色，那么把cn-&gt;lparent和w的颜色<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 互换，并进行左旋转操作LR（x-&gt;parent）,这样cn就会有一个新的兄弟节点，而且它是黑色的。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2、rcn是黑色，而且rcn的两个子节点都是黑色，这样只要把rcn设为红色，并把当前节点变成它的父节点&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> cn = cn-&gt;lparent 进行递规迭代就可以了<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 3、rcn是黑色，而且左子节点rcn-&gt;lleft是红色，右子节点rcn-&gt;lright是黑色, 那么把rcn和rcn-&gt;lleft的颜色<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 互换，并进行右旋转操作RR(rcn)，这样就会转换到第四中情况，而且cn也有了一个新的兄弟节点<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 4、rcn是黑色，而且rcn的右子节点是红色，那么将rcn与rcn-&gt;parent的颜色互换，并进行左旋转操作</p>
&nbsp;
<p><wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> LR(rcn-&gt;lparent),<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 这样就把缺少的黑色节点转移的原来的rcn-&gt;lright节点上，而且这个节点是红色的，这时只要把它的颜色转变成黑色就能完成rbt条件的修补。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 现在我们对于红黑树的一些算法都比较清楚了，下面看看与红黑树操作相关的几个宏的使用<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> RB_HEAD(my_rb_tree, rb_entry) rb_head;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct my_rb_tree * rb_tree;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct rb_entry {<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>RB_ENTRY(rb_entry) rb_entry_list;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> };<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rb_tree = malloc(sizeof(struct my_rb_tree))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> RB_INIT(rb_tree);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct rb_entry * prb1 = NULL, *prb2 = NULL;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> RB_INSERT(my_rb_tree, rb_tree, prb1 = malloc(sizeof(struct rb_entry)))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> RB_INSERT(my_rb_tree, rb_tree, prb2 = malloc(sizeof(struct rb_entry)))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct rb_entry * min = RB_MIN(my_rb_tree, rb_tree);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> struct rb_entry * max = RB_MAX(my_rb_tree, rb_tree);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> assert(RB_EMPTY(rb_tree))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> RB_FOREACH(tmp_entry, my_rb_tree, rb_tree)<br>&nbsp;<wbr>&nbsp;<wbr>custom_function(temp_entry);<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> struct rb_entry * t_entry = NULL;<br>&nbsp;<wbr>&nbsp;<wbr> RB_NEXT(my_rb_tree, rb_tree, t_entry);<br>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr> RB_REMOVE(my_rb_tree, rb_tree, prb1);<br>&nbsp;<wbr>&nbsp;<wbr> ...<br>&nbsp;<wbr>&nbsp;<wbr> RB_FIND(my_rb_tree, rb_tree, prb2）<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 上面的这些操作都比较简单，参数的含义也比较明显，这里就不多说了。<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 需要注意的是rbt定义的辅助宏：<br>&nbsp;<wbr>&nbsp;<wbr> RB_PROTOTYPE(my_rb_tree, rb_entry, rb_entry_list, compare)：声明一些操作函数，它的参数的含义是：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> my_rb_tree: 用户定义的rbt的名称, 它可用于后面的变量定义<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rb_entry:&nbsp;<wbr>&nbsp;<wbr> rbt中保存的结构体类型<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> rb_entry_list: rb_tree定义中RB_ENTRY定义的字段成员字段的名称<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> compare :&nbsp;<wbr>&nbsp;<wbr> 比较函数，用于在find算法和remove算法中查找目标元素，它的原型是：int compare(struct rb_entry * a, struct rb_entry* b)<br>&nbsp;<wbr>&nbsp;<wbr> RB_GENERATE(my_rb_tree, rb_entry, rb_entry_list, compare)：产生操作函数的代码, 它的参数的含义和RB_PROTOTYPE的含义是一样的。</p>
<div>
<p><font face=宋体>2、libevent的处理流程<br>&nbsp;<wbr>&nbsp;<wbr> 现在我们看看libevent的基本处理流程；<br>&nbsp;<wbr>&nbsp;<wbr> 初始化libevent的运行环境<br>&nbsp;<wbr>&nbsp;<wbr> event_init()<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> event_set(&amp;event, filedescriptor, trigger_event, call_back, *parameter)--&gt;初始化事件的结构<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> event_add(&amp;event, timeinterval)--&gt;增加查询对列中的事件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> event_dispatch()<br>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> event_init<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> 创建event_base结构，base = calloc(sizeof(struct event_base))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> 检查系统是否支持clock_gettime, detect_monotic()<br>&nbsp;<wbr>&nbsp;<wbr> 获取系统当前的时间（事实上libevent的工作都是基于定时模型，所以提取系统的时间是非常重要的）<br>&nbsp;<wbr>&nbsp;<wbr> gettime(&amp;base-&gt;event_tv)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr> 初始化保存数据的红黑树和事件队列<br>&nbsp;<wbr>&nbsp;<wbr> RB_INIT(&amp;base-&gt;timetree);<br>&nbsp;<wbr> TAILQ_INIT(&amp;base-&gt;eventqueue);<br>&nbsp;<wbr> TAILQ_INIT(&amp;base-&gt;sig.signalqueue);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |</font></p>
<p>初始化IO系统，对于不同的平台的初始化函数是不同的，</p>
<p>现在以freebsd（kqueue）为例，跟踪一下系统的操作流程<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr> kq_init(base)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |---&gt;初始化struct kqop结构 kqueueop = calloc(sizeof(struct kop))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 创建kqueue描述符， kq = kqueue()<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 创建struct kevnet结构队列，用于kevent函数时用于监测的队列<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kqueueop-&gt;changes = malloc(NEVENT * sizeof(struct kevent))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 创建监测返回队列<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kqueueop-&gt;events = malloc(NEVENT * sizeof(struct kevnent))<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 测试系统是否真的支持kqueue<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kqueueop-&gt;changes[0].ident = -1;<br>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr> kqueueop-&gt;changes[0].filter = EVFILT_READ;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr> kqueueop-&gt;changes[0].flags = EV_ADD;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>当下列情况发生时，就表示该系统并非真的支持kqueue工作方式，一般只有在<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>Mac OS X平台上才会有这种情况发生<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>kevent(kq, kqueueop-&gt;changes, 1, kqueueop-&gt;events, NEVENT, NULL) != 1 ||<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kqueueop-&gt;events[0].ident != -1 || kqueueop-&gt;events[0].flags != EV_ERROR<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr> 创建优先级队列，它是用于保存被激活的事件<br>&nbsp;<wbr> event_base_priority_init(base, 1)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |--&gt;如果当前有事件是激活的，那么就直接返回-1,否则检查需要创建的队列数是否和目前的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 队列数一样，如果不一样就释放当前的资源，然后重新分配新的资源。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr> 设置全局变量current_base，它在很多函数中是充当默认参数的角色<br>&nbsp;<wbr> current_base = base;<br>&nbsp;<wbr> return (base);<br>&nbsp;<wbr><br>&nbsp;<wbr> event_set(struct event * ev, int fd, short events, void(*call_back)(int, short, void*), void *arg)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr> 这个函数比较简单，只是简单地初始化事件结构struct event,这里需要注意的是这里使用了在event_init初始化的<br>&nbsp;<wbr> 全局变量current_base变量，而且event_set中把事件的优先级设置为中等的值<br>&nbsp;<wbr><br>&nbsp;<wbr> event_add(struct event* ev, struct timeval *tv)<br>&nbsp;<wbr>和很多有超时设置的异步io函数一样，event_add的操时参数tv是可选的，在libevent中（以freebsd平台为例）对于<br>&nbsp;<wbr>IO的监控其实是分了两个机制:<br>&nbsp;<wbr>&nbsp;<wbr> 1、调用kqueue/kevent直接监控IO<br>&nbsp;<wbr>&nbsp;<wbr> 2、对于设置了超时的IO，超时置是通过内建的rbt来保存的，对于"阻塞的监控"（反复调用kevent进行操作,也就是<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 开发者调用了event_dispatch后，直到没有需要监控的IO才返回）,一次调用kevnet的超时时间就是当前时间</p>
<p>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 到下一个IO超时的时间<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr> 清楚了libevent的处理方式，对于event_add的操作就比较清楚了：<br>&nbsp;<wbr> 如果超时参数tv非空<br>&nbsp;<wbr> &nbsp;<wbr>|-----&gt;清除与该事件的相关状态信息（如果该事件已经在超时对列中，那么把该事件从操时队列中删除）<br>&nbsp;<wbr> &nbsp;<wbr>|&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> (如果该时间由于超时原因被激活，那么把事件从激活对列中删除)<br>&nbsp;<wbr> &nbsp;<wbr>|&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr> &nbsp;<wbr>|&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 将超时时间调整为绝对时间,并把事件加入到rbt中</p>
<p>检查事件的结果标志，将事件分别加入到kqueue的监控队列、信号队列中(这些对列是libevent维护的数据结构，而不是kqueue)</p>
<p>&nbsp;<wbr>event_queue_insert(base, ev, EVLIST_INSERTED)/event_queue_insert(base, ev, EVLIST_SIGNAL)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr> 将事件加入到kqueue监控队列<br>&nbsp;<wbr> evsel-&gt;add(evbase, ev)---&gt;kq_add(void * arg--&gt;struct kqop*, struct event* )<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 总的来说kq_add函数是根据struct event的参数初始化kevent结构并加入到系统的监测队列中<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 对于信号--&gt;kev.filter = EVFILT_SIGNAL<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> "可读"--&gt;kev.filter = EVFILT_READ<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> "可写"--&gt;kev.filter = EVFILT_WRITE<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 需要注意的是,读写是可以同时加入kqueue的，但是信号是单独加入kqueue的，这是因为信号的fd并<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 不是一个有效的IO描述符，kqueue的信号处理和signal/sigaction机制是兼容的，kqueue记录所有的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> signal处理，即使是标志了SIG_IGN的信号，但是它的优先级比sigal/sigaction低。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> （kev.fflags = NOTE_EOF是为了与select/poll的模式兼容，在读到eof的时候激活事件，对于socket<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 而言，就是在读完了缓冲区的内容，并且对方已经关闭了写端后会激活事件）<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 把监测的IO加入到kqueue中<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kq_insert(kqop, &amp;kev)<br>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> kq_add简单的把kev的数据保存到kqop中(当kqop中保存监听事件和激活事件的数组已经满时，进行了<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2倍的容量重新分配)<br>&nbsp;<wbr><br>&nbsp;<wbr>event_dispatch()<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |--&gt;event_loop(0)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |--&gt;event_base_loop(current_base, flags)--&gt;这里使用了全局变量current_base作为调度对象，所以<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 它不是线程安全的<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> event_base_loop(struct event_base *base, int flags)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> event_base_loop结合了线程安全和非线程安全的接口<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>检查base需要监听的信号对列base-&gt;sig.signalqueue<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>设置全局struct event_base * evsignal_base = base<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> （信号是面向进程的，用一个全局变量来标识信号的处理也很合理，不过开发者需要保证多线程调用event_base中的信号队列只有一个是非空的）<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 重新设置监控对列中的数据结构,(freebsd中就是kevent，不过它不需要设置，所以kq_recalc只是简单地返回0)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 检查base-&gt;event_gotterm，事件处理函数可以通过修改它的值控制循环的结束(这个也可以用在多线程环境中来终止一个监听线程的工作)。检查全局变量event_gotsig,这个是全局的接口，主要是方便在单线程环境境下简化编程工作，与event_gotsig配合的回调函数是 event_sigcb<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>调整超时rbt的绝对时间timeout_correct，这里调整是因为对于没有提供clock_gettime(CLOCK_MONOTONIC)的系统，libevent是通过gettimeofday()来获得系统当前的时间的，而gettimeofday会受到一些外部因素的影响(例如：ntpupdate等)。但是timeout_correct的算法只是当系统时间滞后与前一次检测时间时才会进行超时事件的时间调整，也就是说：<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> libevent保证超时时间的激活不会大于event_add设定的超时时间,当由于某些原因造成当前系统时间滞后时，<br>libevent会对每个事件的唤醒时间进行调整，这是等待时间是不变的，但是如果系统的时间被调整为超前于实际的时间，那么事件的等待时间就会变少。当然这些情况只对于使用gettimeofday的平台适用，对于提供clock_gettime(CLOCK_MONOTONIC)调用的平台，事件等待的始终是event_add设定的超时时间。(timeout_correct中修改的正好是rbt的key，不过它是顺序进行相同的修正，所以并没有破坏rbt的结构)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 计算最小的内核等待时间(调用kevent的超时时间):<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果没有已经存在激活的事件(由于优先级的原因，有的激活的事件可能还留在低优先级的队列中)，或者非"阻塞"的循环等待模式那么内核等待的时间为零，否则就等待下一个超时值到来的时间<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 进入内核等待evsel-&gt;dispatch(base, evbase, tv_p)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果设定了超时值，那么就将struct timeval 结构转化为struct timespec结构<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 调用内核检测函数kevnet<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> res = kevent(kqop-&gt;kq, changes, kqop-&gt;nchanges, events, kqop-&gt;nevents, ts_p);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 循环检查返回的事件结构<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ev = (struct event *)events[i].udata;<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 如果ev没有设置(ev-&gt;ev_events &amp; EV_PERSIST),那么在libevent维护的事件队列中删除对应的事件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> event_del(ev);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 将激活的事件加入到对应的激活队列中<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> event_active(ev, which, ev-&gt;ev_events &amp; EV_SIGNAL ? events[i].data : 1);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>处理超时事件timeout_process<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>timeout_process的处理比较简单，它通过枚举寻找rbt中已经超时的事件，并把该事件从超时事件队列中<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>删除，同时把事件加入到激活对列中<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> |<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>处理激活队列中的事件<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>event_process_active(base);<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>这里有两个地方需要注意的：<br>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr> &nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 1、event_process_active是根据优先级来调度已经激活的事件队列的，而且一次只会处理一个非空的事件队&nbsp;<wbr>&nbsp;<wbr> 列，如果高优先级(下标小的队列)的队列一直有激活的事件，那么低优先级队列就会一直被滞后。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 2、回调函数可以通过修改ev_pncalls指向的变量的值来中断同一个事件的多次激活，而且可以通过全局变量<br>event_gotsig来中断整个event_process_active调用。&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr><br>&nbsp;<wbr>&nbsp;<wbr> 对于libevent的基本处理流程我们已经比较清楚了。<br>&nbsp;<wbr>&nbsp;<wbr> libevent中兼容了多个系统平台的IO的特点，这里就不展开了^_^。&nbsp;<wbr>&nbsp;<wbr><br></p>
<p>3、整体感觉<br>&nbsp;<wbr>&nbsp;<wbr> 1、libevent使用了很经典的通过结构体的函数指针来维护不同平台的差异。<br>&nbsp;<wbr>&nbsp;<wbr> 2、libevent中通过rbt来维护超时事件的信息，在效率上有很可观的表现，同时也考虑了时间的修正问题，对我来说很有启发性^_^。</p>
</div>
<img src ="http://www.cppblog.com/decker502/aggbug/94965.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-09-01 13:46 <a href="http://www.cppblog.com/decker502/articles/94965.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent库api简介</title><link>http://www.cppblog.com/decker502/articles/94238.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Mon, 24 Aug 2009 04:15:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/94238.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/94238.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/94238.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/94238.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/94238.html</trackback:ping><description><![CDATA[LIBEVENT事件驱动库(跨平台)介绍 <br><br>原文地址：<a href="http://www.leftworld.net/wenzhang/show/7408.html">http://www.leftworld.net/wenzhang/show/7408.html</a><br><br>libevent 是一个强大的跨平台的事件通知库，如果不想被多线程困扰，可以考虑这个平台，它从1.2.* 版本开始支持轻量级的http server 开发支持，随后陆续还推出轻量级 DNS server、RPC server 开发支持，网络上目前可参考的代码不多，希望可以给网友提供一些帮助 <br><br><br><br>&nbsp;&nbsp; <br><br>LIBEVENT的功能: <br><br>这组事件API提供了一种当某个指定文件描述符有效或时间到达时执行某个函数的机制. <br><br>在使用事件API前必须使用event_init()初始化. <br><br>In order to process events, an application needs to call <br><br>为了能够处理事件,应用程序必须调用event_dispatch(),该函数只当错误时返回,这时应该由用应程序接管事件. <br><br>函数event_set() <br><br>(原型 <br><br>event_set(struct event *ev, int fd, short event, <br><br>void (*fn)(int, short, void *), void *arg); <br><br>) <br><br>用于生成事件结构体ev,以备event_add()和event_del()使用.事件驱动程序将会调用void (*fn)(int, short, void *)中fn指定的函数,并传递三个参数int:文件描述符,short:事件类型,void*:其它参数由arg参数指定. <br><br>int fd 指定要监视的文件描述符, <br><br>short event 可以是EV_READ,EV_WRITE或EV_READ|EV_WRITE表示该文件可以无阻塞地进行读写. <br><br><br><br>fn函数将会被调用,并传递给三个变量: <br><br>int fd:触发事件的文件描述符. <br><br>short event:触发事件的类型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. <br><br>void* :由arg参数指定的变量. <br><br>另外重复注册的事件将会产生重复的事件通知.EV_PERSIST可以让注册的事件在执行完后不被删除,直到调用event_del()删除. <br><br><br><br>结构体初始化完成后,在无需改变内容的情况下,可以被event_add(),event_del()重复使用.但是当结构体被event_add()添加之后,必须保持结构体的内容,直到事件被执行后退出或调用event_del()删除该事件.不允许将这个结构体变量注册完事件后重复使用.每一个描述符都需要一个单独的event结构体变量. <br><br><br><br>event_add()函数使通过event_set()设置的事件在事件匹配或超时时(如果设置了超时)被执行. <br><br>event结构体变量必须先用event_set()初始化过,并且在事件被删除前不得再次调用event_set()来初始化之.如果事件发生超时,旧的超时时间会被新的超时时间所取代. <br><br>event_del()函数会取消event结构体所指定的事件,如果该事件已经执行或没有注册(在事件链表中不存在),该函数不会产生任何作用. <br><br><br><br>evtimer_set(), evtimer_add(), evtimer_del(),evtimer_initialized() evtimer_pending()等函数()用于设置定时或超时操作.在这些函数中,文件描述符为-1,事件类型为EV_TIMEOUT. <br><br><br><br>signal_set(), signal_add(), signal_del(),signal_initialized(),signal_pending()等函数从略,其中事件类型为EV_SIGNAL.那就意味着signal_set() 添加了EV_PERSIST. <br><br><br><br>为了避免信号竞争,事件API提供了两程变量:event_sigcb 和 event_gotsig.某个信号的句柄设置event_gotsig表示收到信号.应用程序把event_sigcb设置成一个回调函数.当信号句柄设置了 <br><br>event_gotsig之后,event_dispatch函数会执行回调函数处理接收到的信号.当没有事件注册时回调函数返回1.回调函数可以返回-1表示错误,这将导致event_dispatch()结束,错误代码为EINTR. <br><br><br><br>函数跟相似,但是它只调用回调函数一次,并且不需要调用者准备event结构体变量.该函数支持EV_TIMEOUT,EV_READ, and EV_WRITE. <br><br><br><br>event_pending()用于检测event结构体变量指定的事件是否处于等待状态.如果设定了EV_TIMEOUT,并且tv结构体指针变量非空,则事件终止时间由tv返回. <br><br><br><br>event_initialized()用于检测event结构体变量是否已经初始化. <br><br><br><br>event_loop提供一个接口用于单向执行等待事件.EVLOOP_ONCE和 EVLOOP_NONBLOCK有效.调用event_loopexit函数从事件循环中退出.在结定时间超时后下一个event_loop()重复将会正常完成然后不再等待事件直接退出.之后的event_loop()调用将会被正常执行.调用event_loopbreak函数直接从事件循环中退出. 下一个事件完成后event_loop()会中止退出.event_loopbreak()典型的是被事件回调函数调用,这个特性类似于执行了break;语句.之后的event_loop()调用会正常进生 <br><br><br><br>初始化event结构体变量中的回调函数是程序调用者必须提供的. <br><br>事件优先级 <br><br>默认情况下,libevent以相同的优先级调度法动的事件.但是有时候希望以较其它事件更高的优先级处理某些事件.正因为如此,libevent支持精确的优先级队列.优先级值较低活动事件总是比优先级值较高的活动事件. <br><br><br><br>不同的优先级别可以使用event_priority_init()函数来初始化.该函数必须在之前调用.event_priority_set()函数用于设置事件的优先级.默认情况下libevent把所有事件的优先级设置成中间值,除非它们的优先级被明确指定. <br><br><br><br>线程安全事件 <br><br>Libevent支持线程安全,当初始化事件库时调用event_init(),返回一个事件根基event base.这个事件根基可以被 event_base_set(),event_base_dispatch(), event_base_loop(), event_base_loopexit(),bufferevent_base_set() and event_base_free().等函数共同使用. <br><br>event_base_set()应该在event_set()初始化之后调用,因为函数event_set()对最近创建的事件根基赋值.在调用bufferevent_new()初始化缓存事件之后应该调用bufferevent_base_set().当不再需要事件根基时应该调用函数释放内存. <br><br><br><br>缓存事件 <br><br>libevent 提供正常事件回调的一个抽象.这个抽象叫缓存事件.缓存事件提供输入输出缓存自动写入和读出.使用缓存事件的程序员不再需要直接处理IO,而是通过读写输入输出缓存. <br><br><br><br>bufferevent_new()使用创建新的缓存事件. <br><br>(原型: <br><br>struct bufferevent * <br><br>bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, everrorcb, <br><br>void *cbarg); <br><br>) <br><br>int fd:要读写数据的文件描述符.访文件描述符不允许是管道 <br><br>接下来三个参数是回调:读写回调函数原型void (*cb)(struct bufferevent *bufev, void *arg) <br><br>错误处理回调函数原型:void (*cb)(struct bufferevent *bufev, short what, void *arg) <br><br>其中arg参数由cbarg指定.读写回调函数可以是NULL,错误处理回调函数必须指定. <br><br><br><br>一旦初始化,缓存事件结构体可以被bufferevent_enable(),bufferevent_disable()重复使用.标志参数可以是EV_READ 和EV_WRITE.当文件可读时缓存试图从文件描述符中读取数据然后调用回调函数.当缓存中的数据少于写的最低"水位线"时,写回调函数被调用.该最低"水位线"默认为0. <br><br><br><br>bufferevent_write()用于往文件中写入数据.该数据被自动释放到输出缓冲区,当文件可写时写入文件描述符.该函数成功返回0,失败返回-1. <br><br>bufferevent_read()用于读取输入缓冲区中的数据,返回读取的字节数. <br><br><br><br>如果使用多个事件根基,bufferevent_base_set()函数必须在延缓一次使能缓存事件时调用. <br><br><br><br>无阻塞HTTP支持 <br><br>libevent支持所有轻量级的HTTP层,可以且来作HTTP服务器也可以用来发HTTP请求. <br><br>HTTP服务器可以使用来创建calling evhttp_new(). <br><br>也可以用evhttp_bind_socket()绑定所有端口和地址.当HTTP服务器不再使用时,可以调用evhttp_free()释放. <br><br><br><br>要收到HTTP请求,用户应该注册一个HTTP服务器回调,可以用 evhttp_set_cb()来实现.该函数的第二个参数是回调函数注册的URI.相应的回调会收到一个evhttp_request的结构体对象,它包含请求的所有信息. <br><br><br><br>这里不有将所有函数调用一一介绍,请参考event.h查看API接口. <br><br>该文档不是不完整也不是权威著作.如果你对API操作有异意,请查看源码如何工作. <br><br><br><br>所有操作成功返回0,错误返回-1,错误代码在errno中. <br><br>LIBEVENT 函数 <br><br><br><br>struct event_base * <br><br>event_init(void); <br><br><br><br>int <br><br>event_dispatch(void); <br><br><br><br>int <br><br>event_loop(int flags); <br><br><br><br>int <br><br>event_loopexit(struct timeval *tv); <br><br><br><br>int <br><br>event_loopbreak(void); <br><br><br><br>void <br><br>event_set(struct event *ev, int fd, short event, <br><br>void (*fn)(int, short, void *), void *arg); <br><br><br><br>int <br><br>event_base_dispatch(struct event_base *base); <br><br><br><br>int <br><br>event_base_loop(struct event_base *base, int flags); <br><br><br><br>int <br><br>event_base_loopexit(struct event_base *base, struct timeval *tv); <br><br><br><br>int <br><br>event_base_loopbreak(struct event_base *base); <br><br><br><br>int <br><br>event_base_set(struct event_base *base, struct event *); <br><br><br><br>void <br><br>event_base_free(struct event_base *base); <br><br><br><br>int <br><br>event_add(struct event *ev, struct timeval *tv); <br><br><br><br>int <br><br>event_del(struct event *ev); <br><br><br><br>int <br><br>event_once(int fd, short event, void (*fn)(int, short, void *), <br><br>void *arg, struct timeval *tv); <br><br><br><br>int <br><br>event_base_once(struct event_base *base, int fd, short event, <br><br>void (*fn)(int, short, void *), void *arg, struct timeval *tv); <br><br><br><br>int <br><br>event_pending(struct event *ev, short event, struct timeval *tv); <br><br><br><br>int <br><br>event_initialized(struct event *ev); <br><br><br><br>int <br><br>event_priority_init(int npriorities); <br><br><br><br>int <br><br>event_priority_set(struct event *ev, int priority); <br><br><br><br>void <br><br>evtimer_set(struct event *ev, void (*fn)(int, short, void *), void *arg); <br><br><br><br>void <br><br>evtimer_add(struct event *ev, struct timeval *); <br><br><br><br>void <br><br>evtimer_del(struct event *ev); <br><br><br><br>int <br><br>evtimer_pending(struct event *ev, struct timeval *tv); <br><br><br><br>int <br><br>evtimer_initialized(struct event *ev); <br><br><br><br>void <br><br>signal_set(struct event *ev, int signal, void (*fn)(int, short, void *), <br><br>void *arg); <br><br><br><br>void <br><br>signal_add(struct event *ev, struct timeval *); <br><br><br><br>void <br><br>signal_del(struct event *ev); <br><br><br><br>int <br><br>signal_pending(struct event *ev, struct timeval *tv); <br><br><br><br>int <br><br>signal_initialized(struct event *ev); <br><br><br><br>struct bufferevent * <br><br>bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, everrorcb, <br><br>void *cbarg); <br><br><br><br>void <br><br>bufferevent_free(struct bufferevent *bufev); <br><br><br><br>int <br><br>bufferevent_write(struct bufferevent *bufev, void *data, size_t size); <br><br><br><br>int <br><br>bufferevent_write_buffer(struct bufferevent *bufev, <br><br>struct evbuffer *buf); <br><br><br><br>size_t <br><br>bufferevent_read(struct bufferevent *bufev, void *data, size_t size); <br><br><br><br>int <br><br>bufferevent_enable(struct bufferevent *bufev, short event); <br><br><br><br>int <br><br>bufferevent_disable(struct bufferevent *bufev, short event); <br><br><br><br>void <br><br>bufferevent_settimeout(struct bufferevent *bufev, int timeout_read, <br><br>int timeout_write); <br><br><br><br>int <br><br>bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); <br><br><br><br>struct evbuffer * <br><br>evbuffer_new(void); <br><br><br><br>void <br><br>evbuffer_free(struct evbuffer *buf); <br><br><br><br>int <br><br>evbuffer_add(struct evbuffer *buf, const void *data, size_t size); <br><br><br><br>int <br><br>evbuffer_add_buffer(struct evbuffer *dst, struct evbuffer *src); <br><br><br><br>int <br><br>evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...); <br><br><br><br>int <br><br>evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap); <br><br><br><br>void <br><br>evbuffer_drain(struct evbuffer *buf, size_t size); <br><br><br><br>int <br><br>evbuffer_write(struct evbuffer *buf, int fd); <br><br><br><br>int <br><br>evbuffer_read(struct evbuffer *buf, int fd, int size); <br><br><br><br>u_char * <br><br>evbuffer_find(struct evbuffer *buf, const u_char *data, size_t size); <br><br><br><br>char * <br><br>evbuffer_readline(struct evbuffer *buf); <br><br><br><br>struct evhttp * <br><br>evhttp_new(struct event_base *base); <br><br><br><br>int <br><br>evhttp_bind_socket(struct evhttp *http, const char *address, <br><br>u_short port); <br><br><br><br>void <br><br>evhttp_free(struct evhttp *http); <br><br><br><br>int (*event_sigcb)(void); <br>
<img src ="http://www.cppblog.com/decker502/aggbug/94238.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-08-24 12:15 <a href="http://www.cppblog.com/decker502/articles/94238.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sigslot介绍</title><link>http://www.cppblog.com/decker502/articles/93841.html</link><dc:creator>decker</dc:creator><author>decker</author><pubDate>Wed, 19 Aug 2009 09:19:00 GMT</pubDate><guid>http://www.cppblog.com/decker502/articles/93841.html</guid><wfw:comment>http://www.cppblog.com/decker502/comments/93841.html</wfw:comment><comments>http://www.cppblog.com/decker502/articles/93841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/decker502/comments/commentRss/93841.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/decker502/services/trackbacks/93841.html</trackback:ping><description><![CDATA[<p>最近在开发一个基于libjingle开源的IM系统，里面有一个其类为has_slots，搜索了一下其资料发现是一个很好用的C++库，先对其简单介绍一下。 <br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简介<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sigslot是一个线程安全、类型安全，用C++实现的sig/slot机制(sig/slot机制就是对象之间发送和接收消息的机制)的开源代码库。是一个非常好用的库,只有一个头文件sigslot.h。<br>2.&nbsp;&nbsp;&nbsp; Sigslot实例<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现代的C++项目通常包含大量的C++类和对象，对象之间通过成员函数调用，缺点是当类和对象规模很大时，相互之间必须记住对方提供了哪些接口，以及接口的详细信息，很不方便。<br>比如：我们有一个switch类和一个light类，而我们现在需要将两者关联起来，即通过switch控制light的状态，我们可能需要添加一个另外的类ToggleSwitch来将两者关联起来：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Switch<br><img id=Codehighlighter1_13_56_Open_Image onclick="this.style.display='none'; Codehighlighter1_13_56_Open_Text.style.display='none'; Codehighlighter1_13_56_Closed_Image.style.display='inline'; Codehighlighter1_13_56_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_13_56_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_13_56_Closed_Text.style.display='none'; Codehighlighter1_13_56_Open_Image.style.display='inline'; Codehighlighter1_13_56_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_13_56_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_13_56_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Clicked()&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Light<br><img id=Codehighlighter1_71_147_Open_Image onclick="this.style.display='none'; Codehighlighter1_71_147_Open_Text.style.display='none'; Codehighlighter1_71_147_Closed_Image.style.display='inline'; Codehighlighter1_71_147_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_71_147_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_71_147_Closed_Text.style.display='none'; Codehighlighter1_71_147_Open_Image.style.display='inline'; Codehighlighter1_71_147_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_71_147_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_71_147_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ToggleState();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOn();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOff();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ToggleSwitch&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Switch<br><img id=Codehighlighter1_185_312_Open_Image onclick="this.style.display='none'; Codehighlighter1_185_312_Open_Text.style.display='none'; Codehighlighter1_185_312_Closed_Image.style.display='inline'; Codehighlighter1_185_312_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_185_312_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_185_312_Closed_Text.style.display='none'; Codehighlighter1_185_312_Open_Image.style.display='inline'; Codehighlighter1_185_312_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_185_312_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_185_312_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_223_234_Open_Image onclick="this.style.display='none'; Codehighlighter1_223_234_Open_Text.style.display='none'; Codehighlighter1_223_234_Closed_Image.style.display='inline'; Codehighlighter1_223_234_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_223_234_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_223_234_Closed_Text.style.display='none'; Codehighlighter1_223_234_Open_Image.style.display='inline'; Codehighlighter1_223_234_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ToggleSwitch(Light</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;lp)</span><span id=Codehighlighter1_223_234_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_223_234_Open_Text><span style="COLOR: #000000">{m_lp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;lp;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_263_283_Open_Image onclick="this.style.display='none'; Codehighlighter1_263_283_Open_Text.style.display='none'; Codehighlighter1_263_283_Closed_Image.style.display='inline'; Codehighlighter1_263_283_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_263_283_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_263_283_Closed_Text.style.display='none'; Codehighlighter1_263_283_Open_Image.style.display='inline'; Codehighlighter1_263_283_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Clicked()</span><span id=Codehighlighter1_263_283_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_263_283_Open_Text><span style="COLOR: #000000">{m_lp.ToggleState();}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Light</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;m_lp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Light&nbsp;lp1,&nbsp;lp2;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>ToggleSwitch&nbsp;tsw1(lp1),&nbsp;tsw2(lp2);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>这在功能上完全可以实现，但想象一下如果大量的需要相互交互消息的类，那工作量就不是一般的大了。 <br>使用sig/slot机制来解决上述情况，不需要关心关联类的接口细节，sigslot实现的switch和light上述功能如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Switch<br><img id=Codehighlighter1_13_47_Open_Image onclick="this.style.display='none'; Codehighlighter1_13_47_Open_Text.style.display='none'; Codehighlighter1_13_47_Closed_Image.style.display='inline'; Codehighlighter1_13_47_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_13_47_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_13_47_Closed_Text.style.display='none'; Codehighlighter1_13_47_Open_Image.style.display='inline'; Codehighlighter1_13_47_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_13_47_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_13_47_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;signal0</span><span style="COLOR: #000000">&lt;&gt;</span><span style="COLOR: #000000">&nbsp;Clicked;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Light&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;has_slots</span><span style="COLOR: #000000">&lt;&gt;</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_83_159_Open_Image onclick="this.style.display='none'; Codehighlighter1_83_159_Open_Text.style.display='none'; Codehighlighter1_83_159_Closed_Image.style.display='inline'; Codehighlighter1_83_159_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_83_159_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_83_159_Closed_Text.style.display='none'; Codehighlighter1_83_159_Open_Image.style.display='inline'; Codehighlighter1_83_159_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_83_159_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_83_159_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ToggleState();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOn();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOff();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Switch&nbsp;sw1,&nbsp;sw2;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>Light&nbsp;lp1,&nbsp;lp2;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; Sigslot机制实现该功能与第一种方法相比，switch类多了个signal0成员，light类需要从has_slots&lt;&gt;继承，其他没有什么变化，但省去了编写继承类用来实现两者关联的ToggleSwitch。 <br>下面是实现功能的简单代码。<br>&nbsp;&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">iostream</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000">&nbsp;std;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">sigslot.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000">&nbsp;sigslot;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">必须加上sigslot的命名空间<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">在用vs调试时还需要将sigslot.h中很多的自定义模板结构类型前加typename</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;TRUE&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;FALSE&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Switch<br><img id=Codehighlighter1_209_274_Open_Image onclick="this.style.display='none'; Codehighlighter1_209_274_Open_Text.style.display='none'; Codehighlighter1_209_274_Closed_Image.style.display='inline'; Codehighlighter1_209_274_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_209_274_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_209_274_Closed_Text.style.display='none'; Codehighlighter1_209_274_Open_Image.style.display='inline'; Codehighlighter1_209_274_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_209_274_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_209_274_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;signal0</span><span style="COLOR: #000000">&lt;&gt;</span><span style="COLOR: #000000">&nbsp;Clicked;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">这里的信号是不带参数的，signaln表示带几个参数</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #000000">}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Light&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;has_slots</span><span style="COLOR: #000000">&lt;&gt;</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_310_654_Open_Image onclick="this.style.display='none'; Codehighlighter1_310_654_Open_Text.style.display='none'; Codehighlighter1_310_654_Closed_Image.style.display='inline'; Codehighlighter1_310_654_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_310_654_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_310_654_Closed_Text.style.display='none'; Codehighlighter1_310_654_Open_Image.style.display='inline'; Codehighlighter1_310_654_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_310_654_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_310_654_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_344_376_Open_Image onclick="this.style.display='none'; Codehighlighter1_344_376_Open_Text.style.display='none'; Codehighlighter1_344_376_Closed_Image.style.display='inline'; Codehighlighter1_344_376_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_344_376_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_344_376_Closed_Text.style.display='none'; Codehighlighter1_344_376_Open_Image.style.display='inline'; Codehighlighter1_344_376_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Light(</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;state)</span><span id=Codehighlighter1_344_376_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_344_376_Open_Text><span style="COLOR: #000000">{b_state&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;state;Displaystate();}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_403_438_Open_Image onclick="this.style.display='none'; Codehighlighter1_403_438_Open_Text.style.display='none'; Codehighlighter1_403_438_Closed_Image.style.display='inline'; Codehighlighter1_403_438_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_403_438_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_403_438_Closed_Text.style.display='none'; Codehighlighter1_403_438_Open_Image.style.display='inline'; Codehighlighter1_403_438_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ToggleState()</span><span id=Codehighlighter1_403_438_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_403_438_Open_Text><span style="COLOR: #000000">{b_state&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">b_state;Displaystate();}</span></span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">作为消息的响应</span><span style="COLOR: #008000"><br><img id=Codehighlighter1_470_501_Open_Image onclick="this.style.display='none'; Codehighlighter1_470_501_Open_Text.style.display='none'; Codehighlighter1_470_501_Closed_Image.style.display='inline'; Codehighlighter1_470_501_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_470_501_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_470_501_Closed_Text.style.display='none'; Codehighlighter1_470_501_Open_Image.style.display='inline'; Codehighlighter1_470_501_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOn()</span><span id=Codehighlighter1_470_501_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_470_501_Open_Text><span style="COLOR: #000000">{b_state&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;TRUE;Displaystate();}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_524_556_Open_Image onclick="this.style.display='none'; Codehighlighter1_524_556_Open_Text.style.display='none'; Codehighlighter1_524_556_Closed_Image.style.display='inline'; Codehighlighter1_524_556_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_524_556_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_524_556_Closed_Text.style.display='none'; Codehighlighter1_524_556_Open_Image.style.display='inline'; Codehighlighter1_524_556_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;TurnOff()</span><span id=Codehighlighter1_524_556_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_524_556_Open_Text><span style="COLOR: #000000">{b_state&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FALSE;Displaystate();}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_584_622_Open_Image onclick="this.style.display='none'; Codehighlighter1_584_622_Open_Text.style.display='none'; Codehighlighter1_584_622_Closed_Image.style.display='inline'; Codehighlighter1_584_622_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_584_622_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_584_622_Closed_Text.style.display='none'; Codehighlighter1_584_622_Open_Image.style.display='inline'; Codehighlighter1_584_622_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Displaystate()</span><span id=Codehighlighter1_584_622_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_584_622_Open_Text><span style="COLOR: #000000">{cout</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">The&nbsp;state&nbsp;is&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">b_state</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">endl;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;b_state;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;main()<br><img id=Codehighlighter1_669_1406_Open_Image onclick="this.style.display='none'; Codehighlighter1_669_1406_Open_Text.style.display='none'; Codehighlighter1_669_1406_Closed_Image.style.display='inline'; Codehighlighter1_669_1406_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_669_1406_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_669_1406_Closed_Text.style.display='none'; Codehighlighter1_669_1406_Open_Image.style.display='inline'; Codehighlighter1_669_1406_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_669_1406_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_669_1406_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Switch&nbsp;sw1,&nbsp;sw2,all_on,all_off;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Light&nbsp;lp1(TRUE),&nbsp;lp2(FALSE);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw1.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::ToggleState);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">绑定</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw2.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::ToggleState);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_on.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::TurnOn);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_on.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::TurnOn);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_off.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::TurnOff);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_off.Clicked.connect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2,</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">Light::TurnOff);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw1.Clicked();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw2.Clicked();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_on.Clicked();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_off.Clicked();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw1.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sw2.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_on.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_on.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_off.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp1);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;all_off.Clicked.disconnect(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lp2);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;<br>3&nbsp; 。&nbsp; 参数类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sig/slot可以带参数也可以不带，最多可以带8个参数。重新回顾上例，switch类的signal0&lt;&gt; Clicked,称之为sig，即用来发出信号；而继承has_slots&lt;&gt;的类light的成员函数void ToggleState() Turnon() Turnoff(),称之为slot,即信号的处理函数。 sigslot的核心就在这里，就是通过这两个建立对应关系来实现对象间的消息交互。<br>sig是一个成员变量，它形如<br>&nbsp;<br>signal+n&lt;type1,type2&#8230;&#8230;&gt; <br>&nbsp;&nbsp;&nbsp; 后面的n表示signal可以接收几个参数，类型任意，最多为8个。这是由库中指定的，当然如果实际开发需要更多的参数，可以修改sigslot库。<br>slot是一个成员函数，它形如：<br>void SlotFunction(type1,type2&#8230;&#8230;)<br>&nbsp;&nbsp; 需要记住：slot的类必须继承has_slots&lt;&gt;;成员函数的返回值必须为void类型，这是这个库的局限性，当然如果实际开发需要返回值，也是可以修改sigslot库来实现。此外还需要注意的是slot的原形需要与sig一致。怎么说呢，就是signal只能与带有与它相同参数个数的slot函数进行绑定，而且signal的参数是直接传递给slot的。<br>&nbsp;<br>4.&nbsp;&nbsp;&nbsp; Sigslot库用法<br>发送信号<br>信号(sig，即sig/slot的sig，下面提到的信号等同于此含义)：<br>signal1&lt;char *, int&gt; ReportError;<br>比如上面的一个ReportError这个信号，当调用ReportError("Something went wrong", ERR_SOMETHING_WRONG);时候，将自动调用ReportError的emit成员函数发出一个信号。发给谁呢？<br>&nbsp;<br>连接信息号<br>通过调用sig的connect函数建立sig和slot间的对应关系。Connect函数接收两个参数，一个是消息目的对象的地址(指针)，另一个是目的对象的成员函数指针(slot)。为了让整个机制有效运行，目的类必须从has_slots&lt;&gt;继承，并且sig/slot参数类型必须一致。也可以将一个sig连接到多个slot上，这样每次sig发出信号的时候，每个连接的slot都能收到该信号。<br>&nbsp;<br>断开信号连接<br>通过调用sig的disconnect函数断开sig和slot之间的连接，只有一个参数：目的对象的地址。一般不需要显式调用disconnect函数，在sig类和目的类(包含slot函数的类)析构函数中将自动调用disconnect断开sig和slot的连接。也可使用disconnect_all断开该sig的所有slot。<br>all_on.Clicked.connect(&amp;lp1,&amp;Light::TurnOn); <br>all_on.Clicked.connect(&amp;lp2,&amp;Light::TurnOn);//同上<br>all_on.Clicked.disconnect_all();</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/smallcraft/archive/2008/04/01/2237802.aspx">http://blog.csdn.net/smallcraft/archive/2008/04/01/2237802.aspx</a><br></p>
<img src ="http://www.cppblog.com/decker502/aggbug/93841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/decker502/" target="_blank">decker</a> 2009-08-19 17:19 <a href="http://www.cppblog.com/decker502/articles/93841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>