﻿<?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++博客-beautykingdom-随笔分类-Socket</title><link>http://www.cppblog.com/beautykingdom/category/12297.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 22 May 2012 06:54:34 GMT</lastBuildDate><pubDate>Tue, 22 May 2012 06:54:34 GMT</pubDate><ttl>60</ttl><item><title>Comparing Two High-Performance I/O Design Patterns&lt;forward&gt;</title><link>http://www.cppblog.com/beautykingdom/archive/2012/05/21/175576.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 21 May 2012 03:24:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2012/05/21/175576.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/175576.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2012/05/21/175576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/175576.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/175576.html</trackback:ping><description><![CDATA[<span style="font-size: 18px; color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; ">by Alexander Libman with Vladimir Gilbourd</span><br style="color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; " /><span style="font-size: 15px; color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; ">November 25, 2005</span>&nbsp;<br /><br /><div style="padding-left: 38px; padding-right: 38px; padding-bottom: 1em; color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; "><div style="font-size: 1.25em; ">Summary</div>This article investigates and compares different design patterns of high performance TCP-based servers. In addition to existing approaches, it proposes a scalable single-codebase, multi-platform solution (with code examples) and describes its fine-tuning on different platforms. It also compares performance of Java, C# and C++ implementations of proposed and existing solutions.</div><p style="color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; ">System I/O can be blocking, or non-blocking synchronous, or non-blocking asynchronous [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; ">1</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; ">2</a>]. Blocking I/O means that the calling system does not return control to the caller until the operation is finished. As a result, the caller is blocked and cannot perform other activities during that time. Most important, the caller thread cannot be reused for other request processing while waiting for the I/O to complete, and becomes a wasted resource during that time. For example, a<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>&nbsp;operation on a socket in blocking mode will not return control if the socket buffer is empty until some data becomes available.</p><p style="color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; ">By contrast, a non-blocking synchronous call returns control to the caller immediately. The caller is not made to wait, and the invoked system immediately returns one of two responses: If the call was executed and the results are ready, then the caller is told of that. Alternatively, the invoked system can tell the caller that the system has no resources (no data in the socket) to perform the requested action. In that case, it is the responsibility of the caller may repeat the call until it succeeds. For example, a&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>&nbsp;operation on a socket in non-blocking mode may return the number of read bytes or a special return code -1 with errno set to&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">EWOULBLOCK/EAGAIN</code>, meaning "not ready; try again later."</p><p style="color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; ">In a non-blocking asynchronous call, the calling function returns control to the caller immediately, reporting that the requested action was started. The calling system will execute the caller's request using additional system resources/threads and will notify the caller (by callback for example), when the result is ready for processing. For example, a Windows&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">ReadFile()</code>&nbsp;or POSIX&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>&nbsp;API returns immediately and initiates an internal system read operation. Of the three approaches, this non-blocking asynchronous<span style="font-family: Arial; font-size: 14pt; "> </span><span style="font-family: Arial; font-size: 12pt; ">approach offers the best scalability and performance.</span></p><p style="color: #212324; font-family: Arial, Helvetica, sans-serif; line-height: normal; background-color: #ffffff; font-size: medium; "><span style="font-family: Arial; font-size: 12pt; ">This article investigates different non-blocking I/O multiplexing mechanisms and proposes a single multi-platform design pattern/solution. We hope that this article will help developers of high performance TCP based servers to choose optimal design solution. We also compare the performance of Java, C# and C++ implementations of proposed and existing solutions. We will exclude the blocking approach from further discussion and </span>comparison at all, as it the least effective approach for scalability and performance.<br /></p><h1>Reactor and Proactor: two I/O multiplexing approaches</h1><p><span style="font-size: 12pt; ">In general, I/O</span><span style="font-size: 12pt; "> multiplexing mechanisms rely on an event demultiplexor [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">1</span></a><span style="font-size: 12pt; ">,&nbsp;</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">3</span></a><span style="font-size: 12pt; ">], an object that dispatches I/O events from a limited number of sources to the appropriate read/write event handlers. The developer registers interest in specific events and provides event handlers, or callbacks.</span><span style="font-size: 12pt; "> The event demultiplexor delivers the requested events to the event handlers.</span></p><p><span style="font-size: 12pt; ">Two patterns that involve event demultiplexors are called Reactor and Proactor [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">1</span></a><span style="font-size: 12pt; ">]. The Reactor patterns involve synchronous I/O, whereas the Proactor pattern involves asynchronous I/O. In Reactor, the event demultiplexor waits for events that indicate when a file descriptor or socket is ready for a read or write operation. The demultiplexor passes this event to the appropriate handler, which is responsible for performing the actual read or write.</span></p><p><span style="font-size: 12pt; ">In the Proactor pattern, by contrast, the handler&#8212;or the event demultiplexor on behalf of the handler&#8212;initiates asynchronous read and write operations. The I/O operation itself is performed by the operating system (OS). The parameters passed to the OS include the addresses of user-defined data buffers from which the OS gets data to write, or to which the OS puts data read. The event demultiplexor waits for events that indicate the completion of the I/O operation, and forwards those events to the appropriate handlers. For example, on Windows a handler could initiate async I/O (overlapped in Microsoft terminology) operations, and the event demultiplexor could wait for IOCompletion events [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">1</span></a><span style="font-size: 12pt; ">]. The implementation of this classic asynchronous pattern is based on an asynchronous OS-level API, and we will call this implementation the "system-level" or "true" async, because the application fully relies on the OS to execute actual I/O.</span></p><p><span style="font-size: 12pt; ">An example will help you understand the difference between Reactor and Proactor. We will focus on the read operation here, as the write implementation is similar. Here's a read in Reactor:</span></p><ul><li><span style="font-size: 12pt; ">An event handler declares interest in I/O events that indicate readiness for read on a particular socket</span></li><li><span style="font-size: 12pt; ">The event demultiplexor waits for events</span></li><li><span style="font-size: 12pt; ">An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the appropriate handler</span></li><li><span style="font-size: 12pt; ">The event handler performs the actual read operation, handles the data read, declares renewed interest in I/O events, and returns control to the dispatcher</span></li></ul><p><span style="font-size: 12pt; ">By comparison, here is a read operation in Proactor (true async):</span></p><ul><li><span style="font-size: 12pt; ">A handler initiates an asynchronous read operation (note: the OS must support asynchronous I/O). In this case, the handler does not care about I/O readiness events, but is instead registers interest in receiving completion events.</span></li><li><span style="font-size: 12pt; ">The event demultiplexor waits until the operation is completed</span></li><li><span style="font-size: 12pt; ">While the event demultiplexor waits, the OS executes the read operation in a parallel kernel thread, puts data into a user-defined buffer, and notifies the event demultiplexor that the read is complete</span></li><li><span style="font-size: 12pt; ">The event demultiplexor calls the appropriate handler;</span></li><li><span style="font-size: 12pt; ">The event handler handles the data from user defined buffer, starts a new asynchronous operation, and returns control to the event demultiplexor.</span></li></ul><h1>Current practice</h1><p><span style="font-size: 12pt; ">The open-source C++ development framework ACE [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">1</span></a><span style="font-size: 12pt; ">,&nbsp;</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">3</span></a><span style="font-size: 12pt; ">] developed by Douglas Schmidt, et al., offers a wide range of platform-independent, low-level concurrency support classes (threading, mutexes, etc). On the top level it provides two separate groups of classes: implementations of the ACE Reactor and ACE Proactor. Although both of them are based on platform-independent primitives, these tools offer different interfaces.</span></p><p><span style="font-size: 12pt; ">The ACE Proactor gives much better performance and robustness on MS-Windows, as Windows provides a very efficient async API, based on operating-system-level support [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">4</span></a><span style="font-size: 12pt; ">,&nbsp;</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">5</span></a><span style="font-size: 12pt; ">].</span></p><p><span style="font-size: 12pt; ">Unfortunately, not all operating systems provide full robust async OS-level support. For instance, many Unix systems do not. Therefore, ACE Reactor is a preferable solution in UNIX (currently UNIX does not have robust async facilities for sockets). As a result, to achieve the best performance on each system, developers of networked applications need to maintain two separate code-bases: an ACE Proactor based solution on Windows and an ACE Reactor based solution for Unix-based systems.</span></p><p><span style="font-size: 12pt; ">As we mentioned, the true async Proactor pattern requires operating-system-level support. Due to the differing nature of event handler and operating-system interaction, it is difficult to create common, unified external interfaces for both Reactor and Proactor patterns. That, in turn, makes it hard to create a fully portable development framework and encapsulate the interface and OS- related differences.</span></p><h1>Proposed solution</h1><p><span style="font-size: 12pt; ">In this section, we will propose a solution to the challenge of designing a portable framework for the Proactor and Reactor I/O patterns. To demonstrate this solution, we will transform a Reactor demultiplexor I/O solution to an emulated async I/O by moving read/write operations from event handlers inside the demultiplexor (this is "emulated async" approach). The following example illustrates that conversion for a read operation:</span></p><blockquote style="color: #212324; background-color: #ffffff; "><ul><li><span style="font-size: 12pt; ">An event handler declares interest in I/O events (readiness for read) and provides the demultiplexor with information such as the address of a data buffer, or the number of bytes to read.</span></li><li><span style="font-size: 12pt; ">Dispatcher waits for events (for example, on&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">);</span></li><li><span style="font-size: 12pt; ">When an event arrives, it awakes up the dispatcher. The dispatcher performs a non- blocking read operation (it has all necessary information to perform this operation) and on completion calls the appropriate handler.</span></li><li><span style="font-size: 12pt; ">The event handler handles data from the user-defined buffer, declares new interest, along with information about where to put the data buffer and the number bytes to read in I/O events. The event handler then returns control to the dispatcher.</span></li></ul></blockquote><p><span style="font-size: 12pt; ">As we can see, by adding functionality to the demultiplexor I/O pattern, we were able to convert the Reactor pattern to a Proactor pattern. In terms of the amount of work performed, this approach is exactly the same as the Reactor pattern. We simply shifted responsibilities between different actors. There is no performance degradation because the amount of work performed is still the same. The work was simply performed by different actors. The following lists of steps demonstrate that each approach performs an equal amount of work:</span></p><p><span style="font-size: 12pt; ">Standard/classic Reactor:</span></p><ul><li><span style="font-size: 12pt; ">Step 1) wait for event (Reactor job)</span></li><li><span style="font-size: 12pt; ">Step 2) dispatch "Ready-to-Read" event to user handler ( Reactor job)</span></li><li><span style="font-size: 12pt; ">Step 3) read data (user handler job)</span></li><li><span style="font-size: 12pt; ">Step 4) process data ( user handler job)</span></li></ul><p><span style="font-size: 12pt; ">Proposed emulated Proactor:</span></p><ul><li><span style="font-size: 12pt; ">Step 1) wait for event (Proactor job)</span></li><li><span style="font-size: 12pt; ">Step 2) read data (now Proactor job)</span></li><li><span style="font-size: 12pt; ">Step 3) dispatch "Read-Completed" event to user handler (Proactor job)</span></li><li><span style="font-size: 12pt; ">Step 4) process data (user handler job)</span></li></ul><p><span style="font-size: 12pt; ">With an operating system that does not provide an async I/O API, this approach allows us to hide the reactive nature of available socket APIs and to expose a fully proactive async interface. This allows us to create a fully portable platform-independent solution with a common external interface.</span></p><h1>TProactor</h1><p><span style="font-size: 12pt; ">The proposed solution (TProactor) was developed and implemented at Terabit P/L [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">6</span></a><span style="font-size: 12pt; ">]. The solution has two alternative implementations, one in C++ and one in Java. The C++ version was built using ACE cross-platform low-level primitives and has a common unified async proactive interface on all platforms.</span></p><p><span style="font-size: 12pt; ">The main TProactor components are the Engine and WaitStrategy interfaces. Engine manages the async operations lifecycle. WaitStrategy manages concurrency strategies. WaitStrategy depends on Engine and the two always work in pairs. Interfaces between Engine and WaitStrategy are strongly defined.</span></p><p><span style="font-size: 12pt; ">Engines and waiting strategies are implemented as pluggable class-drivers (for the full list of all implemented Engines and corresponding WaitStrategies, see Appendix 1). TProactor is a highly configurable solution. It internally implements three engines (POSIX AIO, SUN AIO and Emulated AIO) and hides six different waiting strategies, based on an asynchronous kernel API (for POSIX- this is not efficient right now due to internal POSIX AIO API problems) and synchronous Unix&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">,&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">poll()</span></code><span style="font-size: 12pt; ">, /dev/poll (Solaris 5.8+),&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">port_get</span></code><span style="font-size: 12pt; ">&nbsp;(Solaris 5.10), RealTime (RT) signals (Linux 2.4+), epoll (Linux 2.6), k-queue (FreeBSD) APIs. TProactor conforms to the standard ACE Proactor implementation interface. That makes it possible to develop a single cross-platform solution (POSIX/MS-WINDOWS) with a common (ACE Proactor) interface.</span></p><p><span style="font-size: 12pt; ">With a set of mutually interchangeable "lego-style" Engines and WaitStrategies, a developer can choose the appropriate internal mechanism (engine and waiting strategy) at run time by setting appropriate configuration parameters. These settings may be specified according to specific requirements, such as the number of connections, scalability, and the targeted OS. If the operating system supports async API, a developer may use the true async approach, otherwise the user can opt for an emulated async solutions built on different sync waiting strategies. All of those strategies are hidden behind an emulated async fa&#231;ade.</span></p><p><span style="font-size: 12pt; ">For an HTTP server running on Sun Solaris, for example, the /dev/poll or&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">port_get()</span></code><span style="font-size: 12pt; ">-based engines is the most suitable choice, able to serve huge number of connections, but for another UNIX solution with a limited number of connections but high throughput requirements, a</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">-based engine may be a better approach. Such flexibility cannot be achieved with a standard ACE Reactor/Proactor, due to inherent algorithmic problems of different wait strategies (see Appendix 2).</span></p><p><span style="font-size: 12pt; ">In terms of performance, our tests show that emulating from reactive to proactive does not impose any overhead&#8212;it can be faster, but not slower. According to our test results, the TProactor gives on average of up to 10-35 % better performance (measured in terms of both throughput and response times) than the reactive model in the standard ACE Reactor implementation on various UNIX/Linux platforms. On Windows it gives the same performance as standard ACE Proactor.</span></p><h1>Performance comparison (JAVA versus C++ versus C#).</h1><p><span style="font-size: 12pt; ">In addition to C++, as we also implemented TProactor in Java. As for JDK version 1.4, Java provides only the sync-based approach that is logically similar to C&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">&nbsp;[</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">7</span></a><span style="font-size: 12pt; ">,&nbsp;</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">8</span></a><span style="font-size: 12pt; ">]. Java TProactor is based on Java's non-blocking facilities (java.nio packages) logically similar to C++ TProactor with waiting strategy based on&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">.</span></p><p><span style="font-size: 12pt; ">Figures 1 and 2 chart the transfer rate in bits/sec versus the number of connections. These charts represent comparison results for a simple echo-server built on standard ACE Reactor, using RedHat Linux 9.0, TProactor C++ and Java (IBM 1.4JVM) on Microsoft's Windows and RedHat Linux9.0, and a C# echo-server running on the Windows operating system. Performance of native AIO APIs is represented by "Async"-marked curves; by emulated AIO (TProactor)&#8212;AsyncE curves; and by TP_Reactor&#8212;Synch curves. All implementations were bombarded by the same client application&#8212;a continuous stream of arbitrary fixed sized messages via N connections.</span></p><p><span style="font-size: 12pt; ">The full set of tests was performed on the same hardware. Tests on different machines proved that relative results are consistent.</span></p><div style="margin-top: 0.75em; text-align: center; "><img src="http://www.artima.com/articles/images/io_dp_fig_1.gif" alt="" /></div><div style="margin-top: 0.75em; margin-bottom: 0.75em; text-align: center; font-weight: bold; "><span style="font-size: 12pt; ">Figure 1. Windows XP/P4 2.6GHz HyperThreading/512 MB RAM.</span></div><div style="margin-top: 0.75em; text-align: center; "><img src="http://www.artima.com/articles/images/io_dp_fig_2.gif" alt="" /></div><div style="margin-top: 0.75em; margin-bottom: 0.75em; text-align: center; font-weight: bold; "><span style="font-size: 12pt; ">Figure 2. Linux RedHat 2.4.20-smp/P4 2.6GHz HyperThreading/512 MB RAM.</span></div><h1>User code example</h1><p><span style="font-size: 12pt; ">The following is the skeleton of a simple TProactor-based Java echo-server. In a nutshell, the developer only has to implement the two interfaces:&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">OpRead</span></code><span style="font-size: 12pt; ">&nbsp;with buffer where TProactor puts its read results, and&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">OpWrite</span></code><span style="font-size: 12pt; ">&nbsp;with a buffer from which TProactor takes data. The developer will also need to implement protocol-specific logic via providing callbacks&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">onReadCompleted()</span></code><span style="font-size: 12pt; ">&nbsp;and&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">onWriteCompleted()</span></code><span style="font-size: 12pt; ">&nbsp;in the&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">AsynchHandler</span></code><span style="font-size: 12pt; ">&nbsp;interface implementation. Those callbacks will be asynchronously called by TProactor on completion of read/write operations and executed on a thread pool space provided by TProactor (the developer doesn't need to write his own pool).</span></p><pre style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; padding-left: 38px; "><div><span style="font-size: 12px;">class EchoServerProtocol implements AsynchHandler</span></div><div><span style="font-size: 12px;">{</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; AsynchChannel achannel = null;</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; EchoServerProtocol(Demultiplexor m, SelectableChannel channel)</span><span style="font-size: 12px; "> throws Exception&nbsp;</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; {</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; &nbsp; &nbsp; this.achannel = new AsynchChannel( m, this, channel );</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; }</span></div><div><span style="font-size: 12px;"><br /></span></div><div><span style="font-size: 12px;">    public void start() throws Exception</span></div><div><span style="font-size: 12px;">    {</span></div><div><span style="font-size: 12px;">	// called after construction&nbsp;</span></div><div><span style="font-size: 12px;">	System.out.println( Thread.currentThread().getName() + ": EchoServer protocol started" );&nbsp;</span></div><div><span style="font-size: 12px;">&nbsp; &nbsp; &nbsp; &nbsp; achannel.read( buffer);</span></div><div><span style="font-size: 12px;">    }</span></div><div><span style="font-size: 12px;"><br /></span></div><div><span style="font-size: 12px;">    public void onReadCompleted( OpRead opRead ) throws Exception</span></div><div><span style="font-size: 12px;">    {</span></div><div><span style="font-size: 12px;">	if (opRead.getError() != null )</span></div><div><span style="font-size: 12px;">	{</span></div><div>&nbsp;&nbsp;&nbsp;<span style="font-size: 12px;">	// handle error, do clean-up if needed &nbsp;</span></div><div><span style="font-size: 12px;">		System.out.println( "EchoServer::readCompleted: " + opRead.getError().toString());</span></div><div><span style="font-size: 12px;">		achannel.close();</span></div><div><span style="font-size: 12px;">		return;</span></div><div><span style="font-size: 12px;">	}</span></div><div><span style="font-size: 12px; ">		</span></div><div><span style="font-size: 12px;">	if (opRead.getBytesCompleted () &lt;= 0)</span></div><div><span style="font-size: 12px;">	{</span></div><div><span style="font-size: 12px;">		System.out.println( "EchoServer::readCompleted: Peer closed " + opRead.getBytesCompleted();</span></div><div><span style="font-size: 12px;">		achannel.close();</span></div><div><span style="font-size: 12px;">		return;</span></div><div><span style="font-size: 12px;">	}</span></div><div><span style="font-size: 12px;"><br /></span></div><div><span style="font-size: 12px;">	ByteBuffer buffer = opRead.getBuffer();</span></div><div><span style="font-size: 12px;">	achannel.write(buffer);</span></div><div><span style="font-size: 12px;">}</span></div><div><span style="font-size: 12px; ">		</span></div><div><span style="font-size: 12px;">public void onWriteCompleted(OpWrite opWrite) throws Exception&nbsp;</span></div><div><span style="font-size: 12px;">{</span></div><div><span style="font-size: 12px;">// logically similar to onReadCompleted &nbsp; &nbsp; &nbsp; &nbsp; ... &nbsp; &nbsp;&nbsp;</span></div><div><span style="font-size: 12px;">}</span></div><div><span style="font-size: 12px;">};</span></div></pre><p><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">IOHandler</span></code><span style="font-size: 12pt; ">&nbsp;is a TProactor base class.&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">AsynchHandler</span></code><span style="font-size: 12pt; ">&nbsp;and Multiplexor, among other things, internally execute the wait strategy chosen by the developer.</span></p><h1>Conclusion</h1><p><span style="font-size: 12pt; ">TProactor provides a common, flexible, and configurable solution for multi-platform high- performance communications development. All of the problems and complexities mentioned in Appendix 2, are hidden from the developer.</span></p><p><span style="font-size: 12pt; ">It is clear from the charts that C++ is still the preferable approach for high performance communication solutions, but Java on Linux comes quite close. However, the overall Java performance was weakened by poor results on Windows. One reason for that may be that the Java 1.4 nio package is based on&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">-style API. � It is true, Java NIO package is kind of Reactor pattern based on&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">-style API (see [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">7</span></a><span style="font-size: 12pt; ">,&nbsp;</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">8</span></a><span style="font-size: 12pt; ">]). Java NIO allows to write your own&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">-style provider (equivalent of TProactor waiting strategies). Looking at Java NIO implementation for Windows (to do this enough to examine import symbols in jdk1.5.0\jre\bin\nio.dll), we can make a conclusion that Java NIO 1.4.2 and 1.5.0 for Windows is based on WSAEventSelect () API. That is better than&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">, but slower than IOCompletionPort�s for significant number of connections. . Should the 1.5 version of Java's nio be based on IOCompletionPorts, then that should improve performance. If Java NIO would use IOCompletionPorts, than conversion of Proactor pattern to Reactor pattern should be made inside nio.dll. Although such conversion is more complicated than Reactor- &gt;Proactor conversion, but it can be implemented in frames of Java NIO interfaces. (this the topic of next arcticle, but we can provide algorithm). At this time, no TProactor performance tests were done on JDK 1.5.</span></p><p><span style="font-size: 12pt; ">Note. All tests for Java are performed on "raw" buffers (java.nio.ByteBuffer) without data processing.</span></p><p><span style="font-size: 12pt; ">Taking into account the latest activities to develop robust AIO on Linux [</span><a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">9</span></a><span style="font-size: 12pt; ">], we can conclude that Linux Kernel API (io_xxxx set of system calls) should be more scalable in comparison with POSIX standard, but still not portable. In this case, TProactor with new Engine/Wait Strategy pair, based on native LINUX AIO can be easily implemented to overcome portability issues and to cover Linux native AIO with standard ACE Proactor interface.</span></p><h1>Appendix I</h1><p><span style="font-size: 12pt; ">Engines and waiting strategies implemented in TProactor</span></p><p>&nbsp;</p><center><table border="1"><tbody><tr bgcolor="#CCCCFF"><th>Engine Type</th><th>Wait Strategies</th><th>Operating System</th></tr><tr></tr><tr valign="top"><td>POSIX_AIO (true async)<br /><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_suspend()<br />Waiting for RT signal<br />Callback function</code></td><td>POSIX complained UNIX (not robust)<br />POSIX (not robust)<br />SGI IRIX, LINUX (not robust)</td></tr><tr valign="top"><td>SUN_AIO (true async)<br /><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_wait()</code></td><td>SUN (not robust)</td></tr><tr valign="top"><td>Emulated Async<br />Non-blocking&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code><br /><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">poll()</code><br />/dev/poll<br />Linux RT signals<br />Kqueue</td><td>generic POSIX<br />Mostly all POSIX implementations<br />SUN<br />Linux<br />FreeBSD<br /></td></tr></tbody></table></center><h1>Appendix II</h1><p><span style="font-size: 12pt; ">All sync waiting strategies can be divided into two groups:</span></p><ul><li><span style="font-size: 12pt; ">edge-triggered (e.g. Linux RT signals)&#8212;signal readiness only when socket became ready (changes state);</span></li><li><span style="font-size: 12pt; ">level-triggered (e.g.&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">select()</span></code><span style="font-size: 12pt; ">,&nbsp;</span><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; "><span style="font-size: 12pt; ">poll()</span></code><span style="font-size: 12pt; ">, /dev/poll)&#8212;readiness at any time.</span></li></ul><p><span style="font-size: 12pt; ">Let us describe some common logical problems for those groups:</span></p><ul><li><span style="font-size: 12pt; ">edge-triggered group: after executing I/O operation, the demultiplexing loop can lose the state of socket readiness. Example: the "read" handler did not read whole chunk of data, so the socket remains still ready for read. But the demultiplexor loop will not receive next notification.</span></li><li><span style="font-size: 12pt; ">level-triggered group: when demultiplexor loop detects readiness, it starts the write/read user defined handler. But before the start, it should remove socket descriptior from the set of monitored descriptors. Otherwise, the same event can be dispatched twice.</span></li><li><span style="font-size: 12pt; ">Obviously, solving these problems adds extra complexities to development. All these problems were resolved internally within TProactor and the developer should not worry about those details, while in the synch approach one needs to apply extra effort to resolve them.</span></li></ul><a name="resources"><h1>Resources</h1></a><p><span style="font-size: 12pt; ">[1] Douglas C. Schmidt, Stephen D. Huston "C++ Network Programming." 2002, Addison-Wesley ISBN 0-201-60464-7</span><br /></p><p><span style="font-size: 12pt; ">[2] W. Richard Stevens "UNIX Network Programming" vol. 1 and 2, 1999, Prentice Hill, ISBN 0-13- 490012-X&nbsp;</span><br /></p><p><span style="font-size: 12pt; ">[3] Douglas C. Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann "Pattern-Oriented Software Architecture: Patterns for Concurrent and Networked Objects, Volume 2" Wiley &amp; Sons, NY 2000</span><br /></p><p><span style="font-size: 12pt; ">[4] INFO: Socket Overlapped I/O Versus Blocking/Non-blocking Mode. Q181611. Microsoft Knowledge Base Articles.</span><br /></p><p><span style="font-size: 12pt; ">[5] Microsoft MSDN. I/O Completion Ports.</span><br /><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/i_o_completion_ports.asp" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://msdn.microsoft.com/library/default.asp?url=/library/en- us/fileio/fs/i_o_completion_ports.asp</span></a></p><p><span style="font-size: 12pt; ">[6] TProactor (ACE compatible Proactor).</span><br /><a href="http://www.artima.com/articles/www.terabit.com.au" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">www.terabit.com.au</span></a></p><p><span style="font-size: 12pt; ">[7] JavaDoc java.nio.channels</span><br /><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/package-summary.html" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/package-summary.html</span></a></p><p><span style="font-size: 12pt; ">[8] JavaDoc Java.nio.channels.spi Class SelectorProvider&nbsp;</span><br /><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/spi/SelectorProvider.html" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/spi/SelectorProvider.html</span></a></p><p><span style="font-size: 12pt; ">[9] Linux AIO development&nbsp;</span><br /><a href="http://lse.sourceforge.net/io/aio.html" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://lse.sourceforge.net/io/aio.html</span></a><span style="font-size: 12pt; ">, and</span><br /><a href="http://archive.linuxsymposium.org/ols2003/Proceedings/All-Reprints/Reprint-Pulavarty-OLS2003.pdf" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://archive.linuxsymposium.org/ols2003/Proceedings/All-Reprints/Reprint-Pulavarty-OLS2003.pdf</span></a></p><p><span style="font-size: 12pt; ">See Also:</span></p><p><span style="font-size: 12pt; ">Ian Barile "I/O Multiplexing &amp; Scalable Socket Servers", 2004 February, DDJ&nbsp;</span><br /></p><p><span style="font-size: 12pt; ">Further reading on event handling</span><br /><a href="http://www.cs.wustl.edu/~schmidt/ACE-papers.html" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">- http://www.cs.wustl.edu/~schmidt/ACE-papers.html</span></a></p><p><span style="font-size: 12pt; ">The Adaptive Communication Environment</span><br /><a href="http://www.cs.wustl.edu/~schmidt/ACE.html" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://www.cs.wustl.edu/~schmidt/ACE.html</span></a></p><p><span style="font-size: 12pt; ">Terabit Solutions</span><br /><a href="http://terabit.com.au/solutions.php" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">http://terabit.com.au/solutions.php</span></a></p><h1>About the authors</h1><p><span style="font-size: 12pt; ">Alex Libman has been programming for 15 years. During the past 5 years his main area of interest is pattern-oriented multiplatform networked programming using C++ and Java. He is big fan and contributor of ACE.</span></p><p><span style="font-size: 12pt; ">Vlad Gilbourd works as a computer consultant, but wishes to spend more time listening jazz :) As a hobby, he started and runs</span><a href="http://www.corporatenews.com.au/" style="color: #800080; text-decoration: none; "><span style="font-size: 12pt; ">www.corporatenews.com.au</span></a><span style="font-size: 12pt; ">&nbsp;website.</span></p><br /><br />from:<br /><a href="http://www.artima.com/articles/io_design_patterns.html">http://www.artima.com/articles/io_design_patterns.html</a> <p>&nbsp;</p><img src ="http://www.cppblog.com/beautykingdom/aggbug/175576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2012-05-21 11:24 <a href="http://www.cppblog.com/beautykingdom/archive/2012/05/21/175576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Comparing Two High-Performance I/O Design Patterns</title><link>http://www.cppblog.com/beautykingdom/archive/2010/09/08/126175.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 08 Sep 2010 09:20:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/09/08/126175.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/126175.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/09/08/126175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/126175.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/126175.html</trackback:ping><description><![CDATA[<div class="summary" style="padding-left: 39px; padding-right: 39px; padding-bottom: 1em; color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; "><div class="summarytitle" style="font-weight: normal; font-size: 1.25em; ">Summary</div>This article investigates and compares different design patterns of high performance TCP-based servers. In addition to existing approaches, it proposes a scalable single-codebase, multi-platform solution (with code examples) and describes its fine-tuning on different platforms. It also compares performance of Java, C# and C++ implementations of proposed and existing solutions.</div><p style="color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; ">System I/O can be blocking, or non-blocking synchronous, or non-blocking asynchronous [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">1</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">2</a>]. Blocking I/O means that the calling system does not return control to the caller until the operation is finished. As a result, the caller is blocked and cannot perform other activities during that time. Most important, the caller thread cannot be reused for other request processing while waiting for the I/O to complete, and becomes a wasted resource during that time. For example, a&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>&nbsp;operation on a socket in blocking mode will not return control if the socket buffer is empty until some data becomes available.</p><p style="color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; ">By contrast, a non-blocking synchronous call returns control to the caller immediately. The caller is not made to wait, and the invoked system immediately returns one of two responses: If the call was executed and the results are ready, then the caller is told of that. Alternatively, the invoked system can tell the caller that the system has no resources (no data in the socket) to perform the requested action. In that case, it is the responsibility of the caller may repeat the call until it succeeds. For example, a&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>&nbsp;operation on a socket in non-blocking mode may return the number of read bytes or a special return code -1 with errno set to&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">EWOULBLOCK/EAGAIN</code>, meaning "not ready; try again later."</p><p style="color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; ">In a non-blocking asynchronous call, the calling function returns control to the caller immediately, reporting that the requested action was started. The calling system will execute the caller's request using additional system resources/threads and will notify the caller (by callback for example), when the result is ready for processing. For example, a Windows&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">ReadFile()</code>&nbsp;or POSIX&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>&nbsp;API returns immediately and initiates an internal system read operation. Of the three approaches, this non-blocking asynchronous approach offers the best scalability and performance.</p><p style="color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; ">This article investigates different non-blocking I/O multiplexing mechanisms and proposes a single multi-platform design pattern/solution. We hope that this article will help developers of high performance TCP based servers to choose optimal design solution. We also compare the performance of Java, C# and C++ implementations of proposed and existing solutions. We will exclude the blocking approach from further discussion and comparison at all, as it the least effective approach for scalability and performance.</p><p style="color: rgb(33, 35, 36); font-family: Arial, Helvetica, sans-serif; "><p>In general, I/O multiplexing mechanisms rely on an event demultiplexor [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">1</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">3</a>], an object that dispatches I/O events from a limited number of sources to the appropriate read/write event handlers. The developer registers interest in specific events and provides event handlers, or callbacks. The event demultiplexor delivers the requested events to the event handlers.</p><p>Two patterns that involve event demultiplexors are called Reactor and Proactor [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">1</a>]. The Reactor patterns involve synchronous I/O, whereas the Proactor pattern involves asynchronous I/O. In Reactor, the event demultiplexor waits for events that indicate when a file descriptor or socket is ready for a read or write operation. The demultiplexor passes this event to the appropriate handler, which is responsible for performing the actual read or write.</p><p>In the Proactor pattern, by contrast, the handler—or the event demultiplexor on behalf of the handler—initiates asynchronous read and write operations. The I/O operation itself is performed by the operating system (OS). The parameters passed to the OS include the addresses of user-defined data buffers from which the OS gets data to write, or to which the OS puts data read. The event demultiplexor waits for events that indicate the completion of the I/O operation, and forwards those events to the appropriate handlers. For example, on Windows a handler could initiate async I/O (overlapped in Microsoft terminology) operations, and the event demultiplexor could wait for IOCompletion events [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">1</a>]. The implementation of this classic asynchronous pattern is based on an asynchronous OS-level API, and we will call this implementation the "system-level" or "true" async, because the application fully relies on the OS to execute actual I/O.</p><p>An example will help you understand the difference between Reactor and Proactor. We will focus on the read operation here, as the write implementation is similar. Here's a read in Reactor:</p><ul><li>An event handler declares interest in I/O events that indicate readiness for read on a particular socket</li><li>The event demultiplexor waits for events</li><li>An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the appropriate handler</li><li>The event handler performs the actual read operation, handles the data read, declares renewed interest in I/O events, and returns control to the dispatcher</li></ul><p>By comparison, here is a read operation in Proactor (true async):</p><ul><li>A handler initiates an asynchronous read operation (note: the OS must support asynchronous I/O). In this case, the handler does not care about I/O readiness events, but is instead registers interest in receiving completion events.</li><li>The event demultiplexor waits until the operation is completed</li><li>While the event demultiplexor waits, the OS executes the read operation in a parallel kernel thread, puts data into a user-defined buffer, and notifies the event demultiplexor that the read is complete</li><li>The event demultiplexor calls the appropriate handler;</li><li>The event handler handles the data from user defined buffer, starts a new asynchronous operation, and returns control to the event demultiplexor.</li></ul><h1 style="font-weight: normal; font-size: 28px; ">Current practice</h1><p>The open-source C++ development framework ACE [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">1</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">3</a>] developed by Douglas Schmidt, et al., offers a wide range of platform-independent, low-level concurrency support classes (threading, mutexes, etc). On the top level it provides two separate groups of classes: implementations of the ACE Reactor and ACE Proactor. Although both of them are based on platform-independent primitives, these tools offer different interfaces.</p><p>The ACE Proactor gives much better performance and robustness on MS-Windows, as Windows provides a very efficient async API, based on operating-system-level support [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">4</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">5</a>].</p><p>Unfortunately, not all operating systems provide full robust async OS-level support. For instance, many Unix systems do not. Therefore, ACE Reactor is a preferable solution in UNIX (currently UNIX does not have robust async facilities for sockets). As a result, to achieve the best performance on each system, developers of networked applications need to maintain two separate code-bases: an ACE Proactor based solution on Windows and an ACE Reactor based solution for Unix-based systems.</p><p>As we mentioned, the true async Proactor pattern requires operating-system-level support. Due to the differing nature of event handler and operating-system interaction, it is difficult to create common, unified external interfaces for both Reactor and Proactor patterns. That, in turn, makes it hard to create a fully portable development framework and encapsulate the interface and OS- related differences.</p><h1 style="font-weight: normal; font-size: 28px; ">Proposed solution</h1><p>In this section, we will propose a solution to the challenge of designing a portable framework for the Proactor and Reactor I/O patterns. To demonstrate this solution, we will transform a Reactor demultiplexor I/O solution to an emulated async I/O by moving read/write operations from event handlers inside the demultiplexor (this is "emulated async" approach). The following example illustrates that conversion for a read operation:</p><blockquote><ul><li>An event handler declares interest in I/O events (readiness for read) and provides the demultiplexor with information such as the address of a data buffer, or the number of bytes to read.</li><li>Dispatcher waits for events (for example, on&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>);</li><li>When an event arrives, it awakes up the dispatcher. The dispatcher performs a non- blocking read operation (it has all necessary information to perform this operation) and on completion calls the appropriate handler.</li><li>The event handler handles data from the user-defined buffer, declares new interest, along with information about where to put the data buffer and the number bytes to read in I/O events. The event handler then returns control to the dispatcher.</li></ul></blockquote><p>As we can see, by adding functionality to the demultiplexor I/O pattern, we were able to convert the Reactor pattern to a Proactor pattern. In terms of the amount of work performed, this approach is exactly the same as the Reactor pattern. We simply shifted responsibilities between different actors. There is no performance degradation because the amount of work performed is still the same. The work was simply performed by different actors. The following lists of steps demonstrate that each approach performs an equal amount of work:</p><p>Standard/classic Reactor:</p><ul><li>Step 1) wait for event (Reactor job)</li><li>Step 2) dispatch "Ready-to-Read" event to user handler ( Reactor job)</li><li>Step 3) read data (user handler job)</li><li>Step 4) process data ( user handler job)</li></ul><p>Proposed emulated Proactor:</p><ul><li>Step 1) wait for event (Proactor job)</li><li>Step 2) read data (now Proactor job)</li><li>Step 3) dispatch "Read-Completed" event to user handler (Proactor job)</li><li>Step 4) process data (user handler job)</li></ul><p>With an operating system that does not provide an async I/O API, this approach allows us to hide the reactive nature of available socket APIs and to expose a fully proactive async interface. This allows us to create a fully portable platform-independent solution with a common external interface.</p><p><h1 style="font-weight: normal; font-size: 28px; ">TProactor</h1><p>The proposed solution (TProactor) was developed and implemented at Terabit P/L [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">6</a>]. The solution has two alternative implementations, one in C++ and one in Java. The C++ version was built using ACE cross-platform low-level primitives and has a common unified async proactive interface on all platforms.</p><p>The main TProactor components are the Engine and WaitStrategy interfaces. Engine manages the async operations lifecycle. WaitStrategy manages concurrency strategies. WaitStrategy depends on Engine and the two always work in pairs. Interfaces between Engine and WaitStrategy are strongly defined.</p><p>Engines and waiting strategies are implemented as pluggable class-drivers (for the full list of all implemented Engines and corresponding WaitStrategies, see Appendix 1). TProactor is a highly configurable solution. It internally implements three engines (POSIX AIO, SUN AIO and Emulated AIO) and hides six different waiting strategies, based on an asynchronous kernel API (for POSIX- this is not efficient right now due to internal POSIX AIO API problems) and synchronous Unix&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>,&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">poll()</code>, /dev/poll (Solaris 5.8+),&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">port_get</code>&nbsp;(Solaris 5.10), RealTime (RT) signals (Linux 2.4+), epoll (Linux 2.6), k-queue (FreeBSD) APIs. TProactor conforms to the standard ACE Proactor implementation interface. That makes it possible to develop a single cross-platform solution (POSIX/MS-WINDOWS) with a common (ACE Proactor) interface.</p><p>With a set of mutually interchangeable "lego-style" Engines and WaitStrategies, a developer can choose the appropriate internal mechanism (engine and waiting strategy) at run time by setting appropriate configuration parameters. These settings may be specified according to specific requirements, such as the number of connections, scalability, and the targeted OS. If the operating system supports async API, a developer may use the true async approach, otherwise the user can opt for an emulated async solutions built on different sync waiting strategies. All of those strategies are hidden behind an emulated async fa&#231;ade.</p><p>For an HTTP server running on Sun Solaris, for example, the /dev/poll or&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">port_get()</code>-based engines is the most suitable choice, able to serve huge number of connections, but for another UNIX solution with a limited number of connections but high throughput requirements, a&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>-based engine may be a better approach. Such flexibility cannot be achieved with a standard ACE Reactor/Proactor, due to inherent algorithmic problems of different wait strategies (see Appendix 2).</p><p>In terms of performance, our tests show that emulating from reactive to proactive does not impose any overhead—it can be faster, but not slower. According to our test results, the TProactor gives on average of up to 10-35 % better performance (measured in terms of both throughput and response times) than the reactive model in the standard ACE Reactor implementation on various UNIX/Linux platforms. On Windows it gives the same performance as standard ACE Proactor.</p><h1 style="font-weight: normal; font-size: 28px; ">Performance comparison (JAVA versus C++ versus C#).</h1><p>In addition to C++, as we also implemented TProactor in Java. As for JDK version 1.4, Java provides only the sync-based approach that is logically similar to C&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>&nbsp;[<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">7</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">8</a>]. Java TProactor is based on Java's non-blocking facilities (java.nio packages) logically similar to C++ TProactor with waiting strategy based on&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>.</p><p>Figures 1 and 2 chart the transfer rate in bits/sec versus the number of connections. These charts represent comparison results for a simple echo-server built on standard ACE Reactor, using RedHat Linux 9.0, TProactor C++ and Java (IBM 1.4JVM) on Microsoft's Windows and RedHat Linux9.0, and a C# echo-server running on the Windows operating system. Performance of native AIO APIs is represented by "Async"-marked curves; by emulated AIO (TProactor)—AsyncE curves; and by TP_Reactor—Synch curves. All implementations were bombarded by the same client application—a continuous stream of arbitrary fixed sized messages via N connections.</p><p>The full set of tests was performed on the same hardware. Tests on different machines proved that relative results are consistent.</p><div class="figure" style="margin-top: 0.75em; text-align: center; "><img src="http://www.artima.com/articles/images/io_dp_fig_1.gif"></div><div class="figurecaption" style="margin-top: 0.75em; margin-bottom: 0.75em; text-align: center; font-weight: bold; ">Figure 1. Windows XP/P4 2.6GHz HyperThreading/512 MB RAM.</div><div class="figure" style="margin-top: 0.75em; text-align: center; "><img src="http://www.artima.com/articles/images/io_dp_fig_2.gif"></div><div class="figurecaption" style="margin-top: 0.75em; margin-bottom: 0.75em; text-align: center; font-weight: bold; ">Figure 2. Linux RedHat 2.4.20-smp/P4 2.6GHz HyperThreading/512 MB RAM.</div><h1 style="font-weight: normal; font-size: 28px; ">User code example</h1><p>The following is the skeleton of a simple TProactor-based Java echo-server. In a nutshell, the developer only has to implement the two interfaces:<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">OpRead</code>&nbsp;with buffer where TProactor puts its read results, and&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">OpWrite</code>&nbsp;with a buffer from which TProactor takes data. The developer will also need to implement protocol-specific logic via providing callbacks&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">onReadCompleted()</code>&nbsp;and&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">onWriteCompleted()</code>&nbsp;in the&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">AsynchHandler</code>interface implementation. Those callbacks will be asynchronously called by TProactor on completion of read/write operations and executed on a thread pool space provided by TProactor (the developer doesn't need to write his own pool).</p><pre class="indent" style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.88em; padding-left: 39px; ">class EchoServerProtocol implements AsynchHandler
{

    AsynchChannel achannel = null;

    EchoServerProtocol( Demultiplexor m,  SelectableChannel channel ) throws Exception
    {
        this.achannel = new AsynchChannel( m, this, channel );
    }

    public void start() throws Exception
    {
        // called after construction
        System.out.println( Thread.currentThread().getName() + ": EchoServer protocol started" );
        achannel.read( buffer);
    }

    public void onReadCompleted( OpRead opRead ) throws Exception
    {
        if ( opRead.getError() != null )
        {
            // handle error, do clean-up if needed
 System.out.println( "EchoServer::readCompleted: " + opRead.getError().toString());
            achannel.close();
            return;
        }

        if ( opRead.getBytesCompleted () &lt;= 0)
        {
            System.out.println( "EchoServer::readCompleted: Peer closed " + opRead.getBytesCompleted();
            achannel.close();
            return;
        }

        ByteBuffer buffer = opRead.getBuffer();

        achannel.write(buffer);
    }

    public void onWriteCompleted(OpWrite opWrite) throws Exception
    {
        // logically similar to onReadCompleted
        ...
    }
}
</pre><p><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">IOHandler</code>&nbsp;is a TProactor base class.&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">AsynchHandler</code>&nbsp;and Multiplexor, among other things, internally execute the wait strategy chosen by the developer.</p><h1 style="font-weight: normal; font-size: 28px; ">Conclusion</h1><p>TProactor provides a common, flexible, and configurable solution for multi-platform high- performance communications development. All of the problems and complexities mentioned in Appendix 2, are hidden from the developer.</p><p>It is clear from the charts that C++ is still the preferable approach for high performance communication solutions, but Java on Linux comes quite close. However, the overall Java performance was weakened by poor results on Windows. One reason for that may be that the Java 1.4 nio package is based on&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>-style API. � It is true, Java NIO package is kind of Reactor pattern based on&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>-style API (see [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">7</a>,&nbsp;<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">8</a>]). Java NIO allows to write your own&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>-style provider (equivalent of TProactor waiting strategies). Looking at Java NIO implementation for Windows (to do this enough to examine import symbols in jdk1.5.0\jre\bin\nio.dll), we can make a conclusion that Java NIO 1.4.2 and 1.5.0 for Windows is based on WSAEventSelect () API. That is better than&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>, but slower than IOCompletionPort�s for significant number of connections. . Should the 1.5 version of Java's nio be based on IOCompletionPorts, then that should improve performance. If Java NIO would use IOCompletionPorts, than conversion of Proactor pattern to Reactor pattern should be made inside nio.dll. Although such conversion is more complicated than Reactor- &gt;Proactor conversion, but it can be implemented in frames of Java NIO interfaces. (this the topic of next arcticle, but we can provide algorithm). At this time, no TProactor performance tests were done on JDK 1.5.</p><p>Note. All tests for Java are performed on "raw" buffers (java.nio.ByteBuffer) without data processing.</p><p>Taking into account the latest activities to develop robust AIO on Linux [<a href="http://www.artima.com/articles/io_design_patterns3.html#resources" style="color: rgb(0, 51, 153); text-decoration: none; ">9</a>], we can conclude that Linux Kernel API (io_xxxx set of system calls) should be more scalable in comparison with POSIX standard, but still not portable. In this case, TProactor with new Engine/Wait Strategy pair, based on native LINUX AIO can be easily implemented to overcome portability issues and to cover Linux native AIO with standard ACE Proactor interface.</p><h1 style="font-weight: normal; font-size: 28px; ">Appendix I</h1><p>Engines and waiting strategies implemented in TProactor</p><p>&#160;</p><center><table border="1"><tbody><tr bgcolor="#CCCCFF"><th>Engine Type</th><th>Wait Strategies</th><th>Operating System</th></tr><tr></tr><tr valign="top"><td>POSIX_AIO (true async)<br><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_suspend()<br>Waiting for RT signal<br>Callback function</code></td><td>POSIX complained UNIX (not robust)<br>POSIX (not robust)<br>SGI IRIX, LINUX (not robust)</td></tr><tr valign="top"><td>SUN_AIO (true async)<br><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">aio_wait()</code></td><td>SUN (not robust)</td></tr><tr valign="top"><td>Emulated Async<br>Non-blocking&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">read()</code>/<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">write()</code></td><td><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code><br><code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">poll()</code><br>/dev/poll<br>Linux RT signals<br>Kqueue</td><td>generic POSIX<br>Mostly all POSIX implementations<br>SUN<br>Linux<br>FreeBSD<br></td></tr></tbody></table></center><h1 style="font-weight: normal; font-size: 28px; ">Appendix II</h1><p>All sync waiting strategies can be divided into two groups:</p><ul><li>edge-triggered (e.g. Linux RT signals)—signal readiness only when socket became ready (changes state);</li><li>level-triggered (e.g.&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">select()</code>,&nbsp;<code style="font-family: 'Lucida Console', 'American Typewriter', 'Courier New', Courier, monospace; font-size: 0.95em; ">poll()</code>, /dev/poll)—readiness at any time.</li></ul><p>Let us describe some common logical problems for those groups:</p><ul><li>edge-triggered group: after executing I/O operation, the demultiplexing loop can lose the state of socket readiness. Example: the "read" handler did not read whole chunk of data, so the socket remains still ready for read. But the demultiplexor loop will not receive next notification.</li><li>level-triggered group: when demultiplexor loop detects readiness, it starts the write/read user defined handler. But before the start, it should remove socket descriptior from the set of monitored descriptors. Otherwise, the same event can be dispatched twice.</li><li>Obviously, solving these problems adds extra complexities to development. All these problems were resolved internally within TProactor and the developer should not worry about those details, while in the synch approach one needs to apply extra effort to resolve them.</li></ul><a name="resources"><h1 style="font-weight: normal; font-size: 28px; ">Resources</h1></a><p>[1] Douglas C. Schmidt, Stephen D. Huston "C++ Network Programming." 2002, Addison-Wesley ISBN 0-201-60464-7<br></p><p>[2] W. Richard Stevens "UNIX Network Programming" vol. 1 and 2, 1999, Prentice Hill, ISBN 0-13- 490012-X&nbsp;<br></p><p>[3] Douglas C. Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann "Pattern-Oriented Software Architecture: Patterns for Concurrent and Networked Objects, Volume 2" Wiley &amp; Sons, NY 2000<br></p><p>[4] INFO: Socket Overlapped I/O Versus Blocking/Non-blocking Mode. Q181611. Microsoft Knowledge Base Articles.<br></p><p>[5] Microsoft MSDN. I/O Completion Ports.<br><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/i_o_completion_ports.asp" style="color: rgb(0, 51, 153); text-decoration: none; ">http://msdn.microsoft.com/library/default.asp?url=/library/en- us/fileio/fs/i_o_completion_ports.asp</a></p><p>[6] TProactor (ACE compatible Proactor).<br><a href="http://www.artima.com/articles/www.terabit.com.au" style="color: rgb(0, 51, 153); text-decoration: none; ">www.terabit.com.au</a></p><p>[7] JavaDoc java.nio.channels<br><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/package-summary.html" style="color: rgb(0, 51, 153); text-decoration: none; ">http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/package-summary.html</a></p><p>[8] JavaDoc Java.nio.channels.spi Class SelectorProvider&nbsp;<br><a href="http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/spi/SelectorProvider.html" style="color: rgb(0, 51, 153); text-decoration: none; ">http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/spi/SelectorProvider.html</a></p><p>[9] Linux AIO development&nbsp;<br><a href="http://lse.sourceforge.net/io/aio.html" style="color: rgb(0, 51, 153); text-decoration: none; ">http://lse.sourceforge.net/io/aio.html</a>, and<br><a href="http://archive.linuxsymposium.org/ols2003/Proceedings/All-Reprints/Reprint-Pulavarty-OLS2003.pdf" style="color: rgb(0, 51, 153); text-decoration: none; ">http://archive.linuxsymposium.org/ols2003/Proceedings/All-Reprints/Reprint-Pulavarty-OLS2003.pdf</a></p><p>See Also:</p><p>Ian Barile "I/O Multiplexing &amp; Scalable Socket Servers", 2004 February, DDJ&nbsp;<br></p><p>Further reading on event handling<br><a href="http://www.cs.wustl.edu/~schmidt/ACE-papers.html" style="color: rgb(0, 51, 153); text-decoration: none; ">- http://www.cs.wustl.edu/~schmidt/ACE-papers.html</a></p><p>The Adaptive Communication Environment<br><a href="http://www.cs.wustl.edu/~schmidt/ACE.html" style="color: rgb(0, 51, 153); text-decoration: none; ">http://www.cs.wustl.edu/~schmidt/ACE.html</a></p><p>Terabit Solutions<br><a href="http://terabit.com.au/solutions.php" style="color: rgb(0, 51, 153); text-decoration: none; ">http://terabit.com.au/solutions.php</a></p><h1 style="font-weight: normal; font-size: 28px; ">About the authors</h1><p>Alex Libman has been programming for 15 years. During the past 5 years his main area of interest is pattern-oriented multiplatform networked programming using C++ and Java. He is big fan and contributor of ACE.</p><p>Vlad Gilbourd works as a computer consultant, but wishes to spend more time listening jazz :) As a hobby, he started and runs<a href="http://www.corporatenews.com.au/" style="color: rgb(0, 51, 153); text-decoration: none; ">www.corporatenews.com.au</a>&nbsp;website.</p></p><p>from:</p><p><a href="http://www.artima.com/articles/io_design_patterns.html">http://www.artima.com/articles/io_design_patterns.html</a></p></p>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/126175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-09-08 17:20 <a href="http://www.cppblog.com/beautykingdom/archive/2010/09/08/126175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下getsockopt/setsockopt 函数说明</title><link>http://www.cppblog.com/beautykingdom/archive/2010/09/06/125991.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 06 Sep 2010 03:17:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/09/06/125991.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/125991.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/09/06/125991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/125991.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/125991.html</trackback:ping><description><![CDATA[<div id="xspace-showmessage" class="xspace-itemmessage" style="word-break: break-all; margin-right: auto; margin-left: auto; margin-top: 0.5em; margin-bottom: 0.5em; width: 658px; overflow-x: auto; overflow-y: hidden; line-height: 1.8em; font-family: Arial, Helvetica, sans-serif; font-size: 12px; "><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; "><br>Linux</strong></u></a>下getsockopt/setsockopt&nbsp;<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">函数</strong></u></a>说明<br style="word-break: break-all; line-height: normal !important; ">【getsockopt/setsockopt系统调用】&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">功能描述：<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获取或者<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">设置</strong></u></a>与某个套接字关联的选 项。选项可能存在于多层<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">协议</strong></u></a>中，它们总会出现在最上面的套接字层。当操作套接字选项时，选项位于的层和选项的名称必须给出。为了操作套接字层的选项，应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项，控制选项的合适协议号必须给出。例如，为了表示一个选项由<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">TCP</strong></u></a>协议解析，层应该设定为协议 号TCP。</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">用法：<br style="word-break: break-all; line-height: normal !important; ">#include &lt;sys/types.h&gt;<br style="word-break: break-all; line-height: normal !important; ">#include &lt;sys/socket.h&gt;</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">int&nbsp;<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">getsockopt</strong></u></a>(int sock, int level, int optname, void *optval, socklen_t *optlen);</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">int&nbsp;<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">setsockopt</strong></u></a>(int sock, int level, int optname, const void *optval, socklen_t optlen);</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">参数：&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">sock：将要被设置或者获取选项的套接字。<br style="word-break: break-all; line-height: normal !important; ">level：选项所在的协议层。<br style="word-break: break-all; line-height: normal !important; ">optname：需要访问的选项名。<br style="word-break: break-all; line-height: normal !important; ">optval：对于getsockopt()，指向返回选项值的缓冲。对于setsockopt()，指向包含新选项值的缓冲。<br style="word-break: break-all; line-height: normal !important; ">optlen：对于getsockopt()，作为入口参数时，选项值的最大长度。作为出口参数时，选项值的实际长度。对于setsockopt()，现选项的长度。</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">返回说明：&nbsp;&nbsp;</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">成功执行时，返回0。失败返回-1，errno被设为以下的某个值&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">EBADF：sock不是有效的<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">文件</strong></u></a>描述词<br style="word-break: break-all; line-height: normal !important; ">EFAULT：optval指向的内存并非有效的进程<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">空间</strong></u></a><br style="word-break: break-all; line-height: normal !important; ">EINVAL：在调用setsockopt()时，optlen无效<br style="word-break: break-all; line-height: normal !important; ">ENOPROTOOPT：指定的协议层不能识别选项<br style="word-break: break-all; line-height: normal !important; ">ENOTSOCK：sock描述的不是套接字</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">参数详细说明：</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">level指定控制套接字的层次.可以取三种值:<br style="word-break: break-all; line-height: normal !important; ">1)SOL_SOCKET:通用套接字选项.<br style="word-break: break-all; line-height: normal !important; ">2)IPPROTO_IP:IP选项.<br style="word-break: break-all; line-height: normal !important; ">3)IPPROTO_TCP:TCP选项.　<br style="word-break: break-all; line-height: normal !important; ">optname指定控制的方式(选项的名称),我们下面详细解释　</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">optval获得或者是设置套接字选项.根据选项名称的<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">数据</strong></u></a>类型进行转换　</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">选项名称　　　　　　　　说明　　　　　　　　　　　　　　　　　　数据类型<br style="word-break: break-all; line-height: normal !important; ">========================================================================<br style="word-break: break-all; line-height: normal !important; ">　　　　　　　　　　　　SOL_SOCKET<br style="word-break: break-all; line-height: normal !important; ">------------------------------------------------------------------------<br style="word-break: break-all; line-height: normal !important; ">SO_BROADCAST　　　　　　允许发送广播数据　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_DEBUG　　　　　　　　允许调试　　　　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_DONTROUTE　　　　　　不查找<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">路由</strong></u></a>　　　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_ERROR　　　　　　　　获得套接字错误　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_KEEPALIVE　　　　　　保持连接　　　　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_LINGER　　　　　　　 延迟关闭连接　　　　　　　　　　　　　　struct linger<br style="word-break: break-all; line-height: normal !important; ">SO_OOBINLINE　　　　　　带外数据放入正常数据流　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_RCVBUF　　　　　　　 接收缓冲区大小　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_SNDBUF　　　　　　　 发送缓冲区大小　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_RCVLOWAT　　　　　　 接收缓冲区下限　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_SNDLOWAT　　　　　　 发送缓冲区下限　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_RCVTIMEO　　　　　　 接收超时　　　　　　　　　　　　　　　　struct timeval<br style="word-break: break-all; line-height: normal !important; ">SO_SNDTIMEO　　　　　　 发送超时　　　　　　　　　　　　　　　　struct timeval<br style="word-break: break-all; line-height: normal !important; ">SO_REUSERADDR　　　　　 允许重用本地地址和端口　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_TYPE　　　　　　　　 获得套接字类型　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">SO_BSDCOMPAT　　　　　　与BSD系统兼容　　　　　　　　　　　　　 int<br style="word-break: break-all; line-height: normal !important; ">========================================================================<br style="word-break: break-all; line-height: normal !important; ">　　　　　　　　　　　　IPPROTO_IP<br style="word-break: break-all; line-height: normal !important; ">------------------------------------------------------------------------<br style="word-break: break-all; line-height: normal !important; ">IP_HDRINCL　　　　　　　在数据包中包含IP首部　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">IP_OPTINOS　　　　　　　IP首部选项　　　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">IP_TOS　　　　　　　　　<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">服务</strong></u></a>类型<br style="word-break: break-all; line-height: normal !important; ">IP_TTL　　　　　　　　　生存时间　　　　　　　　　　　　　　　　int<br style="word-break: break-all; line-height: normal !important; ">========================================================================<br style="word-break: break-all; line-height: normal !important; ">　　　　　　　　　　　　IPPRO_TCP<br style="word-break: break-all; line-height: normal !important; ">------------------------------------------------------------------------<br style="word-break: break-all; line-height: normal !important; ">TCP_MAXSEG　　　　　　　TCP最大数据段的大小　　　　　　　　　　 int<br style="word-break: break-all; line-height: normal !important; ">TCP_NODELAY　　　　　　 不使用Nagle算法　　　　　　　　　　　　 int<br style="word-break: break-all; line-height: normal !important; ">========================================================================</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">返回说明：&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">成功执行时，返回0。失败返回-1，errno被设为以下的某个值&nbsp;&nbsp;<br style="word-break: break-all; line-height: normal !important; ">EBADF：sock不是有效的文件描述词<br style="word-break: break-all; line-height: normal !important; ">EFAULT：optval指向的内存并非有效的进程空间<br style="word-break: break-all; line-height: normal !important; ">EINVAL：在调用setsockopt()时，optlen无效<br style="word-break: break-all; line-height: normal !important; ">ENOPROTOOPT：指定的协议层不能识别选项<br style="word-break: break-all; line-height: normal !important; ">ENOTSOCK：sock描述的不是套接字</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">SO_RCVBUF和SO_SNDBUF每个套接口都有一个发送缓冲区和一个接收缓冲区，使用这两个套接口选项可以改变缺省缓冲区大小。</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">// 接收缓冲区<br style="word-break: break-all; line-height: normal !important; ">int nRecvBuf=32*1024;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置为32K<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&amp;nRecvBuf,sizeof(int));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">//发送缓冲区<br style="word-break: break-all; line-height: normal !important; ">int nSendBuf=32*1024;//设置为32K<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&amp;nSendBuf,sizeof(int));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">注意：</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当设置TCP套接口接收缓冲区的大小时，函数调用顺序是很重要的，因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户，O_RCVBUF选项必须在connect之前设置；对于<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">服务器</strong></u></a>，SO_RCVBUF选项必须在listen前设置。</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">结合<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">原理</strong></u></a>说明：</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">应用</strong></u></a>进程来读。 TCP：TCP通告另一端的窗口大小。 TCP套接口接收缓冲区不可能溢出，因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制，如果对方无视窗口大小而发出了超过窗口大小的数据，则接 收方TCP将丢弃它。 UDP：当接收到的数据报装不进套接口接收缓冲区时，此数据报就被丢弃。UDP是没有流量控制的；快的发送者可以很容易地就淹没慢的接收者，导致接收方的UDP丢弃数据报。<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.我们经常听说tcp协议的三次握手,但三次握手到底是什么，其细节是什么，为什么要这么做呢?<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一次:客户端发送连接请求给服务器，服务器接收;<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二次:服务器返回给客户端一个确认码,附带一个从服务器到客户端的连接请求,客户机接收,确认客户端到服务器的连接.<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第三次:客户机返回服务器上次发送请求的确认码,服务器接收,确认服务器到客户端的连接.<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以看到:<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. tcp的每个连接都需要确认.<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. 客户端到服务器和服务器到客户端的连接是独立的.<br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们再想想tcp协议的特点:连接的,可靠的,全双工的,实际上tcp的三次握手正是为了保证这些特性的实现.</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.setsockopt的用法</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">1.closesocket（一般不会立即关闭而经历TIME_WAIT的过程）后想继续重用该socket：<br style="word-break: break-all; line-height: normal !important; ">BOOL bReuseaddr=TRUE;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&amp;bReuseaddr,sizeof(BOOL));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭，不经历TIME_WAIT的过程：<br style="word-break: break-all; line-height: normal !important; ">BOOL bDontLinger = FALSE;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&amp;bDontLinger,sizeof(BOOL));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">3.在send(),recv()过程中有时由于<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">网络</strong></u></a>状况等原因，发收不能预期进行,而设置收发时限：<br style="word-break: break-all; line-height: normal !important; ">int nNetTimeout=1000;//1秒<br style="word-break: break-all; line-height: normal !important; ">//发送时限<br style="word-break: break-all; line-height: normal !important; ">setsockopt(socket，SOL_S0CKET,SO_SNDTIMEO，(char *)&amp;nNetTimeout,sizeof(int));<br style="word-break: break-all; line-height: normal !important; ">//接收时限<br style="word-break: break-all; line-height: normal !important; ">setsockopt(socket，SOL_S0CKET,SO_RCVTIMEO，(char *)&amp;nNetTimeout,sizeof(int));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">4.在send()的时候，返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节<br style="word-break: break-all; line-height: normal !important; ">(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K)；在实际的过程中发送数据<br style="word-break: break-all; line-height: normal !important; ">和接收数据量比较大，可以设置socket缓冲区，而避免了send(),recv()不断的循环收发：<br style="word-break: break-all; line-height: normal !important; ">// 接收缓冲区<br style="word-break: break-all; line-height: normal !important; ">int nRecvBuf=32*1024;//设置为32K<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&amp;nRecvBuf,sizeof(int));<br style="word-break: break-all; line-height: normal !important; ">//发送缓冲区<br style="word-break: break-all; line-height: normal !important; ">int nSendBuf=32*1024;//设置为32K<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&amp;nSendBuf,sizeof(int));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">5. 如果在发送数据的时，希望不经历由<a href="http://hi.chinaunix.net/?uid-21832962-action-viewspace-itemid-40517" target="_self" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; "><u style="word-break: break-all; line-height: normal !important; "><strong style="word-break: break-all; line-height: normal !important; ">系统</strong></u></a>缓冲区到socket缓冲区的拷贝而影响<br style="word-break: break-all; line-height: normal !important; ">程序的性能：<br style="word-break: break-all; line-height: normal !important; ">int nZero=0;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(socket，SOL_S0CKET,SO_SNDBUF，(char *)&amp;nZero,sizeof(nZero));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区)：<br style="word-break: break-all; line-height: normal !important; ">int nZero=0;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(socket，SOL_S0CKET,SO_RCVBUF，(char *)&amp;nZero,sizeof(int));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">7.一般在发送UDP数据报的时候，希望该socket发送的数据具有广播特性：<br style="word-break: break-all; line-height: normal !important; ">BOOL bBroadcast=TRUE;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&amp;bBroadcast,sizeof(BOOL));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">8.在client连接服务器过程中，如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用，在阻塞的函数调用中作用不大)<br style="word-break: break-all; line-height: normal !important; ">BOOL bConditionalAccept=TRUE;<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&amp;bConditionalAccept,sizeof(BOOL));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; "><br style="word-break: break-all; line-height: normal !important; ">9.如果在发送数据的过程中(send()没有完成，还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了，如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)？<br style="word-break: break-all; line-height: normal !important; ">struct linger {<br style="word-break: break-all; line-height: normal !important; ">u_short l_onoff;<br style="word-break: break-all; line-height: normal !important; ">u_short l_linger;<br style="word-break: break-all; line-height: normal !important; ">};<br style="word-break: break-all; line-height: normal !important; ">linger m_sLinger;<br style="word-break: break-all; line-height: normal !important; ">m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)<br style="word-break: break-all; line-height: normal !important; ">// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;<br style="word-break: break-all; line-height: normal !important; ">m_sLinger.l_linger=5;//(容许逗留的时间为5秒)<br style="word-break: break-all; line-height: normal !important; ">setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&amp;m_sLinger,sizeof(linger));</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">&nbsp;</p><p style="word-break: break-all; line-height: 1.8em !important; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; ">转载出处：<a href="http://blog.csdn.net/chinafe/archive/2008/12/15/3517537.aspx" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; ">http://blog.csdn.net/chinafe/archive/2008/12/15/3517537.aspx</a><br style="word-break: break-all; line-height: normal !important; ">转载出处：<a href="http://blog.csdn.net/xioahw/archive/2009/04/08/4056514.aspx" style="word-break: break-all; text-decoration: underline; color: rgb(101, 109, 119); line-height: normal !important; ">http://blog.csdn.net/xioahw/archive/2009/04/08/4056514.aspx</a></p></div>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/125991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-09-06 11:17 <a href="http://www.cppblog.com/beautykingdom/archive/2010/09/06/125991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个基于完成端口的TCP Server Framework,浅析IOCP</title><link>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 25 Aug 2010 12:42:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/124731.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/124731.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/124731.html</trackback:ping><description><![CDATA[<span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">如果你不投递（POST）Overlapped&nbsp;I/O，那么I/O Completion&nbsp;Ports&nbsp;只能为你提供一个Queue.&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp; CreateIoCompletionPort的NumberOfConcurrentThreads：</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">1.只有当第二个参数ExistingCompletionPort为NULL时它才有效，它是个max threads limits.</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">2.大家有谁把它设置为超出cpu个数的值，当然不只是cpu个数的2倍，而是下面的MAX_THREADS 100甚至更大。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">对于这个值的设定，msdn并没有说非得设成cpu个数的2倍，而且也没有把减少线程之间上下文交换这些影响扯到这里来。I/O Completion Ports MSDN:"If your transaction required a</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>lengthy computation</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">, a</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>larger</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>concurrency value</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">will allow more threads to run. Each completion packet may take longer to finish,</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>but more completion packets will be processed at the same time</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">. "。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp; 对于struct OVERLAPPED，我们常会如下扩展，</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct {</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WSAOVERLAPPED</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>overlapped</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">; //</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><span style="line-height: 18px; color: rgb(255, 0, 0); ">must be first member</span>?</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;是的，必须是第一个。如果你不肯定，你可以试试。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; SOCKET client_s;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; SOCKADDR_IN client_addr;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WORD</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>optCode</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">;//</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>1--read,2--send.</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; 有人常会定义这个数据成员，但也有人不用，争议在send/WSASend,此时的同步和异步是否有必要？&nbsp;至少我下面的server更本就没用它。</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; char buf[MAX_BUF_SIZE];</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; WSABUF</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>wsaBuf</strong></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">;//</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><span style="line-height: 18px; color: rgb(255, 0, 0); "><strong>inited ?</strong></span></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; 这个不要忘了！</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; DWORD numberOfBytesTransferred;</span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><br></span><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp; DWORD flags;&nbsp;&nbsp;&nbsp;</span><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">}QSSOverlapped;//<strong>for per connection<br></strong>我下面的server框架的基本思想是:<br>One connection&nbsp;VS one thread in worker thread pool&nbsp;,worker thread performs completionWorkerRoutine.<br>A&nbsp;Acceptor thread 专门用来accept socket,关联至IOCP,并WSARecv:post Recv Completion Packet to IOCP.<br>在completionWorkerRoutine中有以下的职责:<br>1.handle request,当忙时增加completionWorkerThread数量但不超过maxThreads,post Recv Completion Packet to IOCP.<br>2.timeout时检查是否空闲和当前completionWorkerThread数量,当空闲时保持或减少至minThreads数量.<br>3.对所有Accepted-socket管理生命周期,这里利用系统的keepalive probes,若想实现业务层"心跳探测"只需将QSS_SIO_KEEPALIVE_VALS_TIMEOUT 改回系统默认的2小时.<br><strong>下面结合源代码,浅析一下IOCP</strong>:<br><strong>socketserver.h<br></strong>#ifndef __Q_SOCKET_SERVER__<br>#define __Q_SOCKET_SERVER__<br>#include &lt;winsock2.h&gt;<br>#include &lt;mstcpip.h&gt;<br>#define QSS_SIO_KEEPALIVE_VALS_TIMEOUT 30*60*1000<br>#define QSS_SIO_KEEPALIVE_VALS_INTERVAL 5*1000</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#define MAX_THREADS 100<br>#define MAX_THREADS_MIN&nbsp; 10<br>#define MIN_WORKER_WAIT_TIMEOUT&nbsp; 20*1000<br>#define MAX_WORKER_WAIT_TIMEOUT&nbsp; 60*MIN_WORKER_WAIT_TIMEOUT</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#define MAX_BUF_SIZE 1024<br><br>/*当Accepted socket和socket关闭或发生异常时回调CSocketLifecycleCallback*/<br>typedef void (*CSocketLifecycleCallback)(SOCKET cs,int lifecycle);//lifecycle:0:OnAccepted,-1:OnClose//注意OnClose此时的socket未必可用,可能已经被非正常关闭或其他异常.<br><br>/*协议处理回调*/<br>typedef int (*InternalProtocolHandler)(LPWSAOVERLAPPED overlapped);//return -1:SOCKET_ERROR</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct Q_SOCKET_SERVER SocketServer;<br>DWORD initializeSocketServer(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout);<br>DWORD startSocketServer(SocketServer *ss);<br>DWORD shutdownSocketServer(SocketServer *ss);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">#endif<br>&nbsp;<strong>qsocketserver.c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简称 qss,相应的OVERLAPPED简称qssOl.<br></strong>#include "socketserver.h"<br>#include "stdio.h"<br>typedef struct {&nbsp;&nbsp;<br>&nbsp; WORD&nbsp;<strong>passive</strong>;//<strong>daemon</strong><br>&nbsp; WORD port;<br>&nbsp; WORD minThreads;<br>&nbsp; WORD maxThreads;<br>&nbsp; volatile long&nbsp;<strong>lifecycleStatus</strong>;//0-created,1-starting, 2-running,3-stopping,4-exitKeyPosted,5-stopped&nbsp;<br>&nbsp; long&nbsp; workerWaitTimeout;//wait timeout&nbsp;&nbsp;<br>&nbsp; CRITICAL_SECTION QSS_LOCK;<br>&nbsp; volatile long&nbsp;<strong>workerCounter</strong>;<br>&nbsp; volatile long&nbsp;<strong>currentBusyWorkers</strong>;<br>&nbsp; volatile long&nbsp;<strong>CSocketsCounter</strong>;//<strong>Accepted-socket引用计数<br></strong>&nbsp; CSocketLifecycleCallback cslifecb;<br>&nbsp; InternalProtocolHandler protoHandler;<br>&nbsp; WORD wsaVersion;//=MAKEWORD(2,0);<br>&nbsp; WSADATA wsData;<br>&nbsp; SOCKET server_s;<br>&nbsp; SOCKADDR_IN serv_addr;<br>&nbsp; HANDLE iocpHandle;<br>}QSocketServer;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct {<br>&nbsp; WSAOVERLAPPED overlapped;&nbsp;&nbsp;<br>&nbsp; SOCKET client_s;<br>&nbsp; SOCKADDR_IN client_addr;<br>&nbsp; WORD optCode;<br>&nbsp; char buf[MAX_BUF_SIZE];<br>&nbsp; WSABUF wsaBuf;<br>&nbsp; DWORD numberOfBytesTransferred;<br>&nbsp; DWORD flags;<br>}QSSOverlapped;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>acceptorRoutine</strong>(LPVOID);<br>DWORD&nbsp;&nbsp;<strong>completionWorkerRoutine</strong>(LPVOID);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static void adjustQSSWorkerLimits(QSocketServer *qss){<br>&nbsp;&nbsp;/*adjust size and timeout.*/<br>&nbsp;&nbsp;/*if(qss-&gt;maxThreads &lt;= 0) {<br>&nbsp;&nbsp;&nbsp;qss-&gt;maxThreads = MAX_THREADS;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (qss-&gt;maxThreads &lt; MAX_THREADS_MIN) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;maxThreads = MAX_THREADS_MIN;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;minThreads &gt;&nbsp; qss-&gt;maxThreads) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads =&nbsp; qss-&gt;maxThreads;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;minThreads &lt;= 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(1 == qss-&gt;maxThreads) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;minThreads = qss-&gt;maxThreads/2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;workerWaitTimeout&lt;MIN_WORKER_WAIT_TIMEOUT)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;workerWaitTimeout=MIN_WORKER_WAIT_TIMEOUT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(qss-&gt;workerWaitTimeout&gt;MAX_WORKER_WAIT_TIMEOUT)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;qss-&gt;workerWaitTimeout=MAX_WORKER_WAIT_TIMEOUT;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">typedef struct{<br>&nbsp;QSocketServer * qss;<br>&nbsp;HANDLE th;<br>}QSSWORKER_PARAM;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static WORD addQSSWorker(QSocketServer *qss,WORD addCounter){<br>&nbsp;WORD res=0;<br>&nbsp;if(qss-&gt;workerCounter&lt;qss-&gt;minThreads||(qss-&gt;currentBusyWorkers==qss-&gt;workerCounter&amp;&amp;qss-&gt;workerCounter&lt;qss-&gt;maxThreads)){<br>&nbsp;&nbsp;DWORD threadId;<br>&nbsp;&nbsp;QSSWORKER_PARAM * pParam=NULL;<br>&nbsp;&nbsp;int i=0;&nbsp;&nbsp;<br>&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(qss-&gt;workerCounter+addCounter&lt;=qss-&gt;maxThreads)<br>&nbsp;&nbsp;&nbsp;for(;i&lt;addCounter;i++)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pParam=malloc(sizeof(QSSWORKER_PARAM));<br>&nbsp;&nbsp;&nbsp;&nbsp;if(pParam){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParam-&gt;th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)completionWorkerRoutine,pParam,CREATE_SUSPENDED,&amp;threadId);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParam-&gt;qss=qss;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResumeThread(pParam-&gt;th);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;workerCounter++,res++;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;}&nbsp;&nbsp;<br>&nbsp;return res;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static void SOlogger(const char * msg,SOCKET s,int clearup){<br>&nbsp;perror(msg);<br>&nbsp;if(s&gt;0)<br>&nbsp;closesocket(s);<br>&nbsp;if(clearup)<br>&nbsp;WSACleanup();<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static int _InternalEchoProtocolHandler(LPWSAOVERLAPPED overlapped){<br>&nbsp;QSSOverlapped *qssOl=(QSSOverlapped *)overlapped;<br>&nbsp;<br>&nbsp;printf("numOfT:%d,WSARecvd:%s,\n",qssOl-&gt;numberOfBytesTransferred,qssOl-&gt;buf);<br>&nbsp;//Sleep(500);&nbsp;<br>&nbsp;return send(qssOl-&gt;client_s,qssOl-&gt;buf,qssOl-&gt;numberOfBytesTransferred,0);<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>initializeSocketServer</strong>(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout){<br>&nbsp;QSocketServer * qss=malloc(sizeof(QSocketServer));<br>&nbsp;qss-&gt;passive=passive&gt;0?1:0;<br>&nbsp;qss-&gt;port=port;<br>&nbsp;qss-&gt;minThreads=minThreads;<br>&nbsp;qss-&gt;maxThreads=maxThreads;<br>&nbsp;qss-&gt;workerWaitTimeout=workerWaitTimeout;<br>&nbsp;qss-&gt;wsaVersion=MAKEWORD(2,0);&nbsp;<br>&nbsp;qss-&gt;lifecycleStatus=0;<br>&nbsp;InitializeCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;qss-&gt;workerCounter=0;<br>&nbsp;qss-&gt;currentBusyWorkers=0;<br>&nbsp;qss-&gt;CSocketsCounter=0;<br>&nbsp;qss-&gt;cslifecb=cslifecb,qss-&gt;protoHandler=protoHandler;<br>&nbsp;if(!qss-&gt;protoHandler)<br>&nbsp;&nbsp;qss-&gt;protoHandler=_InternalEchoProtocolHandler;&nbsp;<br>&nbsp;adjustQSSWorkerLimits(qss);<br>&nbsp;*ssp=(SocketServer *)qss;<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>startSocketServer</strong>(SocketServer *ss){&nbsp;<br>&nbsp;QSocketServer * qss=(QSocketServer *)ss;<br>&nbsp;if(qss==NULL||InterlockedCompareExchange(&amp;qss-&gt;lifecycleStatus,1,0))<br>&nbsp;&nbsp;return 0;&nbsp;<br>&nbsp;qss-&gt;serv_addr.sin_family=AF_INET;<br>&nbsp;qss-&gt;serv_addr.sin_port=htons(qss-&gt;port);<br>&nbsp;qss-&gt;serv_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("127.0.0.1");<br>&nbsp;if(WSAStartup(qss-&gt;wsaVersion,&amp;qss-&gt;wsData)){&nbsp;&nbsp;<br>&nbsp; /*<strong>这里还有个插曲就是这个WSAStartup被调用的时候,它居然会启动一条额外的线程,当然稍后这条线程会自动退出的</strong>.不知<strong>WSAClearup</strong>又会如何?......*/</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;SOlogger("WSAStartup failed.\n",0,0);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;qss-&gt;server_s=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);<br>&nbsp;if(qss-&gt;server_s==INVALID_SOCKET){&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("socket failed.\n",0,1);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;if(bind(qss-&gt;server_s,(LPSOCKADDR)&amp;qss-&gt;serv_addr,sizeof(SOCKADDR_IN))==SOCKET_ERROR){&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("bind failed.\n",qss-&gt;server_s,1);<br>&nbsp;&nbsp;return 0;<br>&nbsp;}<br>&nbsp;if(<strong>listen</strong>(qss-&gt;server_s,<strong>SOMAXCONN</strong>)==SOCKET_ERROR)/*这里来谈谈<strong>backlog</strong>,很多人不知道设成何值,我见到过1,5,50,100的,有人说设定的越大越耗资源,的确,这里设成SOMAXCONN不代表windows会真的使用SOMAXCONN,而是" If set to SOMAXCONN, the underlying service provider responsible for socket&nbsp;<em>s</em>&nbsp;will set the backlog to a maximum&nbsp;<strong>reasonable</strong>&nbsp;value. "，同时在现实环境中，不同操作系统支持TCP缓冲队列有所不同，所以还不如让操作系统来决定它的值。像Apache这种服务器：<br>#ifndef DEFAULT_LISTENBACKLOG<br>#define DEFAULT_LISTENBACKLOG 511<br>#endif<br>*/<br>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;SOlogger("listen failed.\n",qss-&gt;server_s,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;qss-&gt;iocpHandle=<strong>CreateIoCompletionPort</strong>(<strong>INVALID_HANDLE_VALUE</strong>,NULL,0,<strong>/*NumberOfConcurrentThreads--&gt;*/qss-&gt;maxThreads</strong>);<br>&nbsp;//initialize worker for completion routine.<br>&nbsp;addQSSWorker(qss,qss-&gt;minThreads);&nbsp;&nbsp;<br>&nbsp;qss-&gt;lifecycleStatus=2;<br>&nbsp;{<br>&nbsp;&nbsp;QSSWORKER_PARAM * pParam=malloc(sizeof(QSSWORKER_PARAM));<br>&nbsp;&nbsp;pParam-&gt;qss=qss;<br>&nbsp;&nbsp;pParam-&gt;th=NULL;<br>&nbsp;&nbsp;if(qss-&gt;<strong>passive</strong>){<br>&nbsp;&nbsp;&nbsp;DWORD threadId;<br>&nbsp;&nbsp;&nbsp;pParam-&gt;th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)acceptorRoutine,pParam,0,&amp;threadId);&nbsp;<br>&nbsp;&nbsp;}else<br>&nbsp;&nbsp;&nbsp;return acceptorRoutine(pParam);<br>&nbsp;}<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;<strong>shutdownSocketServer</strong>(SocketServer *ss){<br>&nbsp;QSocketServer * qss=(QSocketServer *)ss;<br>&nbsp;if(qss==NULL||InterlockedCompareExchange(&amp;qss-&gt;lifecycleStatus,3,2)!=2)<br>&nbsp;&nbsp;return 0;&nbsp;<br>&nbsp;closesocket(qss-&gt;server_s/*<strong>listen-socket</strong>*/);//<strong>..other accepted-sockets associated with the listen-socket will not be closed,except WSACleanup is called..</strong>&nbsp;<br>&nbsp;if(qss-&gt;CSocketsCounter==0)<br>&nbsp;&nbsp;qss-&gt;lifecycleStatus=4,PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);<br>&nbsp;WSACleanup();&nbsp;&nbsp;<br>&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>acceptorRoutine</strong>(LPVOID ss){<br>&nbsp;QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;<br>&nbsp;QSocketServer * qss=pParam-&gt;qss;<br>&nbsp;HANDLE curThread=pParam-&gt;th;<br>&nbsp;QSSOverlapped *qssOl=NULL;<br>&nbsp;SOCKADDR_IN client_addr;<br>&nbsp;int client_addr_leng=sizeof(SOCKADDR_IN);<br>&nbsp;SOCKET cs;&nbsp;<br>&nbsp;free(pParam);<br>&nbsp;while(1){&nbsp;&nbsp;<br>&nbsp;&nbsp;printf("accept starting.....\n");<br>&nbsp;&nbsp;<strong>cs/*Accepted-socket*/</strong>=<strong>accept</strong>(qss-&gt;server_s,(LPSOCKADDR)&amp;client_addr,&amp;client_addr_leng);<br>&nbsp;&nbsp;if(cs==INVALID_SOCKET)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;printf("accept failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{//<strong>SO_KEEPALIVE,SIO_KEEPALIVE_VALS</strong>&nbsp;这里是利用系统的"<strong>心跳探测</strong>",keepalive probes.linux:setsockopt,SOL_TCP:TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct tcp_keepalive alive,aliveOut;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int so_keepalive_opt=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD outDW;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!setsockopt(cs,SOL_SOCKET,SO_KEEPALIVE,(char *)&amp;so_keepalive_opt,sizeof(so_keepalive_opt))){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.onoff=TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.keepalivetime=QSS_SIO_KEEPALIVE_VALS_TIMEOUT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alive.keepaliveinterval=QSS_SIO_KEEPALIVE_VALS_INTERVAL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(WSAIoctl(cs,SIO_KEEPALIVE_VALS,&amp;alive,sizeof(alive),&amp;aliveOut,sizeof(aliveOut),&amp;outDW,NULL,NULL)==SOCKET_ERROR){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("WSAIoctl SIO_KEEPALIVE_VALS failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("setsockopt SO_KEEPALIVE failed:%d\n",GetLastError());&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;CreateIoCompletionPort((HANDLE)cs,qss-&gt;iocpHandle,cs,0);<br>&nbsp;&nbsp;if(qssOl==NULL){<br>&nbsp;&nbsp;&nbsp;qssOl=malloc(sizeof(QSSOverlapped));&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;qssOl-&gt;client_s=cs;<br>&nbsp;&nbsp;qssOl-&gt;wsaBuf.len=MAX_BUF_SIZE,qssOl-&gt;wsaBuf.buf=qssOl-&gt;buf,qssOl-&gt;numberOfBytesTransferred=0,qssOl-&gt;flags=0;//initialize WSABuf.<br>&nbsp;&nbsp;memset(&amp;qssOl-&gt;overlapped,0,sizeof(WSAOVERLAPPED));&nbsp;&nbsp;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;DWORD lastErr=GetLastError();<br>&nbsp;&nbsp;&nbsp;int ret=0;<br>&nbsp;&nbsp;&nbsp;SetLastError(0);<br>&nbsp;&nbsp;&nbsp;ret=WSARecv(cs,&amp;qssOl-&gt;wsaBuf,1,&amp;qssOl-&gt;numberOfBytesTransferred,&amp;qssOl-&gt;flags,&amp;qssOl-&gt;overlapped,NULL);<br>&nbsp;&nbsp;&nbsp;if(ret==0||(ret==SOCKET_ERROR&amp;&amp;GetLastError()==WSA_IO_PENDING)){<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedIncrement(&amp;qss-&gt;<strong>CSocketsCounter</strong>);//<strong>Accepted-socket计数递增.</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;cslifecb)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;cslifecb(cs,0);<br>&nbsp;&nbsp;&nbsp;&nbsp;qssOl=NULL;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(!GetLastError())<br>&nbsp;&nbsp;&nbsp;&nbsp;SetLastError(lastErr);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;printf("accept flags:%d ,cs:%d.\n",GetLastError(),cs);<br>&nbsp;}//end while.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;if(qssOl)<br>&nbsp;&nbsp;free(qssOl);<br>&nbsp;if(qss)<br>&nbsp;&nbsp;shutdownSocketServer((SocketServer *)qss);<br>&nbsp;if(curThread)<br>&nbsp;&nbsp;CloseHandle(curThread);</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;return 1;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">static int postRecvCompletionPacket(QSSOverlapped * qssOl,int SOErrOccurredCode){&nbsp;<br>&nbsp;int SOErrOccurred=0;&nbsp;<br>&nbsp;DWORD lastErr=GetLastError();<br>&nbsp;SetLastError(0);<br>&nbsp;//SOCKET_ERROR:-1,WSA_IO_PENDING:997<br>&nbsp;if(WSARecv(qssOl-&gt;client_s,&amp;qssOl-&gt;wsaBuf,1,&amp;qssOl-&gt;numberOfBytesTransferred,&amp;qssOl-&gt;flags,&amp;qssOl-&gt;overlapped,NULL)==SOCKET_ERROR<br>&nbsp;&nbsp;&amp;&amp;GetLastError()!=WSA_IO_PENDING)//this case lastError maybe 64, 10054&nbsp;<br>&nbsp;{<br>&nbsp;&nbsp;SOErrOccurred=SOErrOccurredCode;&nbsp;&nbsp;<br>&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;if(!GetLastError())<br>&nbsp;&nbsp;SetLastError(lastErr);&nbsp;<br>&nbsp;if(SOErrOccurred)<br>&nbsp;&nbsp;printf("worker[%d] postRecvCompletionPacket SOErrOccurred=%d,preErr:%d,postedErr:%d\n",GetCurrentThreadId(),SOErrOccurred,lastErr,GetLastError());<br>&nbsp;return SOErrOccurred;<br>}</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">DWORD&nbsp;&nbsp;<strong>completionWorkerRoutine</strong>(LPVOID ss){<br>&nbsp;QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;<br>&nbsp;QSocketServer * qss=pParam-&gt;qss;<br>&nbsp;HANDLE curThread=pParam-&gt;th;<br>&nbsp;QSSOverlapped * qssOl=NULL;<br>&nbsp;DWORD numberOfBytesTransferred=0;<br>&nbsp;ULONG_PTR completionKey=0;<br>&nbsp;int postRes=0,handleCode=0,exitCode=0,SOErrOccurred=0;&nbsp;<br>&nbsp;free(pParam);<br>&nbsp;while(!exitCode){<br>&nbsp;&nbsp;SetLastError(0);<br>&nbsp;&nbsp;if(GetQueuedCompletionStatus(qss-&gt;iocpHandle,&amp;numberOfBytesTransferred,&amp;completionKey,(LPOVERLAPPED *)&amp;qssOl,qss-&gt;workerWaitTimeout)){<br>&nbsp;&nbsp;&nbsp;if(<strong>completionKey==-1</strong>&amp;&amp;qss-&gt;lifecycleStatus&gt;=4)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] completionKey -1:%d \n",GetCurrentThreadId(),GetLastError());<br>&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;workerCounter&gt;1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;exitCode=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;<strong>break;<br></strong>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;if(numberOfBytesTransferred&gt;0){&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedIncrement(&amp;qss-&gt;currentBusyWorkers);<br>&nbsp;&nbsp;&nbsp;&nbsp;addQSSWorker(qss,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;handleCode=qss-&gt;protoHandler((LPWSAOVERLAPPED)qssOl);&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;InterlockedDecrement(&amp;qss-&gt;currentBusyWorkers);&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;if(handleCode&gt;=0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=postRecvCompletionPacket(qssOl,1);<br>&nbsp;&nbsp;&nbsp;&nbsp;}else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=2;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}else{<br>&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] numberOfBytesTransferred==0 ***** closesocket servS or cs *****,%d,%d ,ol is:%d\n",GetCurrentThreadId(),GetLastError(),completionKey,qssOl==NULL?0:1);<br>&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br>&nbsp;&nbsp;}else{ //GetQueuedCompletionStatus rtn FALSE, lastError 64 ,<strong>995</strong>[<strong>timeout worker thread exit</strong>.] ,WAIT_TIMEOUT:258&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(qssOl){<br>&nbsp;&nbsp;&nbsp;&nbsp;SOErrOccurred=postRecvCompletionPacket(qssOl,4);<br>&nbsp;&nbsp;&nbsp;}else {&nbsp;&nbsp;&nbsp;&nbsp;</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;&nbsp;&nbsp;printf("worker[%d] GetQueuedCompletionStatus F:%d \n",GetCurrentThreadId(),GetLastError());<br>&nbsp;&nbsp;&nbsp;&nbsp;if(GetLastError()!=WAIT_TIMEOUT){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exitCode=2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}else{//wait timeout&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;lifecycleStatus!=4&amp;&amp;qss-&gt;currentBusyWorkers==0&amp;&amp;qss-&gt;workerCounter&gt;qss-&gt;minThreads){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(qss-&gt;lifecycleStatus!=4&amp;&amp;qss-&gt;currentBusyWorkers==0&amp;&amp;qss-&gt;workerCounter&gt;qss-&gt;minThreads){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;workerCounter--;//until qss-&gt;workerCounter decrease to qss-&gt;minThreads<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exitCode=3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;}//end GetQueuedCompletionStatus.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;&nbsp;if(<strong>SOErrOccurred</strong>){&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;if(qss-&gt;cslifecb)<br>&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;cslifecb(qssOl-&gt;client_s,-1);<br>&nbsp;&nbsp;&nbsp;/*if(qssOl)*/{<br>&nbsp;&nbsp;&nbsp;&nbsp;closesocket(qssOl-&gt;client_s);<br>&nbsp;&nbsp;&nbsp;&nbsp;free(qssOl);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;if(InterlockedDecrement(&amp;qss-&gt;<strong>CSocketsCounter</strong>)==0&amp;&amp;qss-&gt;lifecycleStatus&gt;=3){&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;//for qss workerSize,PostQueuedCompletionStatus -1<br>&nbsp;&nbsp;&nbsp;&nbsp;qss-&gt;lifecycleStatus=4,PostQueuedCompletionStatus(qss-&gt;iocpHandle,0,-1,NULL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;exitCode=4;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<strong>qssOl=NULL,numberOfBytesTransferred=0,completionKey=0,SOErrOccurred=0;//for net while.<br></strong>&nbsp;}//end while.</p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; ">&nbsp;//last to do&nbsp;<br>&nbsp;if(exitCode!=3){&nbsp;<br>&nbsp;&nbsp;int clearup=0;<br>&nbsp;&nbsp;EnterCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(!--qss-&gt;workerCounter&amp;&amp;qss-&gt;lifecycleStatus&gt;=4){//clearup QSS<br>&nbsp;&nbsp;&nbsp;&nbsp;clearup=1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;LeaveCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;if(clearup){<br>&nbsp;&nbsp;&nbsp;DeleteCriticalSection(&amp;qss-&gt;QSS_LOCK);<br>&nbsp;&nbsp;&nbsp;CloseHandle(qss-&gt;iocpHandle);<br>&nbsp;&nbsp;&nbsp;free(qss);&nbsp;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;CloseHandle(curThread);<br>&nbsp;return 1;<br>}<br>------------------------------------------------------------------------------------------------------------------------<br>&nbsp; &nbsp; 对于IOCP的LastError的辨别和处理是个难点,所以请注意我的<strong>completionWorkerRoutine的while结构</strong>,<br>结构如下:<br>while(!exitCode){<br>&nbsp;&nbsp;&nbsp; if(<strong>completionKey==-1</strong>){...<strong>break</strong>;}<br>&nbsp;&nbsp;&nbsp; if(<strong>GetQueuedCompletionStatus</strong>){/*在这个if体中只要你投递的OVERLAPPED is not NULL,那么这里你得到的就是<strong>它</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(numberOfBytesTransferred&gt;0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*在这里handle request,<strong>记得要继续投递你的OVERLAPPED哦!</strong>&nbsp;*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*这里可能客户端或服务端closesocket(the socket),<strong>但是OVERLAPPED is not NULL,只要你投递的不为NULL!</strong>*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }else{/*在这里的if体中,虽然GetQueuedCompletionStatus return&nbsp;<strong>FALSE</strong>,但是不代表OVERLAPPED一定为NULL.<strong>特别是OVERLAPPED is not NULL的情况下,不要以为LastError发生了,就代表当前的socket无用或发生致命的异常,比如发生lastError:995这种情况下此时的socket有可能是一切正常的可用的,你不应该关闭它</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(OVERLAPPED is not NULL){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<strong>这种情况下,请不管37,21继续投递吧!在投递后再检测错误</strong>.*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; if(<strong>socket error occured</strong>){<br><br>&nbsp; }<br>&nbsp; prepare for next while.<br>}&nbsp;<br></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong>&nbsp;&nbsp;&nbsp; 行文仓促,难免有错误或不足之处,希望大家踊跃指正评论,谢谢!<br></strong></p><span  style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong></strong></span><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong>&nbsp;&nbsp;&nbsp; 这个模型在性能上还是有改进的空间哦！</strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong><br></strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong>from:</strong></strong></p><p style="color: rgb(75, 75, 75); font-family: verdana, Arial, helvetica, sans-seriff; font-size: 12px; line-height: 19px; "><strong><strong><a href="http://www.cppblog.com/adapterofcoms/archive/2010/06/26/118781.aspx">http://www.cppblog.com/adapterofcoms/archive/2010/06/26/118781.aspx</a></strong></strong></p>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/124731.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-08-25 20:42 <a href="http://www.cppblog.com/beautykingdom/archive/2010/08/25/124731.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个基于Event Poll(epoll)的TCP Server Framework,浅析epoll</title><link>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124730.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 25 Aug 2010 12:41:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124730.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/124730.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/08/25/124730.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/124730.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/124730.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: epoll,event poll,on linux kernel 2.6.x.pthread,nptl-2.12&nbsp;&nbsp;&nbsp;LT/ET:ET也会多次发送event,当然频率远低于LT,但是epoll one shot才是真正的对"one connection&nbsp;VS one thread in worker thread pool,不依赖于任何connection-...&nbsp;&nbsp;<a href='http://www.cppblog.com/beautykingdom/archive/2010/08/25/124730.html'>阅读全文</a><img src ="http://www.cppblog.com/beautykingdom/aggbug/124730.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-08-25 20:41 <a href="http://www.cppblog.com/beautykingdom/archive/2010/08/25/124730.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]close_wait状态和time_wait状态</title><link>http://www.cppblog.com/beautykingdom/archive/2010/07/17/120646.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sat, 17 Jul 2010 14:37:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/07/17/120646.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/120646.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/07/17/120646.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/120646.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/120646.html</trackback:ping><description><![CDATA[<p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">不久前，我的Socket Client程序遇到了一个非常尴尬的错误。它本来应该在一个socket长连接上持续不断地向服务器发送数据，如果socket连接断开，那么程序会自动不断地重试建立连接。<br style="font: normal normal normal 12px/normal song, Verdana; ">有一天发现程序在不断尝试建立连接，但是总是失败。用netstat查看，这个程序竟然有上千个socket连接处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态，以至于达到了上限，所以无法建立新的socket连接了。<br style="font: normal normal normal 12px/normal song, Verdana; ">为什么会这样呢？<br style="font: normal normal normal 12px/normal song, Verdana; ">它们为什么会都处在<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态呢？<br style="font: normal normal normal 12px/normal song, Verdana; "><strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态的生成原因<br style="font: normal normal normal 12px/normal song, Verdana; ">首先我们知道，如果我们的Client程序处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态的话，说明套接字是被动关闭的！<br style="font: normal normal normal 12px/normal song, Verdana; ">因为如果是Server端主动断掉当前连接的话，那么双方关闭这个TCP连接共需要四个packet：<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server ---&gt; FIN ---&gt; Client<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server &lt;--- ACK &lt;--- Client<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp; 这时候Server端处于FIN_WAIT_2状态；而我们的程序处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态。<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server &lt;--- FIN &lt;--- Client<br style="font: normal normal normal 12px/normal song, Verdana; ">这时Client发送FIN给Server，Client就置为LAST_ACK状态。<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server ---&gt; ACK ---&gt; Client<br style="font: normal normal normal 12px/normal song, Verdana; ">Server回应了ACK，那么Client的套接字才会真正置为CLOSED状态。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3"><img alt="image" src="http://tech.ccidnet.com/pub/attachment/2004/8/322252.png" border="0"><br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">我们的程序处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态，而不是LAST_ACK状态，说明还没有发FIN给Server，那么可能是在关闭连接之前还有许多数据要发送或者其他事要做，导致没有发这个FIN packet。<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">原因知道了，那么为什么不发FIN包呢，难道会在关闭己方连接前有那么多事情要做吗？<br style="font: normal normal normal 12px/normal song, Verdana; ">还有一个问题，为什么有数千个连接都处于这个状态呢？难道那段时间内，服务器端总是主动拆除我们的连接吗？<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">不管怎么样，我们必须防止类似情况再度发生！<br style="font: normal normal normal 12px/normal song, Verdana; ">首先，我们要防止不断开辟新的端口，这可以通过设置SO_REUSEADDR套接字选项做到：<br style="font: normal normal normal 12px/normal song, Verdana; ">重用本地地址和端口<br style="font: normal normal normal 12px/normal song, Verdana; ">以前我总是一个端口不行，就换一个新的使用，所以导致让数千个端口进入<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态。如果下次还发生这种尴尬状况，我希望加一个限定，只是当前这个端口处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态！<br style="font: normal normal normal 12px/normal song, Verdana; ">在调用<br style="font: normal normal normal 12px/normal song, Verdana; ">sockConnected = socket(AF_INET, SOCK_STREAM, 0);<br style="font: normal normal normal 12px/normal song, Verdana; ">之后，我们要设置该套接字的选项来重用：</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">/// 允许重用本地地址和端口:<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 这样的好处是，即使socket断了，调用前面的socket函数也不会占用另一个，而是始终就是一个端口<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 这样防止socket始终连接不上，那么按照原来的做法，会不断地换端口。<br style="font: normal normal normal 12px/normal song, Verdana; ">int nREUSEADDR = 1;<br style="font: normal normal normal 12px/normal song, Verdana; ">setsockopt(sockConnected,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOL_SOCKET,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SO_REUSEADDR,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (const char*)&amp;nREUSEADDR,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(int));</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">教科书上是这么说的：这样，假如服务器关闭或者退出，造成本地地址和端口都处于TIME_WAIT状态，那么SO_REUSEADDR就显得非常有用。<br style="font: normal normal normal 12px/normal song, Verdana; ">也许我们无法避免被冻结在<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态永远不出现，但起码可以保证不会占用新的端口。<br style="font: normal normal normal 12px/normal song, Verdana; ">其次，我们要设置SO_LINGER套接字选项：<br style="font: normal normal normal 12px/normal song, Verdana; ">从容关闭还是强行关闭？<br style="font: normal normal normal 12px/normal song, Verdana; ">LINGER是&#8220;拖延&#8221;的意思。<br style="font: normal normal normal 12px/normal song, Verdana; ">默认情况下(Win2k)，SO_DONTLINGER套接字选项的是1；SO_LINGER选项是，linger为{l_onoff：0，l_linger：0}。<br style="font: normal normal normal 12px/normal song, Verdana; ">如果在发送数据的过程中(send()没有完成，还有数据没发送)而调用了closesocket()，以前我们一般采取的措施是&#8220;从容关闭&#8221;：<br style="font: normal normal normal 12px/normal song, Verdana; ">因为在退出服务或者每次重新建立socket之前，我都会先调用<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 先将双向的通讯关闭<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp; shutdown(sockConnected, SD_BOTH);<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp; /// 安全起见，每次建立Socket连接前，先把这个旧连接关闭<br style="font: normal normal normal 12px/normal song, Verdana; ">closesocket(sockConnected);<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">我们这次要这么做：<br style="font: normal normal normal 12px/normal song, Verdana; ">设置SO_LINGER为零（亦即linger结构中的l_onoff域设为非零，但l_linger为0），便不用担心closesocket调用进入&#8220;锁定&#8221;状态（等待完成），不论是否有排队数据未发送或未被确认。这种关闭方式称为&#8220;强行关闭&#8221;，因为套接字的虚电路立即被复位，尚未发出的所有数据都会丢失。在远端的recv()调用都会失败，并返回WSAECONNRESET错误。<br style="font: normal normal normal 12px/normal song, Verdana; ">在connect成功建立连接之后设置该选项：<br style="font: normal normal normal 12px/normal song, Verdana; ">linger m_sLinger;<br style="font: normal normal normal 12px/normal song, Verdana; ">m_sLinger.l_onoff = 1;&nbsp; // (在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)<br style="font: normal normal normal 12px/normal song, Verdana; ">m_sLinger.l_linger = 0; // (容许逗留的时间为0秒)<br style="font: normal normal normal 12px/normal song, Verdana; ">setsockopt(sockConnected,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SOL_SOCKET,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SO_LINGER,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (const char*)&amp;m_sLinger,<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(linger));</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">总结<br style="font: normal normal normal 12px/normal song, Verdana; ">也许我们避免不了<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态冻结的再次出现，但我们会使影响降到最小，希望那个重用套接字选项能够使得下一次重新建立连接时可以把<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态踢掉。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">Feedback<br style="font: normal normal normal 12px/normal song, Verdana; "># 回复：[Socket]尴尬的<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态以及应对策略 2005-01-30 3:41 PM yun.zheng&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">回复人： elssann(臭屁虫和他的开心果) ( ) 信誉：51 2005-01-30 14:00:00 得分: 0</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">我的意思是：当一方关闭连接后，另外一方没有检测到，就导致了<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>的出现，上次我的一个朋友也是这样，他写了一个客户端和 APACHE连接，当APACHE把连接断掉后，他没检测到，出现了<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>，后来我叫他检测了这个地方，他添加了调用 closesocket的代码后，这个问题就消除了。&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">如果你在关闭连接前还是出现<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>,建议你取消shutdown的调用，直接两边closesocket试试。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">另外一个问题:</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">比如这样的一个例子：&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">当客户端登录上服务器后，发送身份验证的请求，服务器收到了数据，对客户端身份进行验证，发现密码错误，这时候服务器的一般做法应该是先发送一个密码错误的信息给客户端，然后把连接断掉。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">如果把&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">m_sLinger.l_onoff = 1;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">m_sLinger.l_linger = 0;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">这样设置后，很多情况下，客户端根本就收不到密码错误的消息，连接就被断了。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3"># 回复：[Socket]尴尬的<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态以及应对策略 2005-01-30 3:41 PM yun.zheng&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">elssann(臭屁虫和他的开心果) ( ) 信誉：51 2005-01-30 13:24:00 得分: 0</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">出现<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>的原因很简单，就是某一方在网络连接断开后，没有检测到这个错误，没有执行closesocket，导致了这个状态的实现，这在TCP/IP协议的状态变迁图上可以清楚看到。同时和这个相对应的还有一种叫TIME_WAIT的。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">另外，把SOCKET的SO_LINGER设置为0秒拖延（也就是立即关闭）在很多时候是有害处的。&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">还有，把端口设置为可复用是一种不安全的网络编程方法。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3"># 回复：[Socket]尴尬的<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态以及应对策略 2005-01-30 3:42 PM yun.zheng&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">elssann(臭屁虫和他的开心果) ( ) 信誉：51 2005-01-30 14:48:00 得分: 0</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">能不能解释请看这里&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; "></font><a href="http://blog.csdn.net/cqq/archive/2005/01/26/269160.aspx" style="text-decoration: underline; color: rgb(0, 68, 182); "><font color="#0000ff" size="3">http://blog.csdn.net/cqq/archive/2005/01/26/269160.aspx</font></a><font size="3"></font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">再看这个图：</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><a href="http://tech.ccidnet.com/pub/attachment/2004/8/322252.png" style="text-decoration: underline; color: rgb(0, 68, 182); "><font color="#0000ff" size="3">http://tech.ccidnet.com/pub/attachment/2004/8/322252.png</font></a><font size="3"></font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">断开连接的时候，&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">当发起主动关闭的左边这方发送一个FIN过去后，右边被动关闭的这方要回应一个ACK，这个ACK是TCP回应的，而不是应用程序发送的，此时，被动关闭的一方就处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态了。如果此时被动关闭的这一方不再继续调用closesocket,那么他就不会发送接下来的FIN，导致自己老是处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>。只有被动关闭的这一方调用了closesocket,才会发送一个FIN给主动关闭的这一方，同时也使得自己的状态变迁为LAST_ACK。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3"># 回复：[Socket]尴尬的<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态以及应对策略 2005-01-30 3:54 PM yun.zheng&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">elssann(臭屁虫和他的开心果) ( ) 信誉：51 2005-01-30 15:39:00 得分: 0</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3">比如被动关闭的是客户端。。。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">当对方调用closesocket的时候，你的程序正在</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">int nRet = recv(s,....);&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">if (nRet == SOCKET_ERROR)&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">{&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">// closesocket(s);&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">return FALSE;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">}</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">很多人就是忘记了那句closesocket，这种代码太常见了。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">我的理解，当主动关闭的一方发送FIN到被动关闭这边后，被动关闭这边的TCP马上回应一个ACK过去，同时向上面应用程序提交一个ERROR，导致上面的SOCKET的send或者recv返回SOCKET_ERROR，正常情况下，如果上面在返回SOCKET_ERROR后调用了 closesocket,那么被动关闭的者一方的TCP就会发送一个FIN过去，自己的状态就变迁到LAST_ACK.</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><br style="font: normal normal normal 12px/normal song, Verdana; "><font size="3"># 回复：[Socket]尴尬的<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态以及应对策略 2005-01-30 4:17 PM yun.zheng&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">int nRecvBufLength =&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">recv(sockConnected,&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">szRecvBuffer,&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">sizeof(szRecvBuffer),&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">0);&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// zhengyun 20050130:&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// elssann举例说，当对方调用closesocket的时候，我的程序正在&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// recv，这时候有可能对方发送的FIN包我没有收到，而是由TCP代回了&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 一个ACK包，所以我这边程序进入<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>状态。&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 所以他建议在这里判断是否已出错，是就主动closesocket。&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 因为前面我们已经设置了recv超时时间为30秒，那么如果真的是超时了，&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">/// 这里收到的错误应该是WSAETIMEDOUT，这种情况下也可以关闭连接的&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">if (nRecvBufLength == SOCKET_ERROR)&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">{&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">TRACE_INFO(_T("=用recv接收发生Socket错误="));&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">closesocket(sockConnected);&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">continue;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">}</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">这样可以吗？</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">网络连接无法释放——&nbsp;<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong><br style="font: normal normal normal 12px/normal song, Verdana; ">关键字：TCP ，<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>, Java, SocketChannel</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3"></font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">问题描述：最近性能测试碰到的一个问题。客户端使用NIO，服务器还是一般的Socket连接。当测试进行一段时间以后，发现服务器端的系统出现大量未释放的网络连接。用netstat -na查看，连接状态为<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>。这就奇怪了，为什么Socket已经关闭而连接依然未释放。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">解决：Google了半天，发现关于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>的问题一般是C的，Java似乎碰到这个问题的不多（这有一篇不错的，也是解决<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>的，但是好像没有根本解决，而是选择了一个折中的办法）。接着找，由于使用了NIO，所以怀疑可能是这方面的问题，结果找到了这篇。顺着帖子翻下去，其中有几个人说到了一个问题—— 一端的Socket调用close后，另一端的Socket没有调用close.于是查了一下代码，果然发现Server端在某些异常情况时，没有关闭Socket。改正后问题解决。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">时间基本上花在Google上了，不过也学到不少东西。下面为一张TCP连接的状态转换图：</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">说明：虚线和实线分别对应服务器端(被连接端)和客户端端(主动连接端)。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">结合上图使用netstat -na命令即可知道到当前的TCP连接状态。一般LISTEN、ESTABLISHED、TIME_WAIT是比较常见。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">分析：</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">上面我碰到的这个问题主要因为TCP的结束流程未走完，造成连接未释放。现设客户端主动断开连接，流程如下</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Client&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 消息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Server</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close()<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ------ FIN -------&gt;<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FIN_WAIT1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong><br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;----- ACK -------<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FIN_WAIT2&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close()<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;------ FIN ------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TIME_WAIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LAST_ACK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ------ ACK -------&gt;&nbsp;&nbsp;<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLOSED<br style="font: normal normal normal 12px/normal song, Verdana; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLOSED</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">如上图所示，由于Server的Socket在客户端已经关闭时而没有调用关闭，造成服务器端的连接处在&#8220;挂起&#8221;状态，而客户端则处在等待应答的状态上。此问题的典型特征是：一端处于FIN_WAIT2 ，而另一端处于<strong style="color: black; background-color: rgb(255, 255, 102); ">CLOSE_WAIT</strong>. 不过，根本问题还是程序写的不好，有待提高。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">TIME_WAIT状态<br style="font: normal normal normal 12px/normal song, Verdana; ">根据TCP协议，主动发起关闭的一方，会进入TIME_WAIT状态，持续2*MSL(Max Segment Lifetime)，缺省为240秒，在这个post中简洁的介绍了为什么需要这个状态。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">值得一说的是，对于基于TCP的HTTP协议，关闭TCP连接的是Server端，这样，Server端会进入TIME_WAIT状态，可想而知，对于访问量大的Web Server，会存在大量的TIME_WAIT状态，假如server一秒钟接收1000个请求，那么就会积压240*1000=240，000个 TIME_WAIT的记录，维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT，所以对于新的 TCP连接请求，判断是否hit中一个TIME_WAIT不会太费时间，但是有这么多状态要维护总是不好。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">HTTP协议1.1版规定default行为是Keep-Alive，也就是会重用TCP连接传输多个 request/response，一个主要原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少，因为 240秒的时间实在是忒长了点，对于Windows，修改注册表，在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay，一般认为不要少于60，不然可能会有麻烦。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">对于大型的服务，一台server搞不定，需要一个LB(Load Balancer)把流量分配到若干后端服务器上，如果这个LB是以NAT方式工作的话，可能会带来问题。假如所有从LB到后端Server的IP包的 source address都是一样的(LB的对内地址），那么LB到后端Server的TCP连接会受限制，因为频繁的TCP连接建立和关闭，会在server上留下TIME_WAIT状态，而且这些状态对应的remote address都是LB的，LB的source port撑死也就60000多个(2^16=65536,1~1023是保留端口，还有一些其他端口缺省也不会用），每个LB上的端口一旦进入 Server的TIME_WAIT黑名单，就有240秒不能再用来建立和Server的连接，这样LB和Server最多也就能支持300个左右的连接。如果没有LB，不会有这个问题，因为这样server看到的remote address是internet上广阔无垠的集合，对每个address，60000多个port实在是够用了。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">一开始我觉得用上LB会很大程度上限制TCP的连接数，但是实验表明没这回事，LB后面的一台Windows Server 2003每秒处理请求数照样达到了600个，难道TIME_WAIT状态没起作用？用Net Monitor和netstat观察后发现，Server和LB的XXXX端口之间的连接进入TIME_WAIT状态后，再来一个LB的XXXX端口的 SYN包，Server照样接收处理了，而是想像的那样被drop掉了。翻书，从书堆里面找出覆满尘土的大学时代买的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》，中间提到一句，对于BSD-derived实现，只要SYN的sequence number比上一次关闭时的最大sequence number还要大，那么TIME_WAIT状态一样接受这个SYN，难不成Windows也算BSD-derived?有了这点线索和关键字 (BSD)，找到这个post，在NT4.0的时候，还是和BSD-derived不一样的，不过Windows Server 2003已经是NT5.2了，也许有点差别了。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">做个试验，用Socket API编一个Client端，每次都Bind到本地一个端口比如2345，重复的建立TCP连接往一个Server发送Keep-Alive=false 的HTTP请求，Windows的实现让sequence number不断的增长，所以虽然Server对于Client的2345端口连接保持TIME_WAIT状态，但是总是能够接受新的请求，不会拒绝。那如果SYN的Sequence Number变小会怎么样呢？同样用Socket API，不过这次用Raw IP，发送一个小sequence number的SYN包过去，Net Monitor里面看到，这个SYN被Server接收后如泥牛如海，一点反应没有，被drop掉了。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">按照书上的说法，BSD-derived和Windows Server 2003的做法有安全隐患，不过至少这样至少不会出现TIME_WAIT阻止TCP请求的问题，当然，客户端要配合，保证不同TCP连接的sequence number要上涨不要下降。</font></p><p style="font: normal normal normal 12px/normal song, Verdana; border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">本文来自CSDN博客，转载请标明出处：</font><a href="http://blog.csdn.net/lionzl/archive/2009/03/20/4007206.aspx" style="text-decoration: underline; color: rgb(0, 68, 182); "><font color="#0000ff" size="3">http://blog.csdn.net/lionzl/archive/2009/03/20/4007206.as</font></a></p>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/120646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-07-17 22:37 <a href="http://www.cppblog.com/beautykingdom/archive/2010/07/17/120646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP: SYN ACK FIN RST PSH URG 详解</title><link>http://www.cppblog.com/beautykingdom/archive/2010/07/16/120546.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Fri, 16 Jul 2010 06:14:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/07/16/120546.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/120546.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/07/16/120546.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/120546.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/120546.html</trackback:ping><description><![CDATA[
<p class="cc-lisence" style="line-height: 180%;">
<a href="http://creativecommons.org/licenses/by/3.0/deed.zh" target="_blank">版权声明</a>：转载时请以超链接形式标明文章原始出处和作者信息及<a href="http://bangzhuzhongxin.blogbus.com/logs/11205960.html" target="_blank">本声明</a><br><a href="http://xufish.blogbus.com/logs/40536553.html">http://xufish.blogbus.com/logs/40536553.html</a><br><br>
</p>
<span style="font-family: Arial; font-size: 12px; line-height: normal; color: #333333;"><span style="color: #000000; font-family: Georgia; font-size: 14px; line-height: 20px;">
<p style="line-height: normal;"><strong style="line-height: normal;">TCP
的三次握手</strong>是怎么进行的了：发送端发送一个SYN=1，ACK=0标志的数据包给接收端，请求进行连接，这是第一次握手；接收端收到请
求并且允许连接的话，就会发送一个SYN=1，ACK=1标志的数据包给发送端，告诉它，可以通讯了，并且让发送端发送一个确认数据包，这是第二次握手；
最后，发送端发送一个SYN=0，ACK=1的数据包给接收端，告诉它连接已被确认，这就是第三次握手。之后，一个TCP连接建立，开始通讯。</p>
<p style="line-height: normal;">*SYN：同步标志<br style="line-height: normal;">同步序列编号(Synchronize Sequence
Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号，该序列编号为TCP连接初始端(一般是客户
端)的初始序列编号。在这里，可以把TCP序列编号看作是一个范围从0到4，294，967，295的32位计数器。通过TCP连接交换的数据中每一个字
节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。</p>
<p style="line-height: normal;">*ACK：确认标志<br style="line-height: normal;">确认编号(Acknowledgement
Number)栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1，Figure-1)为下一个预期的序列编号，
同时提示远端系统已经成功接收所有数据。</p>
<p style="line-height: normal;">*RST：复位标志<br style="line-height: normal;">复位标志有效。用于复位相应的TCP连接。</p>
<p style="line-height: normal;">*URG：紧急标志<br style="line-height: normal;">紧急(The urgent pointer) 标志有效。紧急标志置位，</p>
<p style="line-height: normal;">*PSH：推标志<br style="line-height: normal;">该
标志置位时，接收端不将该数据进行队列处理，而是尽可能快将数据转由应用处理。在处理 telnet 或 rlogin
等交互模式的连接时，该标志总是置位的。</p>
<p style="line-height: normal;">*FIN：结束标志<br style="line-height: normal;">带有该标志置位的数据包用来结束一个TCP回话，但对应端口仍处于开放状态，准备接收后续数据。</p>
<p style="line-height: normal;">=============================================================<br></p>
</span>三次握手Three-way Handshake<br style="line-height: normal;"><br style="line-height: normal;">一个虚拟连接的建立是通过三次握手来实现的<br style="line-height: normal;"><br style="line-height: normal;">1. (B) --&gt; [SYN] --&gt;
(A)<br style="line-height: normal;"><br style="line-height: normal;">假如服
务器A和客户机B通讯. 当A要和B通信时，B首先向A发一个SYN (Synchronize) 标记的包，告诉A请求建立连接.<br style="line-height: normal;"><br style="line-height: normal;">注意: 一个
SYN包就是仅SYN标记设为1的TCP包(参见TCP包头Resources).
认识到这点很重要，只有当A受到B发来的SYN包，才可建立连接，除此之外别无他法。因此，如果你的防火墙丢弃所有的发往外网接口的SYN包，那么你将不
能让外部任何主机主动建立连接。<br style="line-height: normal;"><br style="line-height: normal;">2. (B) &lt;-- [SYN/ACK] &lt;--(A)<br style="line-height: normal;"><br style="line-height: normal;">接着，A收到后会发一个对SYN包的确认包(SYN/ACK)回
去，表示对第一个SYN包的确认，并继续握手操作.<br style="line-height: normal;"><br style="line-height: normal;">注意: SYN/ACK包是仅SYN 和 ACK 标记为1的包.<br style="line-height: normal;"><br style="line-height: normal;">3. (B)
--&gt; [ACK] --&gt; (A)<br style="line-height: normal;"><br style="line-height: normal;">B收到SYN/ACK
包,B发一个确认包(ACK)，通知A连接已建立。至此，三次握手完成，一个TCP连接完成<br style="line-height: normal;"><br style="line-height: normal;">Note: ACK包就是仅ACK 标记设为1的TCP包.
需要注意的是当三此握手完成、连接建立以后，TCP连接的每个包都会设置ACK位<br style="line-height: normal;"><br style="line-height: normal;">这就是为何连接跟踪很重要的原因了.
没有连接跟踪,防火墙将无法判断收到的ACK包是否属于一个已经建立的连接.一般的包过滤(Ipchains)收到ACK包时,会让它通过(这绝对不是个
好主意). 而当状态型防火墙收到此种包时，它会先在连接表中查找是否属于哪个已建连接，否则丢弃该包<br style="line-height: normal;"><br style="line-height: normal;">四次握手Four-way Handshake<br style="line-height: normal;"><br style="line-height: normal;">四次握手用来关闭已建
立的TCP连接<br style="line-height: normal;"><br style="line-height: normal;">1.
(B) --&gt; ACK/FIN --&gt; (A)<br style="line-height: normal;"><br style="line-height: normal;">2. (B) &lt;-- ACK &lt;-- (A)<br style="line-height: normal;"><br style="line-height: normal;">3. (B)
&lt;-- ACK/FIN &lt;-- (A)<br style="line-height: normal;"><br style="line-height: normal;">4. (B) --&gt; ACK --&gt; (A)<br style="line-height: normal;"><br style="line-height: normal;">注意:
由于TCP连接是双向连接, 因此关闭连接需要在两个方向上做。ACK/FIN 包(ACK 和FIN
标记设为1)通常被认为是FIN(终结)包.然而, 由于连接还没有关闭, FIN包总是打上ACK标记.
没有ACK标记而仅有FIN标记的包不是合法的包，并且通常被认为是恶意的<br style="line-height: normal;"><br style="line-height: normal;">连接复位Resetting a connection<br style="line-height: normal;"><br style="line-height: normal;">四次握手不是关闭
TCP连接的唯一方法. 有时,如果主机需要尽快关闭连接(或连接超时,端口或主机不可达),RST (Reset)包将被发送.
注意在，由于RST包不是TCP连接中的必须部分, 可以只发送RST包(即不带ACK标记). 但在正常的TCP连接中RST包可以带ACK确认标记<br style="line-height: normal;"><br style="line-height: normal;">请注意RST包是可
以不要收到方确认的?<br style="line-height: normal;"><br style="line-height: normal;">无效的TCP标记Invalid TCP Flags<br style="line-height: normal;"><br style="line-height: normal;">到目前为止，你已经看到了 SYN, ACK, FIN, 和RST 标记.
另外，还有PSH (Push) 和URG (Urgent)标记.<br style="line-height: normal;"><br style="line-height: normal;">最常见的非法组合是SYN/FIN 包. 注意:由于 SYN包是用来初始化连接的,
它不可能和 FIN和RST标记一起出现. 这也是一个恶意攻击.<br style="line-height: normal;"><br style="line-height: normal;">由于现在大多数防火墙已知 SYN/FIN 包,
别的一些组合,例如SYN/FIN/PSH, SYN/FIN/RST,
SYN/FIN/RST/PSH。很明显，当网络中出现这种包时，很你的网络肯定受到攻击了。<br style="line-height: normal;"><br style="line-height: normal;">别的已知的非法包有FIN
(无ACK标记)和"NULL"包。如同早先讨论的，由于ACK/FIN包的出现是为了关闭一个TCP连接，那么正常的FIN包总是带有 ACK
标记。"NULL"包就是没有任何TCP标记的包(URG,ACK,PSH,RST,SYN,FIN都为0)。<br style="line-height: normal;"><br style="line-height: normal;">到目前为止，正常的网
络活动下，TCP协议栈不可能产生带有上面提到的任何一种标记组合的TCP包。当你发现这些不正常的包时，肯定有人对你的网络不怀好意。<br style="line-height: normal;"><br style="line-height: normal;">UDP
(用户数据包协议User Datagram Protocol)<br style="line-height: normal;">TCP是面向连接
的，而UDP是非连接的协议。UDP没有对接受进行确认的标记和确认机制。对丢包的处理是在应用层来完成的。(or accidental
arrival).<br style="line-height: normal;"><br style="line-height: normal;">此处需要重点注意的事情是：在正常情况下，当UDP包到达一个关闭的端口时，会返回一个UDP复位包。由于UDP是非面向连接的,
因此没有任何确认信息来确认包是否正确到达目的地。因此如果你的防火墙丢弃UDP包，它会开放所有的UDP端口(?)。<br style="line-height: normal;"><br style="line-height: normal;">由于Internet
上正常情况下一些包将被丢弃，甚至某些发往已关闭端口(非防火墙的)的UDP包将不会到达目的，它们将返回一个复位UDP包。<br style="line-height: normal;"><br style="line-height: normal;">因为这个原因，UDP
端口扫描总是不精确、不可靠的。<br style="line-height: normal;"><br style="line-height: normal;">看起来大UDP包的碎片是常见的DOS (Denial of Service)攻击的常见形式 (这里有个DOS攻击的例子，<a href="http://grc.com/dos/grcdos.htm" style="line-height: normal;" target="_blank">http://grc.com/dos/grcdos.htm</a>&nbsp;).<br style="line-height: normal;"><br style="line-height: normal;">ICMP
(网间控制消息协议Internet Control Message Protocol)<br style="line-height: normal;">如同名字一样， ICMP用来在主机/路由器之间传递控制信息的协议。 ICMP包可以包含诊断信息(ping,
traceroute - 注意目前unix系统中的traceroute用UDP包而不是ICMP)，错误信息(网络/主机/端口 不可达
network/host/port unreachable), 信息(时间戳timestamp, 地址掩码address mask
request, etc.)，或控制信息 (source quench, redirect, etc.) 。<br style="line-height: normal;"><br style="line-height: normal;">你可以在<a href="http://www.iana.org/assignments/icmp-parameters" style="line-height: normal;" target="_blank">http://www.iana.org/assignments/icmp-parameters</a>中
找到ICMP包的类型。<br style="line-height: normal;"><br style="line-height: normal;">尽管ICMP通常是无害的，还是有些类型的ICMP信息需要丢弃。<br style="line-height: normal;"><br style="line-height: normal;">Redirect (5), Alternate Host Address (6),
Router Advertisement (9) 能用来转发通讯。<br style="line-height: normal;"><br style="line-height: normal;">Echo (8), Timestamp (13) and Address Mask
Request (17)
能用来分别判断主机是否起来，本地时间和地址掩码。注意它们是和返回的信息类别有关的。它们自己本身是不能被利用的，但它们泄露出的信息对攻击者是有用
的。<br style="line-height: normal;"><br style="line-height: normal;">ICMP
消息有时也被用来作为DOS攻击的一部分(例如：洪水ping flood ping,死 ping ?呵呵，有趣 ping of
death)?/p&gt;<br style="line-height: normal;"><br style="line-height: normal;">包碎片注意A Note About Packet Fragmentation<br style="line-height: normal;"><br style="line-height: normal;">如果一个包的大小超过了TCP的最大段长度MSS
(Maximum Segment Size) 或MTU (Maximum Transmission
Unit)，能够把此包发往目的的唯一方法是把此包分片。由于包分片是正常的，它可以被利用来做恶意的攻击。<br style="line-height: normal;"><br style="line-height: normal;">因为分片的包的第一个
分片包含一个包头，若没有包分片的重组功能，包过滤器不可能检测附加的包分片。典型的攻击Typical attacks involve in
overlapping the packet data in which packet header is 典型的攻击Typical
attacks involve in overlapping the packet data in which packet header
isnormal until is it overwritten with different destination IP (or port)
thereby bypassing firewall rules。包分片能作为 DOS 攻击的一部分，它可以crash older IP
stacks 或涨死CPU连接能力。<br style="line-height: normal;"><br style="line-height: normal;">Netfilter/Iptables中的连接跟踪代码能自动做分片重组。它仍有弱点，可能
受到饱和连接攻击，可以把CPU资源耗光。<br style="line-height: normal;"><br style="line-height: normal;">握手阶段：<br style="line-height: normal;">序号 方向
seq ack<br style="line-height: normal;">1　　A-&gt;B 10000 0<br style="line-height: normal;">2 B-&gt;A 20000 10000+1=10001<br style="line-height: normal;">3 A-&gt;B 10001 20000+1=20001<br style="line-height: normal;">解释：<br style="line-height: normal;">1：A向B发起
连接请求，以一个随机数初始化A的seq,这里假设为10000，此时ACK＝0<br style="line-height: normal;"><br style="line-height: normal;">2：B收到A的连接请求后，也以一个随机数初始化B的seq，这里假设为20000，意思
是：你的请求我已收到，我这方的数据流就从这个数开始。B的ACK是A的seq加1，即10000＋1＝10001<br style="line-height: normal;"><br style="line-height: normal;">3：A收到B的回复
后，它的seq是它的上个请求的seq加1，即10000＋1＝10001，意思也是：你的回复我收到了，我这方的数据流就从这个数开始。A此时的ACK
是B的seq加1，即20000+1=20001<br style="line-height: normal;"><br style="line-height: normal;"><br style="line-height: normal;">数据传输阶段：<br style="line-height: normal;">序号　　方向　　　　　　seq ack size<br style="line-height: normal;">23 A-&gt;B 40000 70000 1514<br style="line-height: normal;">24 B-&gt;A 70000 40000+1514-54=41460 54<br style="line-height: normal;">25 A-&gt;B 41460 70000+54-54=70000 1514<br style="line-height: normal;">26 B-&gt;A 70000 41460+1514-54=42920 54<br style="line-height: normal;">解释：<br style="line-height: normal;">23:B接收到
A发来的seq=40000,ack=70000,size=1514的数据包<br style="line-height: normal;">24:
于是B向A也发一个数据包，告诉B，你的上个包我收到了。B的seq就以它收到的数据包的ACK填充，ACK是它收到的数据包的SEQ加上数据包的大小
(不包括以太网协议头，IP头，TCP头)，以证实B发过来的数据全收到了。<br style="line-height: normal;">25:A
在收到B发过来的ack为41460的数据包时，一看到41460，正好是它的上个数据包的seq加上包的大小，就明白，上次发送的数据包已安全到达。于
是它再发一个数据包给B。这个正在发送的数据包的seq也以它收到的数据包的ACK填充，ACK就以它收到的数据包的seq(70000)加上包的
size(54)填充,即ack=70000+54-54(全是头长，没数据项)。<br style="line-height: normal;"><br style="line-height: normal;">其实在握手和结束时确认号应该是对方序列号加1,传输数据时则是对方序列号加上对方携带应
用层数据的长度.如果从以太网包返回来计算所加的长度,就嫌走弯路了.<br style="line-height: normal;">另外,如果对
方没有数据过来,则自己的确认号不变,序列号为上次的序列号加上本次应用层数据发送长度</span><img src ="http://www.cppblog.com/beautykingdom/aggbug/120546.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-07-16 14:14 <a href="http://www.cppblog.com/beautykingdom/archive/2010/07/16/120546.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll 精髓</title><link>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114627.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 06 May 2010 07:12:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114627.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/114627.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/114627.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/114627.html</trackback:ping><description><![CDATA[在linux的网络编程中，很长的时间都在使用select来做事件触发。在linux新的内核中，有了一种替换它的机制，就是epoll。<br>相比
于select，epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中，它是采用轮询来处理的，轮询的
fd数目越多，自然耗时越多。并且，在linux/posix_types.h头文件有这样的声明：<br><span style="color: #ff0102;">#define __FD_SETSIZE&nbsp;&nbsp;&nbsp; 1024</span><br>表示select最多同时监听
1024个fd，当然，可以通过修改头文件再重编译内核来扩大这个数目，但这似乎并不治本。<br><br>epoll的接口非常简单，一共就三个函数：<br><span style="color: #ff0102;">1. int epoll_create(int size);</span><br>创
建一个epoll的句柄，size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数，给出最大监听的fd+1的值。
需要注意的是，当创建好epoll句柄后，它就是会占用一个fd值，在linux下如果查看/proc/进程id/fd/，是能够看到这个fd的，所以在
使用完epoll后，必须调用close()关闭，否则可能导致fd被耗尽。<br><br><br><span style="color: #ff0102;">2. int epoll_ctl(int epfd, int op, int fd, struct
epoll_event *event);</span><br>epoll的事件注册函数，它不同与select()是在监听事件时告诉内核要监听什么
类型的事件，而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值，第二个参数表示动作，用三个宏来表示：<br>EPOLL_CTL_ADD：
注册新的fd到epfd中；<br>EPOLL_CTL_MOD：修改已经注册的fd的监听事件；<br>EPOLL_CTL_DEL：从epfd中删除
一个fd；<br>第三个参数是需要监听的fd，第四个参数是告诉内核需要监听什么事，struct epoll_event结构如下：<br>struct
epoll_event {<br>&nbsp; __uint32_t events;&nbsp; /* Epoll events */<br>&nbsp;
epoll_data_t data;&nbsp; /* User data variable */<br>};<br><br>events可以是以下几个宏
的集合：<br>EPOLLIN ：表示对应的文件描述符可以读（包括对端SOCKET正常关闭）；<br>EPOLLOUT：表示对应的文件描述符可以
写；<br>EPOLLPRI：表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）；<br>EPOLLERR：表示对应的文件描述符
发生错误；<br>EPOLLHUP：表示对应的文件描述符被挂断；<br>EPOLLET： 将EPOLL设为边缘触发(Edge
Triggered)模式，这是相对于水平触发(Level Triggered)来说的。<br>EPOLLONESHOT：只监听一次事件，当监听完
这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里<br><br><br><span style="color: #ff0102;">3. int epoll_wait(int epfd, struct
epoll_event * events, int maxevents, int timeout);</span><br>等待事件的产生，类似于
select()调用。参数events用来从内核得到事件的集合，maxevents告之内核这个events有多大，这个maxevents的值不能
大于创建epoll_create()时的size，参数timeout是超时时间（毫秒，0会立即返回，-1将不确定，也有说法说是永久阻塞）。该函数
返回需要处理的事件数目，如返回0表示已超时。<br><br>--------------------------------------------------------------------------------------------<br><br>从
man手册中，得到ET和LT的具体描述如下<br><br>EPOLL事件有两种模型：<br>Edge Triggered (ET)<br>Level
Triggered (LT)<br><br>假如有这样一个例子：<br>1.
我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符<br>2. 这个时候从管道的另一端被写入了2KB的数据<br>3.
调用epoll_wait(2)，并且它会返回RFD，说明它已经准备好读取操作<br>4. 然后我们读取了1KB的数据<br>5.
调用epoll_wait(2)......<br><br>Edge Triggered 工作模式：<br>如果我们在第1步将RFD添加到
epoll描述符的时候使用了EPOLLET标志，那么在第5步调用epoll_wait(2)之后将有可能会挂起，因为剩余的数据还存在于文件的输入缓
冲区内，而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET
工作模式才会汇报事件。因此在第5步的时候，调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中，会有一个事件产生在RFD句柄
上，因为在第2步执行了一个写操作，然后，事件将会在第3步被销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据，因此我们在第5步调用
epoll_wait(2)完成后，是否挂起是不确定的。epoll工作在ET模式的时候，必须使用非阻塞套接口，以避免由于一个文件句柄的阻塞读/阻塞
写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口，在后面会介绍避免可能的缺陷。<br>&nbsp;&nbsp; i&nbsp;&nbsp;&nbsp;
基于非阻塞文件句柄<br>&nbsp;&nbsp; ii&nbsp;&nbsp; 只有当read(2)或者write(2)返回EAGAIN时才需要挂起，等待。<span style="font-weight: bold; color: #0001ff;">但这并不是说每次read()时都需要循环读，
直到读到产生一个EAGAIN才认为此次事件处理完成，当read()返回的读到的数据长度小于请求的数据长度时，就可以确定此时缓冲中已没有数据了，也
就可以认为此事读事件已处理完成。</span><br><br>Level Triggered 工作模式<br>相反的，以LT方式调用epoll接
口的时候，它就相当于一个速度比较快的poll(2)，并且无论后面的数据是否被使用，因此他们具有同样的职能。因为即使使用ET模式的epoll，在收
到多个chunk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标志，在
epoll_wait(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。因此当EPOLLONESHOT设定后，使用带有
EPOLL_CTL_MOD标志的epoll_ctl(2)处理文件句柄就成为调用者必须作的事情。<br><br><br>然后详细解释ET, LT:<br><br>LT(level
triggered)是缺省的工作方式，并且同时支持block和no-block
socket.在这种做法中，内核告诉你一个文件描述符是否就绪了，然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作，内核还是会继续通知你
的，所以，这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表．<br><br>ET(edge-triggered)
是高速工作方式，只支持no-block
socket。在这种模式下，当描述符从未就绪变为就绪时，内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪，并且不会再为那个文件描述
符发送更多的就绪通知，直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如，你在发送，接收或者接收请求，或者发送接收的数据少于一定量时导致
了一个EWOULDBLOCK 错误）。但是请注意，如果一直不对这个fd作IO操作(从而导致它再次变成未就绪)，内核不会发送更多的通知(only
once),<span style="font-weight: bold; font-style: italic;">不过在TCP协议中，ET模
式的加速效用仍需要更多的benchmark确认（这句话不理解）。</span><br><br><span style="font-style: italic;">在许多测试中我们会看到如果没有大量的idle
-connection或者dead-connection，epoll的效率并不会比select/poll高很多，但是当我们遇到大量的idle-
connection(例如WAN环境中存在大量的慢速连接)，就会发现epoll的效率大大高于select/poll。（未测试）</span><br><br><br><br>另
外，当使用epoll的ET模型来工作时，当产生了一个EPOLLIN事件后，<br><span style="color: #ff0102;">读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小，那么很有可能是缓冲区还有数据未读完，也意味着该次事件还没有处理
完，所以还需要再次读取</span>：<br>while(rs)<br>{<br>&nbsp; buflen =
recv(activeevents[i].data.fd, buf, sizeof(buf), 0);<br>&nbsp; if(buflen &lt;
0)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读<br>&nbsp;&nbsp;&nbsp; //
在这里就当作是该次事件已处理处.<br>&nbsp;&nbsp;&nbsp; if(errno == EAGAIN)<br>&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;
return;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; else if(buflen == 0)<br>&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp; //
这里表示对端的socket已正常关闭.<br>&nbsp;&nbsp; }<br><span style="color: #ff0102;">&nbsp;&nbsp;
if(buflen == sizeof(buf)</span><br style="color: #ff0102;"><span style="color: #ff0102;">&nbsp;&nbsp;&nbsp;&nbsp; rs = 1;&nbsp;&nbsp; // 需要再次读取</span><br>&nbsp;&nbsp;
else<br>&nbsp;&nbsp;&nbsp;&nbsp; rs = 0;<br>}<br><br><br><span style="font-weight: bold; color: #ff0102;">还有，假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由
于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发，当缓冲区满后会产生EAGAIN错
误(参考man
send),同时,不理会这次请求发送的数据.所以,需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回，返回
-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并
不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.</span><br><br>ssize_t
socket_send(int sockfd, const char* buffer, size_t buflen)<br>{<br>&nbsp;
ssize_t tmp;<br>&nbsp; size_t total = buflen;<br>&nbsp; const char *p = buffer;<br><br>&nbsp;
while(1)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; tmp = send(sockfd, p, total, 0);<br>&nbsp;&nbsp;&nbsp; if(tmp
&lt; 0)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 当send收到信号时,可以继续写,但这里返回-1.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno
== EINTR)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //
当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 在这里做延时后再重试.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno
== EAGAIN)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; usleep(1000);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; if((size_t)tmp == total)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
return buflen;<br><br>&nbsp;&nbsp;&nbsp; total -= tmp;<br>&nbsp;&nbsp;&nbsp; p += tmp;<br>&nbsp; }<br><br>&nbsp;
return tmp;<br>}<br><br>from:<br><a  href="http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html">http://www.cnblogs.com/OnlyXP/archive/2007/08/10/851222.html</a><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/114627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-05-06 15:12 <a href="http://www.cppblog.com/beautykingdom/archive/2010/05/06/114627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll用法说明</title><link>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114589.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 06 May 2010 04:03:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114589.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/114589.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/05/06/114589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/114589.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/114589.html</trackback:ping><description><![CDATA[<table style="border-collapse: collapse; word-wrap: break-word;" width="100%" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td align="center" height="25"><font style="font-size: 14pt;" color="#02368d"><strong>[转]epoll用法说明(源代码) </strong></font><br>
            </td>
        </tr>
        <tr>
            <td bgcolor="#d2dee2" height="1"><br></td>
        </tr>
        <tr>
            <td bgcolor="#ffffff" height="1"><br></td>
        </tr>
        <tr>
            <td align="center">
            <table style="border-collapse: collapse; word-wrap: break-word;" width="100%" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td width="100%">
                        <div id="art" width="100%" style="margin: 15px;">
                        <div>
                        <table style="border-collapse: collapse; word-wrap: break-word;" width="100%" border="0" cellpadding="0" cellspacing="0">
                            <tbody>
                                <tr>
                                    <td align="middle">
                                    <table style="border-collapse: collapse; word-wrap: break-word;" width="100%" border="0" cellpadding="0" cellspacing="0">
                                        <tbody>
                                            <tr>
                                                <td width="100%">
                                                <div id="art" style="margin: 15px;" width="100%">
                                                <p><span id="ArticleContent1_ArticleContent1_lblContent">epoll用到的所有函数都是在
                                                头文件sys/epoll.h中声明的，下面简要说明所用到的数据结构和函数：<br>所用到的数据结构<br>typedef union
                                                epoll_data {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                __uint32_t u32;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint64_t u64;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
                                                epoll_data_t;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                __uint32_t events;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Epoll events */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                epoll_data_t data;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* User data variable */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>结构体
                                                epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件，其中epoll_data
                                                联合体用来保存触发事件的某个文件描述符相关的数据，例如一个client连接到服务器，服务器通过调用accept函数可以得到于这个client对应
                                                的socket文件描述符，可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。epoll_event
                                                结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为：<strong>EPOLLIN</strong>
                                                ：表示对应的文件描述符可以读；<br><strong>EPOLLOUT：</strong>表示对应的文件描述符可以写；<br><strong>EPOLLPRI：</strong>表
                                                示对应的文件描述符有紧急的数据可读（我不太明白是什么意思，可能是类似client关闭&nbsp; socket连接这样的事件）；<br><strong>EPOLLERR：</strong>表
                                                示对应的文件描述符发生错误；<br><strong>EPOLLHUP：</strong>表示对应的文件描述符被挂断；<br><strong>EPOLLET：</strong>表
                                                示对应的文件描述符有事件发生；<br>所用到的函数：<br>1、epoll_create函数<br>&nbsp;&nbsp;&nbsp;&nbsp; 函数声明：<strong>int
                                                epoll_create(int</strong> size<strong>)</strong> <br>&nbsp;&nbsp;&nbsp;
                                                该函数生成一个epoll专用的文件描述符，其中的参数是指定生成描述符的最大范围（我觉得这个参数和select函数的第一个参数应该是类似的但是该怎
                                                么设置才好，我也不太清楚）。<br>2、epoll_ctl函数<br>&nbsp;&nbsp;&nbsp;&nbsp; 函数声明：<strong>int epoll_ctl(int</strong>
                                                epfd<strong>, int</strong> op<strong>, int</strong> fd<strong>, struct
                                                epoll_event *</strong>event<strong>)<br>&nbsp;&nbsp;&nbsp;&nbsp; </strong>该函数用于控制某个文件描述符上的事
                                                件，可以注册事件，修改事件，删除事件。<br>&nbsp;&nbsp;&nbsp; 参数：epfd：由<strong> epoll_create</strong>
                                                生成的epoll专用的文件描述符；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; op：要进行的操作例如注册事件，可能的取值<strong>EPOLL_CTL_ADD</strong>
                                                注册、<strong>EPOLL_CTL_MOD</strong> 修<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 改、<strong>EPOLL_CTL_DEL</strong>
                                                删除<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd：关联的文件描述符；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                event：指向epoll_event的指针；<br>&nbsp;&nbsp;&nbsp; 如果调用成功返回0,不成功返回-1<br>3、<font size="2">epoll_wait<font size="3">函数<br>函数声明:<strong>int epoll_wait(int</strong> epfd,<strong>struct
                                                epoll_event *</strong> events,<strong>int</strong> maxevents,<strong>int</strong>
                                                timeout)</font></font><br>该函数用于轮询I/O事件的发生；<br>参数：<br>epfd:由<strong>epoll_create</strong>
                                                生成的epoll专用的文件描述符；<br>epoll_event:用于回传代处理事件的数组；<br>maxevents:每次能处理的事件数；<br>timeout:
                                                等待I/O事件发生的超时值；<br>返回发生事件数。<br>例子：</span></p>
                                                <p><span><br></span></p>
                                                <div>
                                                <div align="left"><span style="font-size: 9pt;">&nbsp;#include
                                                &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;errno.h&gt;<br>#include
                                                &lt;string.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include
                                                &lt;netinet/in.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#include
                                                &lt;sys/wait.h&gt;<br>#include &lt;unistd.h&gt;<br>#include
                                                &lt;arpa/inet.h&gt;<br>#include &lt;openssl/ssl.h&gt;<br>#include
                                                &lt;openssl/err.h&gt;<br>#include &lt;fcntl.h&gt;<br>#include
                                                &lt;sys/epoll.h&gt;<br>#include &lt;sys/time.h&gt;<br>#include
                                                &lt;sys/resource.h&gt;<br><br><br>#define MAXBUF 1024<br>#define
                                                MAXEPOLLSIZE 10000<br><br>/*<br>setnonblocking - 设置句柄为非阻塞方式<br>*/<br>int
                                                setnonblocking(int sockfd)<br>{<br>&nbsp;&nbsp;&nbsp; if (fcntl(sockfd, F_SETFL,
                                                fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;
                                                }<br>&nbsp;&nbsp;&nbsp; return 0;<br>}<br><br>/*<br>handle_message - 处理每个 socket
                                                上的消息收发<br>*/<br>int handle_message(int new_fd)<br>{<br>&nbsp;&nbsp;&nbsp; char
                                                buf[MAXBUF + 1];<br>&nbsp;&nbsp;&nbsp; int len;<br>&nbsp;&nbsp;&nbsp; /* 开始处理每个新连接上的数据收发 */<br>&nbsp;&nbsp;&nbsp;
                                                bzero(buf, MAXBUF + 1);<br>&nbsp;&nbsp;&nbsp; /* 接收客户端的消息 */<br>&nbsp;&nbsp;&nbsp; len = recv(new_fd,
                                                buf, MAXBUF, 0);<br>&nbsp;&nbsp;&nbsp; if (len &gt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                ("%d接收消息成功:'%s'，共%d个字节的数据\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new_fd, buf, len);<br>&nbsp;&nbsp;&nbsp;
                                                else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (len &lt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                ("消息接收失败！错误代码是%d，错误信息是'%s'\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errno,
                                                strerror(errno));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(new_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;
                                                }<br>&nbsp;&nbsp;&nbsp; /* 处理每个新连接上的数据收发结束 */<br>&nbsp;&nbsp;&nbsp; return len;<br>}<br>/************
                                                关于本文档********************************************<br>*filename:
                                                epoll-server.c<br>*purpose: 演示epoll处理海量socket连接的方法<br>*wrote by:
                                                zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)<br>Linux爱好者
                                                Linux知识传播者 SOHO族 开发者 最擅长C语言<br>*date time:2007-01-31 21:00<br>*Note:
                                                任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<br>* 但请遵循GPL<br>*Thanks to:Google<br>*Hope:
                                                希望越来越多的人贡献自己的力量，为科学技术发展出力<br>* 科技站在巨人的肩膀上进步更快！感谢有开源前辈的贡献！<br>*********************************************************************/<br>int
                                                main(int argc, char **argv)<br>{<br>&nbsp;&nbsp;&nbsp; int listener, new_fd, kdpfd,
                                                nfds, n, ret, curfds;<br>&nbsp;&nbsp;&nbsp; socklen_t len;<br>&nbsp;&nbsp;&nbsp; struct sockaddr_in
                                                my_addr, their_addr;<br>&nbsp;&nbsp;&nbsp; unsigned int myport, lisnum;<br>&nbsp;&nbsp;&nbsp; struct
                                                epoll_event ev;<br>&nbsp;&nbsp;&nbsp; struct epoll_event events[MAXEPOLLSIZE];<br>&nbsp;&nbsp;&nbsp;
                                                struct rlimit rt;<br><br>&nbsp;&nbsp;&nbsp; if (argv[1])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myport =
                                                atoi(argv[1]);<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myport = 7838;<br><br>&nbsp;&nbsp;&nbsp; if
                                                (argv[2])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lisnum = atoi(argv[2]);<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                lisnum = 2;<br><br>&nbsp;&nbsp;&nbsp; /* 设置每个进程允许打开的最大文件数 */<br>&nbsp;&nbsp;&nbsp; rt.rlim_max =
                                                rt.rlim_cur = MAXEPOLLSIZE;<br>&nbsp;&nbsp;&nbsp; if (setrlimit(RLIMIT_NOFILE, &amp;rt)
                                                == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("setrlimit");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;
                                                else printf("设置系统资源参数成功！\n");<br><br>&nbsp;&nbsp;&nbsp; /* 开启 socket 监听 */<br>&nbsp;&nbsp;&nbsp; if
                                                ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                perror("socket");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<br>&nbsp;&nbsp;&nbsp; } else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                printf("socket 创建成功！\n");<br><br>&nbsp;&nbsp;&nbsp; setnonblocking(listener);<br><br>&nbsp;&nbsp;&nbsp;
                                                bzero(&amp;my_addr, sizeof(my_addr));<br>&nbsp;&nbsp;&nbsp; my_addr.sin_family =
                                                PF_INET;<br>&nbsp;&nbsp;&nbsp; my_addr.sin_port = htons(myport);<br>&nbsp;&nbsp;&nbsp; if (argv[3])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                my_addr.sin_addr.s_addr = inet_addr(argv[3]);<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                my_addr.sin_addr.s_addr = INADDR_ANY;<br><br>&nbsp;&nbsp;&nbsp; if (bind<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                (listener, (struct sockaddr *) &amp;my_addr, sizeof(struct sockaddr))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("bind");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<br>&nbsp;&nbsp;&nbsp; } else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                printf("IP 地址和端口绑定成功\n");<br><br>&nbsp;&nbsp;&nbsp; if (listen(listener, lisnum) ==
                                                -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("listen");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<br>&nbsp;&nbsp;&nbsp; } else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                printf("开启服务成功！\n");<br><br>&nbsp;&nbsp;&nbsp; /* 创建 epoll 句柄，把监听 socket 加入到 epoll 集合里
                                                */<br>&nbsp;&nbsp;&nbsp; kdpfd = epoll_create(MAXEPOLLSIZE);<br>&nbsp;&nbsp;&nbsp; len =
                                                sizeof(struct sockaddr_in);<br>&nbsp;&nbsp;&nbsp; ev.events = EPOLLIN | EPOLLET;<br>&nbsp;&nbsp;&nbsp;
                                                ev.data.fd = listener;<br>&nbsp;&nbsp;&nbsp; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD,
                                                listener, &amp;ev) &lt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "epoll set
                                                insertion error: fd=%d\n", listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp; }
                                                else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("监听 socket 加入 epoll 成功！\n");<br>&nbsp;&nbsp;&nbsp; curfds = 1;<br>&nbsp;&nbsp;&nbsp;
                                                while (1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 等待有事件发生 */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds =
                                                epoll_wait(kdpfd, events, curfds, -1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nfds == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                perror("epoll_wait");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*
                                                处理所有事件 */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (n = 0; n &lt; nfds; ++n) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if
                                                (events[n].data.fd == listener) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new_fd =
                                                accept(listener, (struct sockaddr *) &amp;their_addr,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                &amp;len);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (new_fd &lt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                perror("accept");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
                                                else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("有连接来自于： %d:%d， 分配的 socket 为:%d\n",
                                                inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                setnonblocking(new_fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.events = EPOLLIN |
                                                EPOLLET;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.data.fd = new_fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if
                                                (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_fd, &amp;ev) &lt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                fprintf(stderr, "把 socket '%d' 加入 epoll 失败！%s\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                new_fd, strerror(errno));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; curfds++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                ret = handle_message(events[n].data.fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ret
                                                &lt; 1 &amp;&amp; errno != 11) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl(kdpfd,
                                                EPOLL_CTL_DEL, events[n].data.fd,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                &amp;ev);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; curfds--;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; close(listener);<br>&nbsp;&nbsp;&nbsp; return 0;<br>}<br><br>编
                                                译此程序用命令：<br>gcc -Wall epoll-server.c -o server<br><br>运行此程序需要具有管理员权限！<br><br>sudo
                                                ./server 7838 1<br><br>通过测试这一个服务器可能同时处理10000 -3 = 9997 个连接！<br><br>如果这是
                                                一个在线服务系统，那么它可以支持9997人同时在线，比如游戏、聊天等。</span></div>
                                                </div>
                                                </div>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                    </td>
                                </tr>
                                <tr>
                                    <td height="25">&nbsp;<font color="#000099"><strong>原文地址</strong></font> <a href="http://blog.chinaunix.net/u/8818/showart_440623.html" target="_blank"><font color="#0000ff">http://blog.chinaunix.net/u/8818/showart_440623.html</font></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </div>
                        </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table><img src ="http://www.cppblog.com/beautykingdom/aggbug/114589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-05-06 12:03 <a href="http://www.cppblog.com/beautykingdom/archive/2010/05/06/114589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EPoll Mechanism</title><link>http://www.cppblog.com/beautykingdom/archive/2010/04/24/100420.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sat, 24 Apr 2010 14:41:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/04/24/100420.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/100420.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/04/24/100420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/100420.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/100420.html</trackback:ping><description><![CDATA[<div class="cnt" id="blog_text">
<pre class="text"><font size="3">1 功能介绍<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll与select/poll不同的一点是，它是由一组系统调用组成。<br>&nbsp;&nbsp;&nbsp;&nbsp; int epoll_create(int size);<br>&nbsp;&nbsp;&nbsp;&nbsp; int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);<br>&nbsp;&nbsp;&nbsp;&nbsp; int epoll_wait(int epfd, struct epoll_event *events,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int maxevents, int timeout);<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll相关系统调用是在Linux 2.5.44开始引入的。该系统调用针对传统的selec<br>t/poll系统调用的不足，设计上作了很大的改动。select/poll的缺点在于：<br>&nbsp;&nbsp;&nbsp;&nbsp; 1.每次调用时要重复地从用户态读入参数。<br>&nbsp;&nbsp;&nbsp;&nbsp; 2.每次调用时要重复地扫描文件描述符。<br>&nbsp;&nbsp;&nbsp;&nbsp; 3.每次在调用开始时，要把当前进程放入各个文件描述符的等待队列。在调用结<br>束后，又把进程从各个等待队列中删除。<br>&nbsp;&nbsp;&nbsp;&nbsp; 在实际应用中，select/poll监视的文件描述符可能会非常多，如果每次只是返回<br>一小部分，那么，这种情况下select/poll显得不够高效。epoll的设计思路，是把s<br>elect/poll单个的操作拆分为1个epoll_create+多个epoll_ctrl+一个wait。此外，<br>内核针对epoll操作添加了一个文件系统&#8221;eventpollfs&#8221;，每一个或者多个要监视的<br>文件描述符都有一个对应的eventpollfs文件系统的inode节点，主要信息保存在eve<br>ntpoll结构体中。而被监视的文件的重要信息则保存在epitem结构体中。所以他们<br>是一对多的关系。<br>&nbsp;&nbsp;&nbsp;&nbsp; 由于在执行epoll_create和epoll_ctrl时，已经把用户态的信息保存到内核态了<br>，所以之后即使反复地调用epoll_wait，也不会重复地拷贝参数，扫描文件描述符，<br>反复地把当前进程放入/放出等待队列。这样就避免了以上的三个缺点。<br>&nbsp;&nbsp;&nbsp;&nbsp; 接下去看看它们的实现：<br>2 关键结构体：<br>/* Wrapper struct used by poll queueing */<br>struct ep_pqueue {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; poll_table pt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epitem *epi;<br>};<br>&nbsp;&nbsp;&nbsp;&nbsp; 这个结构体类似于select/poll中的struct poll_wqueues。由于epoll需要在内核<br>态保存大量信息，所以光光一个回调函数指针已经不能满足要求，所以在这里引入了<br>一个新的结构体struct epitem。<br>/*<br> * Each file descriptor added to the eventpoll interface will<br> * have an entry of this type linked to the hash.<br> */<br>struct epitem {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* RB-Tree node used to link this structure to the eventpoll rb<br>-tree */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct rb_node rbn;<br>红黑树，用来保存eventpoll<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List header used to link this structure to the eventpoll rea<br>dy list */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head rdllink;<br>双向链表，用来保存已经完成的eventpoll<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* The file descriptor information this item refers to */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_filefd ffd;<br>这个结构体对应的被监听的文件描述符信息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Number of active wait queue attached to poll operations */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nwait;<br>poll操作中事件的个数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List containing poll wait queues */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head pwqlist;<br>双向链表，保存着被监视文件的等待队列，功能类似于select/poll中的poll_tab<br>le<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* The "container" of this item */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct eventpoll *ep;<br>指向eventpoll，多个epitem对应一个eventpoll<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* The structure that describe the interested events and the so<br>urce fd */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event event;<br>记录发生的事件和对应的fd<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Used to keep track of the usage count of the structure. This<br> avoids<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * that the structure will desappear from underneath our proces<br>sing.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_t usecnt;<br>引用计数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List header used to link this item to the "struct file" item<br>s list */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head fllink;<br>双向链表，用来链接被监视的文件描述符对应的struct file。因为file里有f_ep<br>_link，用来保存所有监视这个文件的epoll节点<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List header used to link the item to the transfer list */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head txlink;<br>双向链表，用来保存传输队列<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * This is used during the collection/transfer of events to use<br>rspace<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * to pin items empty events set.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int revents;<br>文件描述符的状态，在收集和传输时用来锁住空的事件集合<br>};<br>&nbsp;&nbsp;&nbsp;&nbsp; 该结构体用来保存与epoll节点关联的多个文件描述符，保存的方式是使用红黑树<br>实现的hash表。至于为什么要保存，下文有详细解释。它与被监听的文件描述符一一<br>对应。<br>struct eventpoll {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Protect the this structure access */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rwlock_t lock;<br>读写锁<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * This semaphore is used to ensure that files are not removed<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * while epoll is using them. This is read-held during the even<br>t<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * collection loop and it is write-held during the file cleanup<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * path, the epoll file exit code and the ctl operations.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct rw_semaphore sem;<br>读写信号量<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Wait queue used by sys_epoll_wait() */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_head_t wq;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Wait queue used by file-&gt;poll() */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_head_t poll_wait;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List of ready file descriptors */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head rdllist;<br>已经完成的操作事件的队列。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* RB-Tree root used to store monitored fd structs */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct rb_root rbr;<br>保存epoll监视的文件描述符<br>};<br>&nbsp;&nbsp;&nbsp;&nbsp; 这个结构体保存了epoll文件描述符的扩展信息，它被保存在file结构体的priva<br>te_data中。它与epoll文件节点一一对应。通常一个epoll文件节点对应多个被监视<br>的文件描述符。所以一个eventpoll结构体会对应多个epitem结构体。<br>&nbsp;&nbsp;&nbsp;&nbsp; 那么，epoll中的等待事件放在哪里呢？见下面<br>/* Wait structure used by the poll hooks */<br>struct eppoll_entry {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* List header used to link this structure to the "struct epite<br>m" */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct list_head llink;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* The "base" pointer is set to the container "struct epitem" *<br>/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *base;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Wait queue item that will be linked to the target file wait<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * queue head.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_t wait;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* The wait queue head that linked the "wait" wait queue item *<br>/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_head_t *whead;<br>};<br>&nbsp;&nbsp;&nbsp;&nbsp; 与select/poll的struct poll_table_entry相比，epoll的表示等待队列节点的结<br>构体只是稍有不同，与struct poll_table_entry比较一下。<br>struct poll_table_entry {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct file * filp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_t wait;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait_queue_head_t * wait_address;<br>};<br>&nbsp;&nbsp;&nbsp;&nbsp; 由于epitem对应一个被监视的文件，所以通过base可以方便地得到被监视的文件<br>信息。又因为一个文件可能有多个事件发生，所以用llink链接这些事件。<br>3 epoll_create的实现<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll_create()的功能是创建一个eventpollfs文件系统的inode节点。具体由ep<br>_getfd()完成。ep_getfd()先调用ep_eventpoll_inode()创建一个inode节点，然后<br>调用d_alloc()为inode分配一个dentry。最后把file,dentry,inode三者关联起来。<br>&nbsp;&nbsp;&nbsp;&nbsp; 在执行了ep_getfd()之后，它又调用了ep_file_init(),分配了eventpoll结构体<br>，并把eventpoll的指针赋给file结构体，这样eventpoll就与file结构体关联起来了<br>。<br>&nbsp;&nbsp;&nbsp;&nbsp; 需要注意的是epoll_create()的参数size实际上只是起参考作用，只要它不小于<br>等于0，就并不限制这个epoll inode关联的文件描述符数量。<br>4 epoll_ctl的实现<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl的功能是实现一系列操作，如把文件与eventpollfs文件系统的inode节<br>点关联起来。这里要介绍一下eventpoll结构体，它保存在file-&gt;f_private中，记录<br>了eventpollfs文件系统的inode节点的重要信息，其中成员rbr保存了该epoll文件节<br>点监视的所有文件描述符。组织的方式是一棵红黑树，这种结构体在查找节点时非常<br>高效。<br>&nbsp;&nbsp;&nbsp;&nbsp; 首先它调用ep_find()从eventpoll中的红黑树获得epitem结构体。然后根据op参<br>数的不同而选择不同的操作。如果op为EPOLL_CTL_ADD，那么正常情况下epitem是不<br>可能在eventpoll的红黑树中找到的，所以调用ep_insert创建一个epitem结构体并插<br>入到对应的红黑树中。<br>&nbsp;&nbsp;&nbsp;&nbsp; ep_insert()首先分配一个epitem对象，对它初始化后，把它放入对应的红黑树。<br>此外，这个函数还要作一个操作，就是把当前进程放入对应文件操作的等待队列。这<br>一步是由下面的代码完成的。<br>&nbsp;&nbsp;&nbsp;&nbsp; init_poll_funcptr(&amp;epq.pt, ep_ptable_queue_proc);<br>&nbsp;&nbsp;&nbsp;&nbsp; 。。。<br>&nbsp;&nbsp;&nbsp;&nbsp; revents = tfile-&gt;f_op-&gt;poll(tfile, &amp;epq.pt);<br>&nbsp;&nbsp;&nbsp;&nbsp; 函数先调用init_poll_funcptr注册了一个回调函数 ep_ptable_queue_proc，这<br>个函数会在调用f_op-&gt;poll时被执行。该函数分配一个epoll等待队列结点eppoll_e<br>ntry：一方面把它挂到文件操作的等待队列中，另一方面把它挂到epitem的队列中<br>。此外，它还注册了一个等待队列的回调函数ep_poll_callback。当文件操作完成，<br>唤醒当前进程之前，会调用ep_poll_callback()，把eventpoll放到epitem的完成队<br>列中，并唤醒等待进程。<br>&nbsp;&nbsp;&nbsp;&nbsp; 如果在执行f_op-&gt;poll以后，发现被监视的文件操作已经完成了，那么把它放在<br>完成队列中了，并立即把等待操作的那些进程唤醒。<br>5 epoll_wait的实现<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait的工作是等待文件操作完成并返回。<br>&nbsp;&nbsp;&nbsp;&nbsp; 它的主体是ep_poll()，该函数在for循环中检查epitem中有没有已经完成的事件<br>，有的话就把结果返回。没有的话调用schedule_timeout()进入休眠，直到进程被再<br>度唤醒或者超时。<br>6 性能分析<br>&nbsp;&nbsp;&nbsp;&nbsp; epoll机制是针对select/poll的缺陷设计的。通过新引入的eventpollfs文件系统<br>，epoll把参数拷贝到内核态，在每次轮询时不会重复拷贝。通过把操作拆分为epol<br>l_create,epoll_ctl,epoll_wait，避免了重复地遍历要监视的文件描述符。此外，<br>由于调用epoll的进程被唤醒后，只要直接从epitem的完成队列中找出完成的事件，<br>找出完成事件的复杂度由O(N)降到了O(1)。<br>&nbsp;&nbsp;&nbsp;&nbsp; 但是epoll的性能提高是有前提的，那就是监视的文件描述符非常多，而且每次完<br>成操作的文件非常少。所以，epoll能否显著提高效率，取决于实际的应用场景。这<br>方面需要进一步测试。</font></pre>
<p><font size="3">转自</font><a  href="http://www.freecity.cn/agent/thread.do?id=LinuxDev-48b24eba-c4e53e6f2d89ff3cb039f2c4ed4102e9&amp;page=0&amp;bd=LinuxDev&amp;bp=0&amp;m=2"><font color="#0000ff" size="3">http://www.freecity.cn/agent/thread.do?id=LinuxDev-48b24eba-c4e53e6f2d89ff3cb039f2c4ed4102e9</font></a></p>
<p><a  href="http://www.freecity.cn/agent/thread.do?id=LinuxDev-48b24eba-c4e53e6f2d89ff3cb039f2c4ed4102e9&amp;page=0&amp;bd=LinuxDev&amp;bp=0&amp;m=2"><font color="#0000ff" size="3">from:</font></a></p>
<p><a  href="http://www.freecity.cn/agent/thread.do?id=LinuxDev-48b24eba-c4e53e6f2d89ff3cb039f2c4ed4102e9&amp;page=0&amp;bd=LinuxDev&amp;bp=0&amp;m=2"><font color="#0000ff" size="3">http://blog.chinaunix.net/u2/67780/showart_2064403.html<br></font></a></p>
</div><img src ="http://www.cppblog.com/beautykingdom/aggbug/100420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-04-24 22:41 <a href="http://www.cppblog.com/beautykingdom/archive/2010/04/24/100420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll使用例子</title><link>http://www.cppblog.com/beautykingdom/archive/2010/01/10/105365.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sun, 10 Jan 2010 14:46:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/01/10/105365.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/105365.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/01/10/105365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/105365.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/105365.html</trackback:ping><description><![CDATA[<span style="border-collapse: collapse; font-family: song,Verdana; font-size: 12px;">
<p minmax_bound="true" style="font: 12px song,Verdana;"><strong minmax_bound="true">名词解释：</strong>man epoll之后，得到如下结果：</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">NAME<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll - I/O event notification facility</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">SYNOPSIS<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/epoll.h&gt;</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">DESCRIPTION<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll is a variant of poll(2) that can be used either as Edge or Level<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Triggered interface and scales well to large numbers of&nbsp; watched&nbsp; fds.<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Three&nbsp; system&nbsp; calls&nbsp; are provided to set up and control an epoll set:<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_create(2), epoll_ctl(2), epoll_wait(2).</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; An epoll set is connected to a file descriptor created&nbsp; by&nbsp; epoll_cre-<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ate(2).&nbsp;&nbsp; Interest for certain file descriptors is then registered via<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl(2).&nbsp; Finally, the actual wait is started by epoll_wait(2).</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">其实，一切的解释都是多余的，按照我目前的了解，EPOLL模型似乎只有一种格式，所以大家只要参考我下面的代码，就能够对EPOLL有所了解了，代码的解释都已经在注释中：</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">while (TRUE)<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT);//等待EPOLL时间的发生，相当于监听，至于相关的端口，需要在初始化EPOLL的时候绑定。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;if (nfds &lt;= 0)<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;continue;<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;m_bOnTimeChecking = FALSE;<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;G_CurTime = time(NULL);<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;for (int i=0; i&lt;nfds; i++)<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;try<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;if (m_events[i].data.fd == m_listen_http_fd)//如果新监测到一个HTTP用户连接到绑定的HTTP端口，建立新的连接。由于我们新采用了SOCKET连接，所以基本没用。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnAcceptHttpEpoll ();<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;}<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;else if (m_events[i].data.fd == m_listen_sock_fd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口，建立新的连接。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnAcceptSockEpoll ();<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;}<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;else if (m_events[i].events &amp; EPOLLIN)//如果是已经连接的用户，并且收到数据，那么进行读入。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnReadEpoll (i);<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;OnWriteEpoll (i);//查看当前的活动连接是否有需要写出的数据。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;}<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;catch (int)<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;{<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;PRINTF ("CATCH捕获错误\n");<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;continue;<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;}<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;}<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;m_bOnTimeChecking = TRUE;<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;&nbsp;OnTimer ();//进行一些定时的操作，主要就是删除一些短线用户等。<br minmax_bound="true" style="font: 12px song,Verdana;">&nbsp;}</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">　其实EPOLL的精华，按照我目前的理解，也就是上述的几段短短的代码，看来时代真的不同了，以前如何接受大量用户连接的问题，现在却被如此轻松的搞定，真是让人不得不感叹。</p>
<p minmax_bound="true" style="font: 12px song,Verdana;">今天搞了一天的epoll，想做一个高并发的代理程序。刚开始真是郁闷,一直搞不通，网上也有几篇介绍epoll的文章。但都不深入，没有将一些注意的地方讲明。以至于走了很多弯路，现将自己的一些理解共享给大家,以少走弯路。&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">epoll用到的所有函数都是在头文件sys/epoll.h中声明，有什么地方不明白或函数忘记了可以去看一下。&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">epoll和select相比，最大不同在于:&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;"><font minmax_bound="true" color="#ff0000">1epoll返回时已经明确的知道哪个sokcet fd发生了事件，不用再一个个比对。这样就提高了效率。&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">2select的FD_SETSIZE是有限止的，而epoll是没有限止的只与系统资源有关。</font>&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">1、epoll_create函数<br minmax_bound="true" style="font: 12px song,Verdana;">函数声明：<font minmax_bound="true" color="#0000ff">int epoll_create(int size)</font><br minmax_bound="true" style="font: 12px song,Verdana;">该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间，用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同2.&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">22、epoll_ctl函数&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">函数声明：<font minmax_bound="true" color="#0000ff">int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)</font>&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">该函数用于控制某个epoll文件描述符上的事件，可以注册事件，修改事件，删除事件。&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">参数：&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">epfd：由 epoll_create 生成的epoll专用的文件描述符；&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">op：要进行的操作例如注册事件，可能的取值<font minmax_bound="true" color="#0000ff">EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除</font>&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">fd：关联的文件描述符；&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">event：指向epoll_event的指针；&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">如果调用成功返回0,不成功返回-1&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">用到的数据结构&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><font minmax_bound="true" color="#0000ff">typedef union epoll_data {&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">void *ptr;<br minmax_bound="true" style="font: 12px song,Verdana;">int fd;<br minmax_bound="true" style="font: 12px song,Verdana;">__uint32_t u32;<br minmax_bound="true" style="font: 12px song,Verdana;">__uint64_t u64;<br minmax_bound="true" style="font: 12px song,Verdana;">} epoll_data_t;&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">struct epoll_event {<br minmax_bound="true" style="font: 12px song,Verdana;">__uint32_t events; /* Epoll events */<br minmax_bound="true" style="font: 12px song,Verdana;">epoll_data_t data; /* User data variable */<br minmax_bound="true" style="font: 12px song,Verdana;">};<br minmax_bound="true" style="font: 12px song,Verdana;"></font><br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">如：&nbsp;<br minmax_bound="true" style="font: 12px song,Verdana;">struct epoll_event ev;<br minmax_bound="true" style="font: 12px song,Verdana;">//设置与要处理的事件相关的文件描述符<br minmax_bound="true" style="font: 12px song,Verdana;">ev.data.fd=listenfd;<br minmax_bound="true" style="font: 12px song,Verdana;">//设置要处理的事件类型<br minmax_bound="true" style="font: 12px song,Verdana;">ev.events=EPOLLIN|EPOLLET;<br minmax_bound="true" style="font: 12px song,Verdana;">//注册epoll事件<br minmax_bound="true" style="font: 12px song,Verdana;">epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&amp;ev);<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">常用的事件类型:<br minmax_bound="true" style="font: 12px song,Verdana;"><font minmax_bound="true" color="#0000ff">EPOLLIN ：表示对应的文件描述符可以读；<br minmax_bound="true" style="font: 12px song,Verdana;">EPOLLOUT：表示对应的文件描述符可以写；<br minmax_bound="true" style="font: 12px song,Verdana;">EPOLLPRI：表示对应的文件描述符有紧急的数据可读<br minmax_bound="true" style="font: 12px song,Verdana;">EPOLLERR：表示对应的文件描述符发生错误；<br minmax_bound="true" style="font: 12px song,Verdana;">EPOLLHUP：表示对应的文件描述符被挂断；<br minmax_bound="true" style="font: 12px song,Verdana;">EPOLLET：表示对应的文件描述符有事件发生；<br minmax_bound="true" style="font: 12px song,Verdana;"></font><br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;">3、epoll_wait函数<br minmax_bound="true" style="font: 12px song,Verdana;">函数声明:<font minmax_bound="true" color="#0000ff">int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)</font><br minmax_bound="true" style="font: 12px song,Verdana;">该函数用于轮询I/O事件的发生；<br minmax_bound="true" style="font: 12px song,Verdana;">参数：<br minmax_bound="true" style="font: 12px song,Verdana;">epfd:由epoll_create 生成的epoll专用的文件描述符；<br minmax_bound="true" style="font: 12px song,Verdana;">epoll_event:用于回传代处理事件的数组；<br minmax_bound="true" style="font: 12px song,Verdana;">maxevents:每次能处理的事件数；<br minmax_bound="true" style="font: 12px song,Verdana;">timeout:等待I/O事件发生的超时值(单位我也不太清楚)；-1相当于阻塞，0相当于非阻塞。一般用-1即可<br minmax_bound="true" style="font: 12px song,Verdana;">返回发生事件数。<br minmax_bound="true" style="font: 12px song,Verdana;"><br minmax_bound="true" style="font: 12px song,Verdana;"></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span>#include&nbsp;&lt;stdio.h&gt;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>#include&nbsp;&lt;stdlib.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;errno.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;string.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;sys/types.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;netinet/in.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;sys/socket.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;sys/wait.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;unistd.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;arpa/inet.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;openssl/ssl.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;openssl/err.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;fcntl.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;sys/epoll.h&gt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>#include&nbsp;&lt;sys/time.h&gt;&nbsp;&nbsp;</span></li>
    <li class=""><span>#include&nbsp;&lt;sys/resource.h&gt;&nbsp;&nbsp;</span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span>#define&nbsp;MAXBUF&nbsp;</span><span class="number">1024</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>#define&nbsp;MAXEPOLLSIZE&nbsp;<span class="number">10000</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="comment">/*</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">setnonblocking&nbsp;-&nbsp;设置句柄为非阻塞方式</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="keyword">int</span><span>&nbsp;setnonblocking(</span><span class="keyword">int</span><span>&nbsp;sockfd)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(fcntl(sockfd,&nbsp;F_SETFL,&nbsp;fcntl(sockfd,&nbsp;F_GETFD,&nbsp;</span><span class="number">0</span><span>)|O_NONBLOCK)&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;-</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="number">0</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="comment">/*</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">handle_message&nbsp;-&nbsp;处理每个&nbsp;socket&nbsp;上的消息收发</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="keyword">int</span><span>&nbsp;handle_message(</span><span class="keyword">int</span><span>&nbsp;new_fd)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">char</span><span>&nbsp;buf[MAXBUF&nbsp;+&nbsp;</span><span class="number">1</span><span>];&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;len;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;<span class="comment">/*&nbsp;开始处理每个新连接上的数据收发&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;bzero(buf,&nbsp;MAXBUF&nbsp;+&nbsp;<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;<span class="comment">/*&nbsp;接收客户端的消息&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;=&nbsp;recv(new_fd,&nbsp;buf,&nbsp;MAXBUF,&nbsp;<span class="number">0</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(len&nbsp;&gt;&nbsp;</span><span class="number">0</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span class="string">"%d接收消息成功:'%s'，共%d个字节的数据\n"</span><span>,&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_fd,&nbsp;buf,&nbsp;len);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(len&nbsp;&lt;&nbsp;</span><span class="number">0</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span class="string">"消息接收失败！错误代码是%d，错误信息是'%s'\n"</span><span>,&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno,&nbsp;strerror(errno));&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(new_fd);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;-</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*&nbsp;处理每个新连接上的数据收发结束&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;len;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
    <li class=""><span><span class="comment">/************关于本文档********************************************</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*filename:&nbsp;epoll-server.c</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">*purpose:&nbsp;演示epoll处理海量socket连接的方法</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*wrote&nbsp;by:&nbsp;zhoulifa(&lt;a&nbsp;href="mailto:zhoulifa@163.com"&amp; gt;zhoulifa@163.com&lt;/a&gt;)&nbsp;周立发(&lt;a&nbsp;href="http: //zhoulifa.bokee.com"&gt;http://zhoulifa.bokee.com&lt;/a&gt;)</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">Linux爱好者&nbsp;Linux知识传播者&nbsp;SOHO族&nbsp;开发者&nbsp;最擅长C语言</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*date&nbsp;time:2007-01-31&nbsp;21:00</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">*Note:&nbsp;任何人可以任意复制代码并运用这些文档，当然包括你的商业用途</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*&nbsp;但请遵循GPL</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">*Thanks&nbsp;to:Google</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">*&nbsp;科技站在巨人的肩膀上进步更快！感谢有开源前辈的贡献！</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*********************************************************************/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="keyword">int</span><span>&nbsp;main(</span><span class="keyword">int</span><span>&nbsp;argc,&nbsp;</span><span class="keyword">char</span><span>&nbsp;**argv)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;listener,&nbsp;new_fd,&nbsp;kdpfd,&nbsp;nfds,&nbsp;n,&nbsp;ret,&nbsp;curfds;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;socklen_t&nbsp;len;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;sockaddr_in&nbsp;my_addr,&nbsp;their_addr;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;<span class="keyword">int</span><span>&nbsp;myport,&nbsp;lisnum;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;epoll_event&nbsp;ev;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;epoll_event&nbsp;events[MAXEPOLLSIZE];&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;rlimit&nbsp;rt;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;myport&nbsp;=&nbsp;<span class="number">5000</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;lisnum&nbsp;=&nbsp;<span class="number">2</span><span>;&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;<span class="comment">/*&nbsp;设置每个进程允许打开的最大文件数&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;rt.rlim_max&nbsp;=&nbsp;rt.rlim_cur&nbsp;=&nbsp;MAXEPOLLSIZE;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(setrlimit(RLIMIT_NOFILE,&nbsp;&amp;rt)&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span class="string">"setrlimit"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span class="string">"设置系统资源参数成功！\n"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*&nbsp;开启&nbsp;socket&nbsp;监听&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;((listener&nbsp;=&nbsp;socket(PF_INET,&nbsp;SOCK_STREAM,&nbsp;</span><span class="number">0</span><span>))&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span class="string">"socket"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span class="string">"socket&nbsp;创建成功！\n"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;setnonblocking(listener);&nbsp;&nbsp;</span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;bzero(&amp;my_addr,&nbsp;sizeof(my_addr));&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_family&nbsp;=&nbsp;PF_INET;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_port&nbsp;=&nbsp;htons(myport);&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;my_addr.sin_addr.s_addr&nbsp;=&nbsp;INADDR_ANY;&nbsp;&nbsp;</span></li>
</ol>
</div>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;" style="text-decoration: underline; color: #3300ff;">view plain</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;" style="text-decoration: underline; color: #3300ff;">copy to clipboard</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;" style="text-decoration: underline; color: #3300ff;">print</a><a href="http://blog.csdn.net/haoahua/archive/2008/01/11/2037704.aspx#" onclick="dp.sh.Toolbar.Command('About',this);return false;" style="text-decoration: underline; color: #3300ff;">?</a></div>
<div class="tools">
<div class="dp-highlighter">
<ol class="dp-j" start="1">
    <li class="alt"><span><span>&nbsp;&nbsp; &nbsp;</span><span class="keyword">if</span><span>&nbsp;(bind(listener,&nbsp;(struct&nbsp;sockaddr&nbsp;*)&nbsp;&amp;my_addr,&nbsp;sizeof(struct&nbsp;sockaddr))&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span class="string">"bind"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span class="string">"IP&nbsp;地址和端口绑定成功\n"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(listen(listener,&nbsp;lisnum)&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span class="string">"listen"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span class="string">"开启服务成功！\n"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; }&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;<span class="comment">/*&nbsp;创建&nbsp;epoll&nbsp;句柄，把监听&nbsp;socket&nbsp;加入到&nbsp;epoll&nbsp;集合里&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;kdpfd&nbsp;=&nbsp;epoll_create(MAXEPOLLSIZE);&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;=&nbsp;sizeof(struct&nbsp;sockaddr_in);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ev.events&nbsp;=&nbsp;EPOLLIN&nbsp;|&nbsp;EPOLLET;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;ev.data.fd&nbsp;=&nbsp;listener;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_ADD,&nbsp;listener,&nbsp;&amp;ev)&nbsp;&lt;&nbsp;</span><span class="number">0</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; {&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,&nbsp;<span class="string">"epoll&nbsp;set&nbsp;insertion&nbsp;error:&nbsp;fd=%d\n"</span><span>,&nbsp;listener);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;-</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;printf(<span class="string">"监听&nbsp;socket&nbsp;加入&nbsp;epoll&nbsp;成功！\n"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;curfds&nbsp;=&nbsp;<span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">while</span><span>&nbsp;(</span><span class="number">1</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*&nbsp;等待有事件发生&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nfds&nbsp;=&nbsp;epoll_wait(kdpfd,&nbsp;events,&nbsp;curfds,&nbsp;-<span class="number">1</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(nfds&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span class="string">"epoll_wait"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*&nbsp;处理所有事件&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span><span>&nbsp;(n&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;n&nbsp;&lt;&nbsp;nfds;&nbsp;++n)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; {&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(events[n].data.fd&nbsp;==&nbsp;listener)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_fd&nbsp;=&nbsp;accept(listener,&nbsp;(struct&nbsp;sockaddr&nbsp;*)&nbsp;&amp;their_addr,&amp;len);&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(new_fd&nbsp;&lt;&nbsp;</span><span class="number">0</span><span>)&nbsp;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(<span class="string">"accept"</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="keyword">continue</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}&nbsp;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="Apple-tab-span" style="white-space: pre;">	</span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;printf(<span class="string">"有连接来自于：&nbsp;%d:%d，&nbsp;分配的&nbsp;socket&nbsp;为:%d\n"</span><span>,&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;inet_ntoa(their_addr.sin_addr),&nbsp;ntohs(their_addr.sin_port),&nbsp;new_fd);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setnonblocking(new_fd);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ev.events&nbsp;=&nbsp;EPOLLIN&nbsp;|&nbsp;EPOLLET;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ev.data.fd&nbsp;=&nbsp;new_fd;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="keyword">if</span><span>&nbsp;(epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_ADD,&nbsp;new_fd,&nbsp;&amp;ev)&nbsp;&lt;&nbsp;</span><span class="number">0</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr,&nbsp;<span class="string">"把&nbsp;socket&nbsp;'%d'&nbsp;加入&nbsp;epoll&nbsp;失败！%s\n"</span><span>,&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_fd,&nbsp;strerror(errno));&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="keyword">return</span><span>&nbsp;-</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;curfds++;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ret&nbsp;=&nbsp;handle_message(events[n].data.fd);&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(ret&nbsp;&lt;&nbsp;</span><span class="number">1</span><span>&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;</span><span class="number">11</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;epoll_ctl(kdpfd,&nbsp;EPOLL_CTL_DEL,&nbsp;events[n].data.fd,&amp;ev);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curfds--;&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;close(listener);&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="number">0</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<div>
<p style="font: 12px song,verdana; text-align: left;" align="left">&nbsp;<font color="#ff0000">epoll_wait 运行的原理是 等侍注册在epfd上的socket fd的事件的发生，如果发生则将发生的sokct fd和事件类型放入到events数组中。 并且将注册在epfd上的socket fd的事件类型给清空，所以如果下一个循环你还要关注这个socket fd的话，则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&amp;ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空，只是事件类型清空。这一步非常重要。&nbsp;</font></p>
<p style="font: 12px song,verdana; text-align: left;" align="left"><font color="#ff0000"><br></font></p>
<p style="font: 12px song,verdana; text-align: left;" align="left"><font color="#ff0000">转自：</font></p>
<p style="font: 12px song,verdana; text-align: left;" align="left"><font color="#ff0000"><a href="http://blog.chinaunix.net/u/17928/showart.php?id=2098011">http://blog.chinaunix.net/u/17928/showart.php?id=2098011</a></font></p>
</div>
</div>
</div>
</div>
</span> <img src ="http://www.cppblog.com/beautykingdom/aggbug/105365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-01-10 22:46 <a href="http://www.cppblog.com/beautykingdom/archive/2010/01/10/105365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows Socket五种I/O模型</title><link>http://www.cppblog.com/beautykingdom/archive/2010/01/09/105215.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Fri, 08 Jan 2010 16:15:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/01/09/105215.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/105215.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/01/09/105215.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/105215.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/105215.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Winsock&nbsp;的I/O操作：1、&nbsp;两种I/O模式&nbsp;阻塞模式：执行I/O操作完成前会一直进行等待，不会将控制权交给程序。套接字&nbsp;默认为阻塞模式。可以通过多线程技术进行处理。&nbsp;非阻塞模式：执行I/O操作时，Winsock函数会返回并交出控制权。这种模式使用&nbsp;起来比较复杂，因为函数在没有运行完成就进行返回，会不断地返回&nbsp;WSAEWO...&nbsp;&nbsp;<a href='http://www.cppblog.com/beautykingdom/archive/2010/01/09/105215.html'>阅读全文</a><img src ="http://www.cppblog.com/beautykingdom/aggbug/105215.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-01-09 00:15 <a href="http://www.cppblog.com/beautykingdom/archive/2010/01/09/105215.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下各类TCP网络服务器的实现源代码《转》</title><link>http://www.cppblog.com/beautykingdom/archive/2010/01/07/105122.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 07 Jan 2010 15:22:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/01/07/105122.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/105122.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/01/07/105122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/105122.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/105122.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Linux下各类TCP网络服务器的实现源代码大家都知道各类网络服务器程序的编写步骤，并且都知道网络服务器就两大类：循环服务和并发服务。这里附上源代码来个小结吧。首先，循环网络服务器编程实现的步骤是这样的：[IMG]http://zhoulifa.bokee.com/inc/directsocket.png[/IMG]&nbsp;这种服务器模型是典型循环服务，如果不加上多进程/线程技术，此种服务吞吐...&nbsp;&nbsp;<a href='http://www.cppblog.com/beautykingdom/archive/2010/01/07/105122.html'>阅读全文</a><img src ="http://www.cppblog.com/beautykingdom/aggbug/105122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2010-01-07 23:22 <a href="http://www.cppblog.com/beautykingdom/archive/2010/01/07/105122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>socket know-how</title><link>http://www.cppblog.com/beautykingdom/archive/2009/12/17/103435.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Thu, 17 Dec 2009 15:14:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/12/17/103435.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/103435.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/12/17/103435.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/103435.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/103435.html</trackback:ping><description><![CDATA[1.<a  href="http://www.cppblog.com/prayer/archive/2009/04/14/79900.html" id="viewpost1_TitleUrl">如何判断socket已经断开</a><br>&nbsp;&nbsp;&nbsp; 在server端会使用专门的线程处理一条socket连接。如果socket连接断开（异常，正常）后，如何才能感知到？server端这边是绝对被动的，sever端不能主动断开连接。也没有连接链路维持包之类的。client端发送数据的时间也是不定的。在
socket连接断开后， server要能够感知到并释放资源。<br>&nbsp;&nbsp;&nbsp; 当使用 select()函数测试一个socket是否可读时，如果select()函数返回值为1，且使用recv()函数读取的数据长度为0 时，就说明该socket已经断开。<br>&nbsp;&nbsp;&nbsp; 为了更好的判定socket是否断开，判断当recv()返回值小于等于0时，socket连接断开。但是还需要判断 errno是否等于 EINTR
。如果errno == EINTR
则说明recv函数是由于程序接收到信号后返回的，socket连接还是正常的，不应close掉socket连接。<br><br>PS：对于堵塞socket的recv函数会在以下三种情况下返回：<br>（1）recv到数据时，会返回。<br>（2）在整个程序接收到信号时，返回-1。<strong>errno = EINTR</strong>。//在程序的起始阶段，屏蔽掉信号的除外。部分信号还是屏蔽不掉的。<br>（3）socket出现问题时，返回-1.具体错误码看 man recv()<br>（4）一定要看 man 说明，很详细，很有帮助。<br><img src ="http://www.cppblog.com/beautykingdom/aggbug/103435.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-12-17 23:14 <a href="http://www.cppblog.com/beautykingdom/archive/2009/12/17/103435.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux 下 http 协议实现分析</title><link>http://www.cppblog.com/beautykingdom/archive/2009/12/07/102759.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 07 Dec 2009 15:12:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/12/07/102759.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/102759.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/12/07/102759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/102759.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/102759.html</trackback:ping><description><![CDATA[<p>程序是从<a href="http://zhoulifa.bokee.com/4640913.html">http://zhoulifa.bokee.com/4640913.html</a> 下的，做了些裁剪，使程序更加的清晰<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;sys/types.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;unistd.h&gt;<br>#include &lt;netinet/in.h&gt;<br>#include &lt;limits.h&gt;<br>#include &lt;netdb.h&gt;<br>#include &lt;arpa/inet.h&gt;<br>#include &lt;ctype.h&gt;</p>
<p>int main(int argc, char *argv[])<br>{<br>int sockfd;<br>char buffer[1024];<br>struct sockaddr_in server_addr;<br>struct hostent *host;<br>int portnumber,nbytes;<br>char host_addr[256];<br>char host_file[1024];<br>char local_file[256];<br>FILE * fp;<br>char request[1024];<br>int send, totalsend;<br>int i;<br>char * pt;</p>
<p>if(argc!=2)<br>{<br>&nbsp;&nbsp;&nbsp; fprintf(stderr,"Usage:%s web-address\a\n",argv[0]);<br>&nbsp;&nbsp;&nbsp; exit(1);<br>}<br>portnumber=80;<br>strcpy(host_addr,argv[1]);<br>if((host=gethostbyname(argv[1]))==NULL)/*取得主机IP地址*/<br>{<br>&nbsp;&nbsp;&nbsp; fprintf(stderr,"Gethostname error, %s\n", strerror(errno));<br>&nbsp;&nbsp;&nbsp; exit(1);<br>}<br>if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/<br>{<br>&nbsp;&nbsp;&nbsp; fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));<br>&nbsp;&nbsp;&nbsp; exit(1);<br>}<br>/* 客户程序填充服务端的资料 */<br>bzero(&amp;server_addr,sizeof(server_addr));<br>server_addr.sin_family=AF_INET;<br>server_addr.sin_port=htons(portnumber);<br>server_addr.sin_addr=*((struct in_addr *)host-&gt;h_addr);</p>
<p>/* 客户程序发起连接请求 */<br>if(connect(sockfd,(struct sockaddr *)(&amp;server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/<br>{<br>&nbsp;&nbsp;&nbsp; fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));<br>&nbsp;&nbsp;&nbsp; exit(1);<br>}</p>
<p>sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n\<br>User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n\<br>Host: %s:%d\r\nConnection: Close\r\n\r\n", host_file, host_addr, portnumber);<br>printf("%s", request);/*准备request，将要发送给主机*/</p>
<p>/*取得真实的文件名*/<br>strcpy(local_file, "index.html");<br>/*发送http请求request*/<br>send = 0;totalsend = 0;<br>nbytes=strlen(request);<br>while(totalsend &lt; nbytes) {<br>&nbsp;&nbsp;&nbsp; send = write(sockfd, request + totalsend, nbytes - totalsend);<br>&nbsp;&nbsp;&nbsp; if(send==-1) {printf("send error!%s\n", strerror(errno));exit(0);}<br>&nbsp;&nbsp;&nbsp; totalsend+=send;<br>&nbsp;&nbsp;&nbsp; printf("%d bytes send OK!\n", totalsend);<br>}</p>
<p>fp = fopen(local_file, "a");<br>if(!fp) {<br>&nbsp;&nbsp;&nbsp; printf("create file error! %s\n", strerror(errno));<br>&nbsp;&nbsp;&nbsp; return 0;<br>}<br>printf("\nThe following is the response header:\n");<br>i=0;<br>/* 连接成功了，接收http响应，response */<br>while((nbytes=read(sockfd,buffer,1))==1)<br>{<br>&nbsp;&nbsp;&nbsp; if(i &lt; 4) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(buffer[0] == '\r' || buffer[0] == '\n') i++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else i = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%c", buffer[0]);/*把http头信息打印在屏幕上*/<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fwrite(buffer, 1, 1, fp);/*将http主体信息写入文件*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(i%1024 == 0) fflush(fp);/*每1K时存盘一次*/<br>&nbsp;&nbsp;&nbsp; }<br>}<br>fclose(fp);<br>/* 结束通讯 */<br>close(sockfd);<br>exit(0);<br>}</p>
<p><br>1&nbsp;&nbsp;&nbsp; struct hostent *gethostbyname(const char *name);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 这个函数完成进行域名和IP地址的转换，返回的为：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct hostent { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *h_name;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 主机的官方域名 */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char **h_aliases;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 一个以NULL结尾的主机别名数组 */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int h_addrtype;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 返回的地址类型，在Internet环境下为AF-INET */ <br>&nbsp;&nbsp;&nbsp;&nbsp; int h_length;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*地址的字节长度 */ <br>&nbsp;&nbsp;&nbsp;&nbsp; char **h_addr_list;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 一个以0结尾的数组，包含该主机的所有地址*/&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; #define h_addr h_addr_list[0]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*在h-addr-list中的第一个地址*/</p>
<p>&nbsp;&nbsp;&nbsp; 我们一般用的也就第一个地址</p>
<p>2&nbsp;&nbsp; 接下去就是SOCKET 的建立，绑定，连接，我们想要连接，上面得到的ip地址是不行的，我们要使用服务器的地址，具体数据结构如下：</p>
<p>&nbsp;&nbsp; struct sockaddr_in { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short int sin_family;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 地址族 */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short int sin_port;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 端口号 */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct in_addr sin_addr;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* IP地址 */ 上面得到的地址<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char sin_zero[8];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 填充0 以保持与struct sockaddr同样大小 */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp; 这里也提下 struct sockaddr 这个是描述sock 地址信息的，和上面的结构大小一样，可以相互转换</p>
<p>&nbsp;&nbsp; struct sockaddr { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short sa_family; /* 地址族， AF_xxx */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char sa_data[14]; /* 14 字节的协议地址 */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>3 我们和服务器连上之后，就可以向服务器发送请求了 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; write(sockfd, char *, size);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内容就是：GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n\User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n\<br>Host: %s:%d\r\nConnection: Close\r\n\r\n</p>
<p>&nbsp;&nbsp; 具体是由其协议定的，我也还不是很清楚</p>
<p>4 服务器响应，就会发来信息头+ 实际页面的信息， 这个中间是有4个（"\r"或"\n"）进行分开的。<br><br>转自：<br><a href="http://blog.chinaunix.net/u2/76292/showart_1335922.html">http://blog.chinaunix.net/u2/76292/showart_1335922.html</a></p>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/102759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-12-07 23:12 <a href="http://www.cppblog.com/beautykingdom/archive/2009/12/07/102759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows完成端口与Linux epoll技术简介</title><link>http://www.cppblog.com/beautykingdom/archive/2009/11/24/101814.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 24 Nov 2009 07:28:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/11/24/101814.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/101814.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/11/24/101814.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/101814.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/101814.html</trackback:ping><description><![CDATA[<span style="border-collapse: collapse; font-family: song,Verdana; font-size: 12px;">
<p style="font: 12px song,Verdana;"><font size="3"><strong>WINDOWS完成端口编程<br style="font: 12px song,Verdana;"></strong>1、基本概念<br style="font: 12px song,Verdana;">2、WINDOWS完成端口的特点<br style="font: 12px song,Verdana;">3、完成端口（Completion Ports ）相关数据结构和创建<br style="font: 12px song,Verdana;">4、完成端口线程的工作原理<br style="font: 12px song,Verdana;">5、Windows完成端口的实例代码<br style="font: 12px song,Verdana;"><strong>Linux的EPoll模型<br style="font: 12px song,Verdana;"></strong>1、为什么select落后<br style="font: 12px song,Verdana;">2、内核中提高I/O性能的新方法epoll<br style="font: 12px song,Verdana;">3、epoll的优点<br style="font: 12px song,Verdana;">4、epoll的工作模式&nbsp;<br style="font: 12px song,Verdana;">5、epoll的使用方法<br style="font: 12px song,Verdana;">6、Linux下EPOll编程实例<br style="font: 12px song,Verdana;"><strong>总结</strong></font></p>
<p style="font: 12px song,Verdana;"><font size="3"><strong>WINDOWS完成端口编程<br style="font: 12px song,Verdana;"></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 摘要：开发网络程序从来都不是一件容易的事情，尽管只需要遵守很少的一些规则;创建socket,发起连接，接受连接，发送和接受数据。真正的困难在于： 让你的程序可以适应从单单一个连接到几千个连接乃至于上万个连接。利用Windows平台完成端口进行重叠I/O的技术和Linux在2.6版本的内核中 引入的EPOll技术，可以很方便在在在Windows和Linux平台上开发出支持大量连接的网络服务程序。本文介绍在Windows和Linux平台 上使用的完成端口和EPoll模型开发的基本原理，同时给出实际的例子。本文主要关注C/S结构的服务器端程序，因为一般来说，开发一个大容量，具可扩展 性的winsock程序一般就是指服务程序。<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">1、基本概念<br style="font: 12px song,Verdana;"></strong>&nbsp;&nbsp;&nbsp; 设备---windows操作系统上允许通信的任何东西，比如文件、目录、串行口、并行口、邮件槽、命名管道、无名管道、套接字、控制台、逻辑磁盘、物理 磁盘等。绝大多数与设备打交道的函数都是CreateFile/ReadFile/WriteFile等。所以我们不能看到**File函数就只想到文件 设备。与设备通信有两种方式，同步方式和异步方式。同步方式下，当调用ReadFile函数时，函数会等待系统执行完所要求的工作，然后才返回；异步方式 下，ReadFile这类函数会直接返回，系统自己去完成对设备的操作，然后以某种方式通知完成操作。<br style="font: 12px song,Verdana;">重叠I/O----顾名思义，当你调用了某个函数（比如ReadFile）就立刻返回做自己的其他动作的时候，同时系统也在对I/0设备进行你要求的操 作，在这段时间内你的程序和系统的内部动作是重叠的，因此有更好的性能。所以，重叠I/O是用于异步方式下使用I/O设备的。 重叠I/O需要使用的一个非常重要的数据结构OVERLAPPED。<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">2、WINDOWS完成端口的特点<br style="font: 12px song,Verdana;"></strong>&nbsp;&nbsp; Win32重叠I/O(Overlapped I/O)机制允许发起一个操作，然后在操作完成之后接受到信息。对于那种需要很长时间才能完成的操作来说，重叠IO机制尤其有用，因为发起重叠操作的线程 在重叠请求发出后就可以自由的做别的事情了。在WinNT和Win2000上，提供的真正的可扩展的I/O模型就是使用完成端口（Completion Port）的重叠I/O.完成端口---是一种WINDOWS内核对象。完成端口用于异步方式的重叠I/0情况下，当然重叠I/O不一定非使用完成端口不 可，还有设备内核对象、事件对象、告警I/0等。但是完成端口内部提供了线程池的管理，可以避免反复创建线程的开销，同时可以根据CPU的个数灵活的决定 线程个数，而且可以让减少线程调度的次数从而提高性能其实类似于WSAAsyncSelect和select函数的机制更容易兼容Unix，但是难以实现 我们想要的&#8220;扩展性&#8221;。而且windows的完成端口机制在操作系统内部已经作了优化，提供了更高的效率。所以，我们选择完成端口开始我们的服务器程序的 开发。<br style="font: 12px song,Verdana;">1、发起操作不一定完成，系统会在完成的时候通知你，通过用户在完成端口上的等待，处理操作的结果。所以要有检查完成端口，取操作结果的线程。在完成端口 上守候的线程系统有优化，除非在执行的线程阻塞，不会有新的线程被激活，以此来减少线程切换造成的性能代价。所以如果程序中没有太多的阻塞操作，没有必要 启动太多的线程，CPU数量的两倍，一般这样来启动线程。<br style="font: 12px song,Verdana;">2、操作与相关数据的绑定方式：在提交数据的时候用户对数据打相应的标记，记录操作的类型，在用户处理操作结果的时候，通过检查自己打的标记和系统的操作结果进行相应的处理。&nbsp;<br style="font: 12px song,Verdana;">3、操作返回的方式:一般操作完成后要通知程序进行后续处理。但写操作可以不通知用户，此时如果用户写操作不能马上完成，写操作的相关数据会被暂存到到非 交换缓冲区中，在操作完成的时候，系统会自动释放缓冲区。此时发起完写操作，使用的内存就可以释放了。此时如果占用非交换缓冲太多会使系统停止响应。<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">3、完成端口（Completion Ports ）相关数据结构和创建<br style="font: 12px song,Verdana;"></strong>&nbsp;&nbsp;&nbsp; 其实可以把完成端口看成系统维护的一个队列，操作系统把重叠IO操作完成的事件通知放到该队列里，由于是暴露 &#8220;操作完成&#8221;的事件通知，所以命名为&#8220;完成端口&#8221;（COmpletion Ports）。一个socket被创建后，可以在任何时刻和一个完成端口联系起来。<br style="font: 12px song,Verdana;">完成端口相关最重要的是OVERLAPPED数据结构<br style="font: 12px song,Verdana;">typedef struct _OVERLAPPED {&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; ULONG_PTR Internal;//被系统内部赋值，用来表示系统状态&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; ULONG_PTR InternalHigh;// 被系统内部赋值，传输的字节数&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; union {&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct {&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD Offset;//和OffsetHigh合成一个64位的整数，用来表示从文件头部的多少字节开始&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD OffsetHigh;//操作，如果不是对文件I/O来操作，则必须设定为0&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PVOID Pointer;&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; };&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; HANDLE hEvent;//如果不使用，就务必设为0,否则请赋一个有效的Event句柄&nbsp;<br style="font: 12px song,Verdana;">} OVERLAPPED, *LPOVERLAPPED;&nbsp;<br style="font: 12px song,Verdana;"><br style="font: 12px song,Verdana;">下面是异步方式使用ReadFile的一个例子&nbsp;<br style="font: 12px song,Verdana;">OVERLAPPED Overlapped;&nbsp;<br style="font: 12px song,Verdana;">Overlapped.Offset=345;&nbsp;<br style="font: 12px song,Verdana;">Overlapped.OffsetHigh=0;&nbsp;<br style="font: 12px song,Verdana;">Overlapped.hEvent=0;&nbsp;<br style="font: 12px song,Verdana;">//假定其他参数都已经被初始化&nbsp;<br style="font: 12px song,Verdana;">ReadFile(hFile,buffer,sizeof(buffer),&amp;dwNumBytesRead,&amp;Overlapped);&nbsp;<br style="font: 12px song,Verdana;">这样就完成了异步方式读文件的操作，然后ReadFile函数返回，由操作系统做自己的事情，下面介绍几个与OVERLAPPED结构相关的函数&nbsp;<br style="font: 12px song,Verdana;">等待重叠I/0操作完成的函数&nbsp;<br style="font: 12px song,Verdana;">BOOL GetOverlappedResult (<br style="font: 12px song,Verdana;">HANDLE hFile,<br style="font: 12px song,Verdana;">LPOVERLAPPED lpOverlapped,//接受返回的重叠I/0结构<br style="font: 12px song,Verdana;">LPDWORD lpcbTransfer,//成功传输了多少字节数<br style="font: 12px song,Verdana;">BOOL fWait //TRUE只有当操作完成才返回，FALSE直接返回，如果操作没有完成，通过调//用GetLastError ( )函数会返回ERROR_IO_INCOMPLETE&nbsp;<br style="font: 12px song,Verdana;">);<br style="font: 12px song,Verdana;">宏HasOverlappedIoCompleted可以帮助我们测试重叠I/0操作是否完成，该宏对OVERLAPPED结构的Internal成员进行了测试，查看是否等于STATUS_PENDING值。</font></p>
<p style="font: 12px song,Verdana;"><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一般来说，一个应用程序可以创建多个工作线程来处理完成端口上的通知事件。工作线程的数量依赖于程序的具体需要。但是在理想的情况下，应该对应一个CPU 创建一个线程。因为在完成端口理想模型中，每个线程都可以从系统获得一个&#8220;原子&#8221;性的时间片，轮番运行并检查完成端口，线程的切换是额外的开销。在实际开 发的时候，还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作，系统则将其挂起，让别的线程获得运行时间。因此，如果有这样的情况， 可以多创建几个线程来尽量利用时间。<br style="font: 12px song,Verdana;">应用完成端口：<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; 创建完成端口：完成端口是一个内核对象，使用时他总是要和至少一个有效的设备句柄进行关联，完成端口是一个复杂的内核对象，创建它的函数是：<br style="font: 12px song,Verdana;">HANDLE CreateIoCompletionPort(&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN HANDLE FileHandle,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN HANDLE ExistingCompletionPort,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN ULONG_PTR CompletionKey,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN DWORD NumberOfConcurrentThreads&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; );&nbsp;<br style="font: 12px song,Verdana;"><br style="font: 12px song,Verdana;">通常创建工作分两步：<br style="font: 12px song,Verdana;">第一步，创建一个新的完成端口内核对象，可以使用下面的函数：<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE CreateNewCompletionPort(DWORD dwNumberOfThreads)&nbsp;<br style="font: 12px song,Verdana;">{&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,dwNumberOfThreads);<br style="font: 12px song,Verdana;">};<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br style="font: 12px song,Verdana;">第二步，将刚创建的完成端口和一个有效的设备句柄关联起来，可以使用下面的函数：<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool AssicoateDeviceWithCompletionPort(HANDLE hCompPort,HANDLE hDevice,DWORD dwCompKey)&nbsp;<br style="font: 12px song,Verdana;">{&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; HANDLE h=CreateIoCompletionPort(hDevice,hCompPort,dwCompKey,0);&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return h==hCompPort;&nbsp;<br style="font: 12px song,Verdana;">};&nbsp;<br style="font: 12px song,Verdana;">说明&nbsp;<br style="font: 12px song,Verdana;">1） CreateIoCompletionPort函数也可以一次性的既创建完成端口对象，又关联到一个有效的设备句柄&nbsp;<br style="font: 12px song,Verdana;">2） CompletionKey是一个可以自己定义的参数，我们可以把一个结构的地址赋给它，然后在合适的时候取出来使用，最好要保证结构里面的内存不是分配在栈上，除非你有十分的把握内存会保留到你要使用的那一刻。<br style="font: 12px song,Verdana;">3） NumberOfConcurrentThreads通常用来指定要允许同时运行的的线程的最大个数。通常我们指定为0，这样系统会根据CPU的个数来自 动确定。创建和关联的动作完成后，系统会将完成端口关联的设备句柄、完成键作为一条纪录加入到这个完成端口的设备列表中。如果你有多个完成端口，就会有多 个对应的设备列表。如果设备句柄被关闭，则表中自动删除该纪录。<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">4、完成端口线程的工作原理</strong><br style="font: 12px song,Verdana;">完成端口可以帮助我们管理线程池，但是线程池中的线程需要我们使用_beginthreadex来创建，凭什么通知完成端口管理我们的新线程呢？答案在函数GetQueuedCompletionStatus。该函数原型：&nbsp;<br style="font: 12px song,Verdana;">BOOL GetQueuedCompletionStatus(&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN HANDLE CompletionPort,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; OUT LPDWORD lpNumberOfBytesTransferred,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; OUT PULONG_PTR lpCompletionKey,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; OUT LPOVERLAPPED *lpOverlapped,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; IN DWORD dwMilliseconds&nbsp;<br style="font: 12px song,Verdana;">);&nbsp;<br style="font: 12px song,Verdana;">这个函数试图从指定的完成端口的I/0完成队列中抽取纪录。只有当重叠I/O动作完成的时候，完成队列中才有纪录。凡是调用这个函数的线程将被放入到完成 端口的等待线程队列中，因此完成端口就可以在自己的线程池中帮助我们维护这个线程。完成端口的I/0完成队列中存放了当重叠I/0完成的结果---- 一条纪录，该纪录拥有四个字段，前三项就对应GetQueuedCompletionStatus函数的2、3、4参数，最后一个字段是错误信息 dwError。我们也可以通过调用PostQueudCompletionStatus模拟完成了一个重叠I/0操作。&nbsp;<br style="font: 12px song,Verdana;">当I/0完成队列中出现了纪录，完成端口将会检查等待线程队列，该队列中的线程都是通过调用GetQueuedCompletionStatus函数使自 己加入队列的。等待线程队列很简单，只是保存了这些线程的ID。完成端口会按照后进先出的原则将一个线程队列的ID放入到释放线程列表中，同时该线程将从 等待GetQueuedCompletionStatus函数返回的睡眠状态中变为可调度状态等待CPU的调度。所以我们的线程要想成为完成端口管理的线 程，就必须要调用GetQueuedCompletionStatus函数。出于性能的优化，实际上完成端口还维护了一个暂停线程列表，具体细节可以参考 《Windows高级编程指南》，我们现在知道的知识，已经足够了。 完成端口线程间数据传递线程间传递数据最常用的办法是在_beginthreadex函数中将参数传递给线程函数，或者使用全局变量。但是完成端口还有自 己的传递数据的方法，答案就在于CompletionKey和OVERLAPPED参数。<br style="font: 12px song,Verdana;">CompletionKey被保存在完成端口的设备表中，是和设备句柄一一对应的，我们可以将与设备句柄相关的数据保存到CompletionKey中， 或者将CompletionKey表示为结构指针，这样就可以传递更加丰富的内容。这些内容只能在一开始关联完成端口和设备句柄的时候做，因此不能在以后 动态改变。<br style="font: 12px song,Verdana;">OVERLAPPED参数是在每次调用ReadFile这样的支持重叠I/0的函数时传递给完成端口的。我们可以看到，如果我们不是对文件设备做操作，该 结构的成员变量就对我们几乎毫无作用。我们需要附加信息，可以创建自己的结构，然后将OVERLAPPED结构变量作为我们结构变量的第一个成员，然后传 递第一个成员变量的地址给ReadFile函数。因为类型匹配，当然可以通过编译。当GetQueuedCompletionStatus函数返回时，我 们可以获取到第一个成员变量的地址，然后一个简单的强制转换，我们就可以把它当作完整的自定义结构的指针使用，这样就可以传递很多附加的数据了。太好了！ 只有一点要注意，如果跨线程传递，请注意将数据分配到堆上，并且接收端应该将数据用完后释放。我们通常需要将ReadFile这样的异步函数的所需要的缓 冲区放到我们自定义的结构中，这样当GetQueuedCompletionStatus被返回时，我们的自定义结构的缓冲区变量中就存放了I/0操作的 数据。CompletionKey和OVERLAPPED参数，都可以通过GetQueuedCompletionStatus函数获得。<br style="font: 12px song,Verdana;">线程的安全退出<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很多线程为了不止一次的执行异步数据处理，需要使用如下语句<br style="font: 12px song,Verdana;">while (true)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ......<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetQueuedCompletionStatus(...);&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ......<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">那么如何退出呢，答案就在于上面曾提到的PostQueudCompletionStatus函数，我们可以用它发送一个自定义的包含了OVERLAPPED成员变量的结构地址，里面包含一个状态变量，当状态变量为退出标志时，线程就执行清除动作然后退出。<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">5、Windows完成端口的实例代码：<br style="font: 12px song,Verdana;"></strong>DWORD WINAPI WorkerThread(LPVOID lpParam)<br style="font: 12px song,Verdana;">{&nbsp;<br style="font: 12px song,Verdana;">ULONG_PTR *PerHandleKey;<br style="font: 12px song,Verdana;">OVERLAPPED *Overlap;<br style="font: 12px song,Verdana;">OVERLAPPEDPLUS *OverlapPlus,<br style="font: 12px song,Verdana;">*newolp;<br style="font: 12px song,Verdana;">DWORD dwBytesXfered;<br style="font: 12px song,Verdana;">while (1)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">ret = GetQueuedCompletionStatus(<br style="font: 12px song,Verdana;">hIocp,<br style="font: 12px song,Verdana;">&amp;dwBytesXfered,<br style="font: 12px song,Verdana;">(PULONG_PTR)&amp;PerHandleKey,<br style="font: 12px song,Verdana;">&amp;Overlap,<br style="font: 12px song,Verdana;">INFINITE);<br style="font: 12px song,Verdana;">if (ret == 0)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">// Operation failed<br style="font: 12px song,Verdana;">continue;<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol);<br style="font: 12px song,Verdana;">switch (OverlapPlus-&gt;OpCode)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">case OP_ACCEPT:<br style="font: 12px song,Verdana;">// Client socket is contained in OverlapPlus.sclient<br style="font: 12px song,Verdana;">// Add client to completion port<br style="font: 12px song,Verdana;">CreateIoCompletionPort(<br style="font: 12px song,Verdana;">(HANDLE)OverlapPlus-&gt;sclient,<br style="font: 12px song,Verdana;">hIocp,<br style="font: 12px song,Verdana;">(ULONG_PTR)0,<br style="font: 12px song,Verdana;">0);<br style="font: 12px song,Verdana;">// Need a new OVERLAPPEDPLUS structure<br style="font: 12px song,Verdana;">// for the newly accepted socket. Perhaps<br style="font: 12px song,Verdana;">// keep a look aside list of free structures.<br style="font: 12px song,Verdana;">newolp = AllocateOverlappedPlus();<br style="font: 12px song,Verdana;">if (!newolp)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">// Error<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">newolp-&gt;s = OverlapPlus-&gt;sclient;<br style="font: 12px song,Verdana;">newolp-&gt;OpCode = OP_READ;<br style="font: 12px song,Verdana;">// This function divpares the data to be sent<br style="font: 12px song,Verdana;">PrepareSendBuffer(&amp;newolp-&gt;wbuf);<br style="font: 12px song,Verdana;">ret = WSASend(<br style="font: 12px song,Verdana;">newolp-&gt;s,<br style="font: 12px song,Verdana;">&amp;newolp-&gt;wbuf,<br style="font: 12px song,Verdana;">1,<br style="font: 12px song,Verdana;">&amp;newolp-&gt;dwBytes,<br style="font: 12px song,Verdana;">0,<br style="font: 12px song,Verdana;">&amp;newolp.ol,<br style="font: 12px song,Verdana;">NULL);<br style="font: 12px song,Verdana;">if (ret == SOCKET_ERROR)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">if (WSAGetLastError() != WSA_IO_PENDING)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">// Error<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">// Put structure in look aside list for later use<br style="font: 12px song,Verdana;">FreeOverlappedPlus(OverlapPlus);<br style="font: 12px song,Verdana;">// Signal accept thread to issue another AcceptEx<br style="font: 12px song,Verdana;">SetEvent(hAcceptThread);<br style="font: 12px song,Verdana;">break;<br style="font: 12px song,Verdana;">case OP_READ:<br style="font: 12px song,Verdana;">// Process the data read&nbsp;<br style="font: 12px song,Verdana;">// Repost the read if necessary, reusing the same<br style="font: 12px song,Verdana;">// receive buffer as before<br style="font: 12px song,Verdana;">memset(&amp;OverlapPlus-&gt;ol, 0, sizeof(OVERLAPPED));<br style="font: 12px song,Verdana;">ret = WSARecv(<br style="font: 12px song,Verdana;">OverlapPlus-&gt;s,<br style="font: 12px song,Verdana;">&amp;OverlapPlus-&gt;wbuf,<br style="font: 12px song,Verdana;">1,<br style="font: 12px song,Verdana;">&amp;OverlapPlus-&gt;dwBytes,<br style="font: 12px song,Verdana;">&amp;OverlapPlus-&gt;dwFlags,<br style="font: 12px song,Verdana;">&amp;OverlapPlus-&gt;ol,<br style="font: 12px song,Verdana;">NULL);<br style="font: 12px song,Verdana;">if (ret == SOCKET_ERROR)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">if (WSAGetLastError() != WSA_IO_PENDING)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">// Error<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">break;<br style="font: 12px song,Verdana;">case OP_WRITE:<br style="font: 12px song,Verdana;">// Process the data sent, etc.<br style="font: 12px song,Verdana;">break;<br style="font: 12px song,Verdana;">} // switch<br style="font: 12px song,Verdana;">} // while<br style="font: 12px song,Verdana;">} // WorkerThread<br style="font: 12px song,Verdana;">　</font></p>
<p style="font: 12px song,Verdana;"><font size="3">查看以上代码，注意如果Overlapped操作立刻失败（比如，返回SOCKET_ERROR或其他非WSA_IO_PENDING的错误），则 没有任何完成通知时间会被放到完成端口队列里。反之，则一定有相应的通知时间被放到完成端口队列。更完善的关于Winsock的完成端口机制，可以参考 MSDN的Microsoft PlatFormSDK，那里有完成端口的例子。访问<a style="color: #0044b6; text-decoration: underline;" href="http://msdn.microsoft.com/library/techart/msdn_servrapp.htm">http://msdn.microsoft.com/library/techart/msdn_servrapp.htm</a>可以获得更多信息。</font></p>
<p style="font: 12px song,Verdana;"><font size="3"><strong>Linux的EPoll模型<br style="font: 12px song,Verdana;"></strong>Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用，即比较多的用到select函数。<br style="font: 12px song,Verdana;"><br style="font: 12px song,Verdana;"><strong>1、为什么select落后<br style="font: 12px song,Verdana;"></strong>首先，在Linux内核中，select所用到的FD_SET是有限的，即内核中有个参数__FD_SETSIZE定义了每个FD_SET的句柄个数，在我用的2.6.15-25-386内核中，该值是1024，搜索内核源代码得到：<br style="font: 12px song,Verdana;">include/linux/posix_types.h:#define __FD_SETSIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1024<br style="font: 12px song,Verdana;">也就是说，如果想要同时检测1025个句柄的可读状态是不可能用select实现的。或者同时检测1025个句柄的可写状态也是不可能的。其次，内核中实 现select是用轮询方法，即每次检测都会遍历所有FD_SET中的句柄，显然，select函数执行时间与FD_SET中的句柄个数有一个比例关系， 即select要检测的句柄数越多就会越费时。当然，在前文中我并没有提及poll方法，事实上用select的朋友一定也试过poll，我个人觉得 select和poll大同小异，个人偏好于用select而已。</font></p>
<p style="font: 12px song,Verdana;"><font size="3"><strong>2、内核中提高I/O性能的新方法epoll</strong><br style="font: 12px song,Verdana;">epoll是什么？按照man手册的说法：是为处理大批量句柄而作了改进的poll。要使用epoll只需要这三个系统调用：epoll_create(2)， epoll_ctl(2)， epoll_wait(2)。<br style="font: 12px song,Verdana;">当然，这不是2.6内核才有的，它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44)</font></p>
<p style="font: 12px song,Verdana;"><font size="3">Linux2.6内核epoll介绍<br style="font: 12px song,Verdana;">先介绍2本书《The Linux Networking Architecture--Design and Implementation of Network Protocols in the Linux Kernel》，以2.4内核讲解Linux TCP/IP实现，相当不错.作为一个现实世界中的实现，很多时候你必须作很多权衡，这时候参考一个久经考验的系统更有实际意义。举个例子,linux内 核中sk_buff结构为了追求速度和安全，牺牲了部分内存，所以在发送TCP包的时候，无论应用层数据多大,sk_buff最小也有272的字节.其实 对于socket应用层程序来说，另外一本书《UNIX Network Programming Volume 1》意义更大一点.2003年的时候，这本书出了最新的第3版本，不过主要还是修订第2版本。其中第6章《I/O Multiplexing》是最重要的。Stevens给出了网络IO的基本模型。在这里最重要的莫过于select模型和Asynchronous I/O模型.从理论上说，AIO似乎是最高效的，你的IO操作可以立即返回，然后等待os告诉你IO操作完成。但是一直以来，如何实现就没有一个完美的方 案。最著名的windows完成端口实现的AIO,实际上也是内部用线程池实现的罢了，最后的结果是IO有个线程池，你应用也需要一个线程池...... 很多文档其实已经指出了这带来的线程context-switch带来的代价。在linux 平台上，关于网络AIO一直是改动最多的地方，2.4的年代就有很多AIO内核patch,最著名的应该算是SGI那个。但是一直到2.6内核发布，网络 模块的AIO一直没有进入稳定内核版本(大部分都是使用用户线程模拟方法，在使用了NPTL的linux上面其实和windows的完成端口基本上差不多 了)。2.6内核所支持的AIO特指磁盘的AIO---支持io_submit(),io_getevents()以及对Direct IO的支持(就是绕过VFS系统buffer直接写硬盘，对于流服务器在内存平稳性上有相当帮助)。<br style="font: 12px song,Verdana;">所以，剩下的select模型基本上就是我们在linux上面的唯一选择，其实，如果加上no-block socket的配置，可以完成一个"伪"AIO的实现，只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受的缺 点，所以改进一直是2.4-2.5开发版本内核的任务，包括/dev/poll，realtime signal等等。最终，Davide Libenzi开发的epoll进入2.6内核成为正式的解决方案<br style="font: 12px song,Verdana;"><strong><br style="font: 12px song,Verdana;">3、epoll的优点</strong><br style="font: 12px song,Verdana;">&lt;1&gt;支持一个进程打开大数目的socket描述符(FD)<br style="font: 12px song,Verdana;">select 最不能忍受的是一个进程所打开的FD是有一定限制的，由FD_SETSIZE设置，默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显 然太少了。这时候你一是可以选择修改这个宏然后重新编译内核，不过资料也同时指出这样会带来网络效率的下降，二是可以选择多进程的解决方案(传统的 Apache方案)，不过虽然linux上面创建进程的代价比较小，但仍旧是不可忽视的，加上进程间数据同步远比不上线程间同步的高效，所以也不是一种完 美的方案。不过 epoll则没有这个限制，它所支持的FD上限是最大可以打开文件的数目，这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右，具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。<br style="font: 12px song,Verdana;">&lt;2&gt;IO效率不随FD数目增加而线性下降<br style="font: 12px song,Verdana;">传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合，不过由于网络延时，任一时间只有部分的socket是"活跃"的， 但是select/poll每次调用都会线性扫描全部的集合，导致效率呈现线性下降。但是epoll不存在这个问题，它只会对"活跃"的socket进行 操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么，只有"活跃"的socket才会主动的去调用 callback函数，其他idle状态socket则不会，在这点上，epoll实现了一个"伪"AIO，因为这时候推动力在os内核。在一些 benchmark中，如果所有的socket基本上都是活跃的---比如一个高速LAN环境，epoll并不比select/poll有什么效率，相 反，如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。<br style="font: 12px song,Verdana;">&lt;3&gt;使用mmap加速内核与用户空间的消息传递。<br style="font: 12px song,Verdana;">这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间，如何避免不必要的内存拷贝就 很重要，在这点上，epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我一样从2.5内核就关注epoll的话，一定不会忘记手工 mmap这一步的。<br style="font: 12px song,Verdana;">&lt;4&gt;内核微调<br style="font: 12px song,Verdana;">这一点其实不算epoll的优点了，而是整个linux平台的优点。也许你可以怀疑linux平台，但是你无法回避linux平台赋予你微调内核的能力。 比如，内核TCP/IP协议栈使用内存池管理sk_buff结构，那么可以在运行时期动态调整这个内存pool(skb_head_pool)的大小 --- 通过echo XXXX&gt;/proc/sys/net/core/hot_list_length完成。再比如listen函数的第2个参数(TCP完成3次握手 的数据包队列长度)，也可以根据你平台内存大小动态调整。更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网 卡驱动架构。<br style="font: 12px song,Verdana;">4、epoll的工作模式<br style="font: 12px song,Verdana;">令人高兴的是，2.6内核的epoll比其2.5开发版本的/dev/epoll简洁了许多，所以，大部分情况下，强大的东西往往是简单的。唯一有点麻烦是epoll有2种工作方式:LT和ET。<br style="font: 12px song,Verdana;">LT(level triggered)是缺省的工作方式，并且同时支持block和no-block socket.在这种做法中，内核告诉你一个文件描述符是否就绪了，然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作，内核还是会继续通知你 的，所以，这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表．<br style="font: 12px song,Verdana;">ET (edge-triggered)是高速工作方式，只支持no-block socket。在这种模式下，当描述符从未就绪变为就绪时，内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪，并且不会再为那个文件描述 符发送更多的就绪通知，直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如，你在发送，接收或者接收请求，或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误）。但是请注意，如果一直不对这个fd作IO操作(从而导致它再次变成未就绪)，内核不会发送更多的通知(only once),不过在TCP协议中，ET模式的加速效用仍需要更多的benchmark确认。<br style="font: 12px song,Verdana;">epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用，具体用法请参考<a style="color: #0044b6; text-decoration: underline;" href="http://www.xmailserver.org/linux-patches/nio-improve.html">http://www.xmailserver.org/linux-patches/nio-improve.html</a>&nbsp;，在<a style="color: #0044b6; text-decoration: underline;" href="http://www.kegel.com/rn/">http://www.kegel.com/rn/</a>也有一个完整的例子，大家一看就知道如何使用了<br style="font: 12px song,Verdana;">Leader/follower模式线程pool实现，以及和epoll的配合。<br style="font: 12px song,Verdana;"><br style="font: 12px song,Verdana;"><strong>5、 epoll的使用方法</strong><br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp; 首先通过create_epoll(int maxfds)来创建一个epoll的句柄，其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄，之后的所有操作 将通过这个句柄来进行操作。在用完之后，记得用close()来关闭这个创建出来的epoll句柄。 之后在你的网络主循环里面，每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口，看哪一个可以读，哪一个可以写了。基本的语法为：&nbsp;<br style="font: 12px song,Verdana;">nfds = epoll_wait(kdpfd, events, maxevents, -1);&nbsp;<br style="font: 12px song,Verdana;">其中kdpfd为用epoll_create创建之后的句柄，events是一个epoll_event*的指针，当epoll_wait这个函数操作成 功之后，epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时，为0的时候表示马上返回，为-1的时候表示一直等下去，直到有事件范围，为任意正整数的时候表示等这么长的时间，如果一直没 有事件，则范围。一般如果网络主循环是单独的线程的话，可以用-1来等，这样可以保证一些效率，如果是和主逻辑在同一个线程的话，则可以用0来保证主循环 的效率。</font></p>
<p style="font: 12px song,Verdana;"><font size="3">epoll_wait范围之后应该是一个循环，遍利所有的事件：&nbsp;<br style="font: 12px song,Verdana;">for(n = 0; n &lt; nfds; ++n) {&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(events[n].data.fd == listener) { //如果是主socket的事件的话，则表示有新连接进入了，进行新连接的处理。&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client = accept(listener, (struct sockaddr *) &amp;local,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;addrlen);&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(client &lt; 0){&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("accept");&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setnonblocking(client); // 将新连接置于非阻塞模式&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.events = EPOLLIN | EPOLLET; // 并且将新连接也加入EPOLL的监听队列。&nbsp;<br style="font: 12px song,Verdana;">注意，这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听，如果有写操作的话，这个时候epoll是不会返回事件的，如果要对写操作也监听的话，应该是EPOLLIN | EPOLLOUT | EPOLLET&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.data.fd = client;&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &amp;ev) &lt; 0) {&nbsp;<br style="font: 12px song,Verdana;">// 设置好event之后，将这个新的event通过epoll_ctl加入到epoll的监听队列里面，这里用EPOLL_CTL_ADD来加一个新的 epoll事件，通过EPOLL_CTL_DEL来减少一个epoll事件，通过EPOLL_CTL_MOD来改变一个事件的监听方式。&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "epoll set insertion error: fd=%d0,&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client);&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else // 如果不是主socket的事件的话，则代表是一个用户socket的事件，则来处理这个用户socket的事情，比如说read(fd,xxx)之类的，或者一些其他的处理。&nbsp;<br style="font: 12px song,Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_use_fd(events[n].data.fd);&nbsp;<br style="font: 12px song,Verdana;">}</font></p>
<p style="font: 12px song,Verdana;"><font size="3">对，epoll的操作就这么简单，总共不过4个API：epoll_create, epoll_ctl, epoll_wait和close。&nbsp;<br style="font: 12px song,Verdana;">如果您对epoll的效率还不太了解，请参考我之前关于网络游戏的网络编程等相关的文章。</font></p>
<p style="font: 12px song,Verdana;"><font size="3"><br style="font: 12px song,Verdana;">以前公司的服务器都是使用HTTP连接，但是这样的话，在手机目前的网络情况下不但显得速度较慢，而且不稳定。因此大家一致同意用SOCKET来进行连 接。虽然使用SOCKET之后，对于用户的费用可能会增加(由于是用了CMNET而非CMWAP)，但是，秉着用户体验至上的原则，相信大家还是能够接受 的(希望那些玩家月末收到帐单不后能够保持克制...)。<br style="font: 12px song,Verdana;">这次的服务器设计中，最重要的一个突破，是使用了EPOLL模型，虽然对之也是一知半解，但是既然在各大PC网游中已经经过了如此严酷的考验，相信他不会让我们失望，使用后的结果，确实也是表现相当不错。在这里，我还是主要大致介绍一下这个模型的结构。<br style="font: 12px song,Verdana;">6、Linux下EPOll编程实例<br style="font: 12px song,Verdana;">EPOLL模型似乎只有一种格式，所以大家只要参考我下面的代码，就能够对EPOLL有所了解了，代码的解释都已经在注释中：</font></p>
<p style="font: 12px song,Verdana;"><font size="3">while (TRUE)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT);//等待EPOLL时间的发生，相当于监听，至于相关的端口，需要在初始化EPOLL的时候绑定。<br style="font: 12px song,Verdana;">if (nfds &lt;= 0)<br style="font: 12px song,Verdana;">continue;<br style="font: 12px song,Verdana;">m_bOnTimeChecking = FALSE;<br style="font: 12px song,Verdana;">G_CurTime = time(NULL);<br style="font: 12px song,Verdana;">for (int i=0; i<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">try<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">if (m_events[i].data.fd == m_listen_http_fd)//如果新监测到一个HTTP用户连接到绑定的HTTP端口，建立新的连接。由于我们新采用了SOCKET连接，所以基本没用。<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">OnAcceptHttpEpoll ();<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">else if (m_events[i].data.fd == m_listen_sock_fd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口，建立新的连接。<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">OnAcceptSockEpoll ();<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">else if (m_events[i].events &amp; EPOLLIN)//如果是已经连接的用户，并且收到数据，那么进行读入。<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">OnReadEpoll (i);<br style="font: 12px song,Verdana;">}</font></p>
<p style="font: 12px song,Verdana;"><font size="3">OnWriteEpoll (i);//查看当前的活动连接是否有需要写出的数据。<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">catch (int)<br style="font: 12px song,Verdana;">{<br style="font: 12px song,Verdana;">PRINTF ("CATCH捕获错误\n");<br style="font: 12px song,Verdana;">continue;<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">m_bOnTimeChecking = TRUE;<br style="font: 12px song,Verdana;">OnTimer ();//进行一些定时的操作，主要就是删除一些短线用户等。<br style="font: 12px song,Verdana;">}<br style="font: 12px song,Verdana;">　其实EPOLL的精华，也就是上述的几段短短的代码，看来时代真的不同了，以前如何接受大量用户连接的问题，现在却被如此轻松的搞定，真是让人不得不感叹，对哪。</font></p>
<p style="font: 12px song,Verdana;"><font size="3"><br style="font: 12px song,Verdana;"><strong>总结<br style="font: 12px song,Verdana;"></strong>Windows完成端口与Linux epoll技术方案是这2个平台上实现异步IO和设计开发一个大容量，具可扩展性的winsock程序指服务程序的很好的选择，本文对这2中技术的实现原理和实际的使用方法做了一个详细的介绍。</font></p>
<p style="font: 12px song,Verdana;"><span style="font-size: medium;">转自：</span></p>
<p style="font: 12px song,Verdana;"><span style="font-size: medium;"><a href="http://blog.chinaunix.net/u2/67780/showart_2057153.html">http://blog.chinaunix.net/u2/67780/showart_2057153.html</a></span></p>
</span><img src ="http://www.cppblog.com/beautykingdom/aggbug/101814.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-11-24 15:28 <a href="http://www.cppblog.com/beautykingdom/archive/2009/11/24/101814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux网络socket编程指南</title><link>http://www.cppblog.com/beautykingdom/archive/2009/11/15/101015.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sun, 15 Nov 2009 13:40:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/11/15/101015.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/101015.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/11/15/101015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/101015.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/101015.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Linux Socket Introduction&nbsp;&nbsp;<a href='http://www.cppblog.com/beautykingdom/archive/2009/11/15/101015.html'>阅读全文</a><img src ="http://www.cppblog.com/beautykingdom/aggbug/101015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-11-15 21:40 <a href="http://www.cppblog.com/beautykingdom/archive/2009/11/15/101015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll mechanism from linux man -2.6.16</title><link>http://www.cppblog.com/beautykingdom/archive/2009/11/08/100400.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sun, 08 Nov 2009 05:13:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/11/08/100400.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/100400.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/11/08/100400.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/100400.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/100400.html</trackback:ping><description><![CDATA[<p>1.NAME<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll - I/O event notification facility</p>
<p>SYNOPSIS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/epoll.h&gt;</p>
<p>DESCRIPTION<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll&nbsp; is&nbsp; a&nbsp; variant&nbsp; of&nbsp; poll(2) that can be used either as Edge or Level Triggered interface and scales well to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; large numbers of watched fds. Three system calls are provided to set up and control an epoll set: epoll_create(2),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl(2), epoll_wait(2).</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; An&nbsp; epoll set is connected to a file descriptor created by epoll_create(2).&nbsp; Interest for certain file descriptors<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; is then registered via epoll_ctl(2).&nbsp; Finally, the actual wait is started by epoll_wait(2).</p>
<p><br>NOTES<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The epoll event distribution interface is able to behave both as Edge Triggered ( ET ) and Level Triggered ( LT ).<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp; difference between ET and LT event distribution mechanism can be described as follows. Suppose that this sce-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nario happens :</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The file descriptor that represents the read side of a pipe ( RFD ) is added inside the epoll device.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Pipe writer writes 2Kb of data on the write side of the pipe.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A call to epoll_wait(2) is done that will return RFD as ready file descriptor.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The pipe reader reads 1Kb of data from RFD.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A call to epoll_wait(2) is done.</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If the RFD file descriptor has been added to the epoll interface using the EPOLLET flag, the call to epoll_wait(2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; done&nbsp; in&nbsp; step&nbsp; 5 will probably hang because of the available data still present in the file input buffers and the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; remote peer might be expecting a response based on the data it already sent. The reason&nbsp; for&nbsp; this&nbsp; is&nbsp; that&nbsp; Edge<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Triggered&nbsp; event&nbsp; distribution&nbsp; delivers events only when events happens on the monitored file.&nbsp; So, in step 5 the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; caller might end up waiting for some data that is already present inside the input buffer. In the&nbsp; above&nbsp; example,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; an&nbsp; event&nbsp; on RFD will be generated because of the write done in 2 and the event is consumed in 3.&nbsp; Since the read<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operation done in 4 does not consume the whole buffer data, the call to epoll_wait(2) done in step&nbsp; 5&nbsp; might&nbsp; lock<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; indefinitely. The epoll interface, when used with the EPOLLET flag ( Edge Triggered ) should use non-blocking file<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; descriptors to avoid having a blocking read or write starve the task that is handling multiple&nbsp; file&nbsp; descriptors.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp; suggested&nbsp; way to use epoll as an Edge Triggered (EPOLLET) interface is below, and possible pitfalls to avoid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; follow.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with non-blocking file descriptors</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii&nbsp;&nbsp;&nbsp;&nbsp; by going to wait for an event only after read(2) or write(2) return EAGAIN</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; On the contrary, when used as a Level Triggered interface, epoll is by all means a faster poll(2), and can be used<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wherever&nbsp; the latter is used since it shares the same semantics. Since even with the Edge Triggered epoll multiple<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; events can be generated up on receival of multiple chunks of data, the caller has the option to specify the&nbsp; EPOL-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LONESHOT&nbsp; flag,&nbsp; to&nbsp; tell&nbsp; epoll&nbsp; to&nbsp; disable&nbsp; the&nbsp; associated file descriptor after the receival of an event with<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait(2).&nbsp; When the EPOLLONESHOT flag is specified, it is caller responsibility to rearm the file&nbsp; descriptor<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using epoll_ctl(2) with EPOLL_CTL_MOD.</p>
<p><br>EXAMPLE FOR SUGGESTED USAGE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; While&nbsp; the&nbsp; usage of epoll when employed like a Level Triggered interface does have the same semantics of poll(2),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; an Edge Triggered usage requires more clarification to avoid stalls in the application event loop. In&nbsp; this&nbsp; exam-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ple,&nbsp; listener&nbsp; is a non-blocking socket on which listen(2) has been called. The function do_use_fd() uses the new<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ready file descriptor until EAGAIN is returned by either read(2) or&nbsp; write(2).&nbsp;&nbsp; An&nbsp; event&nbsp; driven&nbsp; state&nbsp; machine<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; application should, after having received EAGAIN, record its current state so that at the next call to do_use_fd()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it will continue to read(2) or write(2) from where it stopped before.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event ev, *events;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = epoll_wait(kdpfd, events, maxevents, -1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(n = 0; n &lt; nfds; ++n) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(events[n].data.fd == listener) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client = accept(listener, (struct sockaddr *) &amp;local,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;addrlen);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(client &lt; 0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("accept");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setnonblocking(client);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.events = EPOLLIN | EPOLLET;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ev.data.fd = client;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &amp;ev) &lt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "epoll set insertion error: fd=%d\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_use_fd(events[n].data.fd);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When used as an Edge triggered interface, for performance reasons, it is&nbsp; possible&nbsp; to&nbsp; add&nbsp; the&nbsp; file&nbsp; descriptor<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inside&nbsp; the&nbsp; epoll&nbsp; interface&nbsp; ( EPOLL_CTL_ADD ) once by specifying ( EPOLLIN|EPOLLOUT ). This allows you to avoid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continuously switching between EPOLLIN and EPOLLOUT calling epoll_ctl(2) with EPOLL_CTL_MOD.</p>
<p>QUESTIONS AND ANSWERS (from linux-kernel)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q1&nbsp;&nbsp;&nbsp;&nbsp; What happens if you add the same fd to an epoll_set twice?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A1&nbsp;&nbsp;&nbsp;&nbsp; You will probably get EEXIST. However, it is possible that two threads may add the same fd twice. This is a<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; harmless condition.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q2&nbsp;&nbsp;&nbsp;&nbsp; Can two epoll sets wait for the same fd? If so, are events reported to both epoll sets fds?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A2&nbsp;&nbsp;&nbsp;&nbsp; Yes. However, it is not recommended. Yes it would be reported to both.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q3&nbsp;&nbsp;&nbsp;&nbsp; Is the epoll fd itself poll/epoll/selectable?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A3&nbsp;&nbsp;&nbsp;&nbsp; Yes.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q4&nbsp;&nbsp;&nbsp;&nbsp; What happens if the epoll fd is put into its own fd set?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A4&nbsp;&nbsp;&nbsp;&nbsp; It will fail. However, you can add an epoll fd inside another epoll fd set.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q5&nbsp;&nbsp;&nbsp;&nbsp; Can I send the epoll fd over a unix-socket to another process?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A5&nbsp;&nbsp;&nbsp;&nbsp; No.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q6&nbsp;&nbsp;&nbsp;&nbsp; Will the close of an fd cause it to be removed from all epoll sets automatically?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A6&nbsp;&nbsp;&nbsp;&nbsp; Yes.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q7&nbsp;&nbsp;&nbsp;&nbsp; If more than one event comes in between epoll_wait(2) calls, are they combined or reported separately?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A7&nbsp;&nbsp;&nbsp;&nbsp; They will be combined.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q8&nbsp;&nbsp;&nbsp;&nbsp; Does an operation on an fd affect the already collected but not yet reported events?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A8&nbsp;&nbsp;&nbsp;&nbsp; You can do two operations on an existing fd. Remove would be meaningless for this case. Modify will re-read<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; available I/O.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q9&nbsp;&nbsp;&nbsp;&nbsp; Do I need to continuously read/write an fd until EAGAIN when&nbsp; using&nbsp; the&nbsp; EPOLLET&nbsp; flag&nbsp; (&nbsp; Edge&nbsp; Triggered<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; behaviour ) ?<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Q9&nbsp;&nbsp;&nbsp;&nbsp; Do I need to continuously read/write an fd until EAGAIN when&nbsp; using&nbsp; the&nbsp; EPOLLET&nbsp; flag&nbsp; (&nbsp; Edge&nbsp; Triggered<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; behaviour ) ?</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A9&nbsp;&nbsp;&nbsp;&nbsp; No&nbsp; you&nbsp; don't.&nbsp; Receiving&nbsp; an&nbsp; event from epoll_wait(2) should suggest to you that such file descriptor is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ready for the requested I/O operation. You have simply to consider it ready until you will receive the next<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EAGAIN.&nbsp; When and how you will use such file descriptor is entirely up to you. Also, the condition that the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read/write I/O space is exhausted can be detected by checking the amount of&nbsp; data&nbsp; read/write&nbsp; from/to&nbsp; the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; target&nbsp; file&nbsp; descriptor.&nbsp; For&nbsp; example, if you call read(2) by asking to read a certain amount of data and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read(2) returns a lower number of bytes, you can be sure to have exhausted the read I/O space for such file<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; descriptor. Same is valid when writing using the write(2) function.</p>
<p><br>POSSIBLE PITFALLS AND WAYS TO AVOID THEM<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; o Starvation ( Edge Triggered )</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If&nbsp; there&nbsp; is&nbsp; a large amount of I/O space, it is possible that by trying to drain it the other files will not get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; processed causing starvation. This is not specific to epoll.</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The solution is to maintain a ready list and mark the file descriptor as ready in its associated&nbsp; data&nbsp; structure,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thereby&nbsp; allowing&nbsp; the&nbsp; application to remember which files need to be processed but still round robin amongst all<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the ready files. This also supports ignoring subsequent events you receive for fd's that are already ready.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; o If using an event cache...</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If you use an event cache or store all the fd's returned from epoll_wait(2), then make sure to provide&nbsp; a&nbsp; way&nbsp; to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mark&nbsp; its&nbsp; closure&nbsp; dynamically (ie- caused by a previous event's processing). Suppose you receive 100 events from<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait(2), and in event #47 a condition causes event #13 to be&nbsp; closed.&nbsp;&nbsp; If&nbsp; you&nbsp; remove&nbsp; the&nbsp; structure&nbsp; and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close()&nbsp; the&nbsp; fd for event #13, then your event cache might still say there are events waiting for that fd causing<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; confusion.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; One solution for this is to call, during the processing of event 47, epoll_ctl(EPOLL_CTL_DEL) to delete fd 13&nbsp; and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(),&nbsp; then&nbsp; mark&nbsp; its&nbsp; associated data structure as removed and link it to a cleanup list. If you find another<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event for fd 13 in your batch processing, you will discover the fd had been previously removed and there&nbsp; will&nbsp; be<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; no confusion.</p>
<p><br>CONFORMING TO<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll(7)&nbsp; is&nbsp; a&nbsp; new&nbsp; API&nbsp; introduced&nbsp; in&nbsp; Linux kernel 2.5.44.&nbsp; Its interface should be finalized in Linux kernel<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.5.66.<br><br>2.NAME<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait - wait for an I/O event on an epoll file descriptor</p>
<p>SYNOPSIS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/epoll.h&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)</p>
<p>DESCRIPTION<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Wait&nbsp; for&nbsp; events&nbsp; on&nbsp; the&nbsp; epoll file descriptor epfd for a maximum time of timeout milliseconds. The memory area<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pointed to by events will contain the events that will be available for the caller.&nbsp; Up to maxevents are&nbsp; returned<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; by&nbsp; epoll_wait(2).&nbsp;&nbsp; The&nbsp; maxevents&nbsp; parameter&nbsp; must&nbsp; be&nbsp; greater&nbsp; than&nbsp; zero.&nbsp; Specifying&nbsp; a&nbsp; timeout of -1 makes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait(2) wait indefinitely, while specifying a timeout equal to zero makes epoll_wait(2)&nbsp; to&nbsp; return&nbsp; immedi-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ately even if no events are available (return code equal to zero).&nbsp; The struct epoll_event is defined as :</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef union epoll_data {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t u32;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint64_t u64;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } epoll_data_t;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t events;&nbsp; /* Epoll events */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_data_t data;&nbsp; /* User data variable */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp;&nbsp; data&nbsp;&nbsp; of&nbsp;&nbsp; each&nbsp; returned&nbsp; structure&nbsp; will&nbsp; contain&nbsp; the&nbsp; same&nbsp; data&nbsp; the&nbsp; user&nbsp; set&nbsp; with&nbsp; a&nbsp; epoll_ctl(2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (EPOLL_CTL_ADD,EPOLL_CTL_MOD) while the events member will contain the returned event bit field.</p>
<p>RETURN VALUE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When successful, epoll_wait(2) returns the number of file descriptors ready for the requested I/O, or zero&nbsp; if&nbsp; no<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file&nbsp; descriptor&nbsp; became&nbsp; ready&nbsp; during&nbsp; the&nbsp; requested timeout milliseconds.&nbsp; When an error occurs, epoll_wait(2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; returns -1 and errno is set appropriately.</p>
<p>ERRORS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EBADF&nbsp; epfd is not a valid file descriptor.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EFAULT The memory area pointed to by events is not accessible with write permissions.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EINTR&nbsp; The call was interrupted by a signal handler before any of the requested events&nbsp; occurred&nbsp; or&nbsp; the&nbsp; timeout<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expired.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.</p>
<p><br>3.NAME<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl - control interface for an epoll descriptor</p>
<p>SYNOPSIS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/epoll.h&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)</p>
<p>DESCRIPTION<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Control an epoll descriptor, epfd, by requesting that the operation op be performed on the target file descriptor,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd.&nbsp; The event describes the object linked to the file descriptor fd.&nbsp; The struct epoll_event is defined as :</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef union epoll_data {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t u32;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint64_t u64;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } epoll_data_t;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t events;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Epoll events */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_data_t data;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* User data variable */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The events member is a bit set composed using the following available event types :</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The associated file is available for read(2) operations.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLOUT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The associated file is available for write(2) operations.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLPRI<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; There is urgent data available for read(2) operations.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLERR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Error condition happened on the associated file descriptor.&nbsp; epoll_wait(2) will always wait for this event;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it is not necessary to set it in events.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLHUP<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hang&nbsp; up&nbsp; happened on the associated file descriptor.&nbsp; epoll_wait(2) will always wait for this event; it is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; not necessary to set it in events.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLET<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sets the Edge Triggered behaviour for the associated file descriptor.&nbsp; The default behaviour for&nbsp; epoll&nbsp; is<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Level&nbsp; Triggered. See epoll(7) for more detailed information about Edge and Level Triggered event distribu-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tion architectures.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLLONESHOT (since kernel 2.6.2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sets the one-shot behaviour for the associated file descriptor.&nbsp; This means that after an event&nbsp; is&nbsp; pulled<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out&nbsp; with&nbsp; epoll_wait(2)&nbsp; the associated file descriptor is internally disabled and no other events will be<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reported by the epoll interface. The user must call epoll_ctl(2) with EPOLL_CTL_MOD to re-enable&nbsp; the&nbsp; file<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; descriptor with a new event mask.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The epoll interface supports all file descriptors that support poll(2).&nbsp; Valid values for the op parameter are :</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLL_CTL_ADD<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Add&nbsp; the&nbsp; target&nbsp; file descriptor fd to the epoll descriptor epfd and associate the event event with<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the internal file linked to fd.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLL_CTL_MOD<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Change the event event associated with the target file descriptor fd.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPOLL_CTL_DEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Remove the target file descriptor fd from the epoll file descriptor, epfd.&nbsp; The event is ignored and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; can be NULL (but see BUGS below).</p>
<p>RETURN VALUE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When&nbsp; successful, epoll_ctl(2) returns zero. When an error occurs, epoll_ctl(2) returns -1 and errno is set appro-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; priately.</p>
<p>ERRORS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EBADF&nbsp; epfd is not a valid file descriptor.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EEXIST op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already in epfd.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EINVAL epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested operation op is not&nbsp; sup-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ported by this interface.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ENOENT op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not in epfd.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ENOMEM There was insufficient memory to handle the requested op control operation.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EPERM&nbsp; The target file fd does not support epoll.</p>
<p>CONFORMING TO<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_ctl(2)&nbsp; is&nbsp; a&nbsp; new API introduced in Linux kernel 2.5.44.&nbsp; The interface should be finalized by Linux kernel<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.5.66.</p>
<p>BUGS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required a non-NULL pointer&nbsp; in&nbsp; event,&nbsp; even&nbsp; though<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this argument is ignored.&nbsp; Since kernel 2.6.9, event can be specified as NULL when using EPOLL_CTL_DEL.</p>
<p><br>4.NAME<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait - wait for an I/O event on an epoll file descriptor</p>
<p>SYNOPSIS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/epoll.h&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)</p>
<p>DESCRIPTION<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Wait&nbsp; for&nbsp; events&nbsp; on&nbsp; the&nbsp; epoll file descriptor epfd for a maximum time of timeout milliseconds. The memory area<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pointed to by events will contain the events that will be available for the caller.&nbsp; Up to maxevents are&nbsp; returned<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; by&nbsp; epoll_wait(2).&nbsp;&nbsp; The&nbsp; maxevents&nbsp; parameter&nbsp; must&nbsp; be&nbsp; greater&nbsp; than&nbsp; zero.&nbsp; Specifying&nbsp; a&nbsp; timeout of -1 makes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait(2) wait indefinitely, while specifying a timeout equal to zero makes epoll_wait(2)&nbsp; to&nbsp; return&nbsp; immedi-<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ately even if no events are available (return code equal to zero).&nbsp; The struct epoll_event is defined as :</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; typedef union epoll_data {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *ptr;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t u32;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint64_t u64;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } epoll_data_t;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct epoll_event {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __uint32_t events;&nbsp; /* Epoll events */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_data_t data;&nbsp; /* User data variable */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp;&nbsp; data&nbsp;&nbsp; of&nbsp;&nbsp; each&nbsp; returned&nbsp; structure&nbsp; will&nbsp; contain&nbsp; the&nbsp; same&nbsp; data&nbsp; the&nbsp; user&nbsp; set&nbsp; with&nbsp; a&nbsp; epoll_ctl(2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (EPOLL_CTL_ADD,EPOLL_CTL_MOD) while the events member will contain the returned event bit field.</p>
<p>RETURN VALUE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When successful, epoll_wait(2) returns the number of file descriptors ready for the requested I/O, or zero&nbsp; if&nbsp; no<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file&nbsp; descriptor&nbsp; became&nbsp; ready&nbsp; during&nbsp; the&nbsp; requested timeout milliseconds.&nbsp; When an error occurs, epoll_wait(2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; returns -1 and errno is set appropriately.</p>
<p>ERRORS<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EBADF&nbsp; epfd is not a valid file descriptor.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EFAULT The memory area pointed to by events is not accessible with write permissions.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EINTR&nbsp; The call was interrupted by a signal handler before any of the requested events&nbsp; occurred&nbsp; or&nbsp; the&nbsp; timeout<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expired.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EINVAL epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.</p>
<p>CONFORMING TO<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; epoll_wait(2)&nbsp; is&nbsp; a new API introduced in Linux kernel 2.5.44.&nbsp; The interface should be finalized by Linux kernel<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.5.66.</p>
<p><br><br><br><br><br>&nbsp;</p><img src ="http://www.cppblog.com/beautykingdom/aggbug/100400.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/beautykingdom/" target="_blank">chatler</a> 2009-11-08 13:13 <a href="http://www.cppblog.com/beautykingdom/archive/2009/11/08/100400.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>