﻿<?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-随笔分类-Network</title><link>http://www.cppblog.com/beautykingdom/category/12134.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 22 May 2012 05:14:40 GMT</lastBuildDate><pubDate>Tue, 22 May 2012 05:14:40 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>一个基于完成端口的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>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>NAT的缺陷</title><link>http://www.cppblog.com/beautykingdom/archive/2010/07/13/120225.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 13 Jul 2010 07:28:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/07/13/120225.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/120225.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/07/13/120225.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/120225.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/120225.html</trackback:ping><description><![CDATA[NAT的优点不必多讲,它提供了一系列相关技术来实现多个内网用户通过一个公网ip和外部通信,有效的解决了ipv4地址不够用的问题.那么位于NAT后
的用户使用私网ip真的和使用公网ip一样吗?NAT解决了所有地址转换的相关问题了吗?<br>下面主要讲一些NAT不支持的方面,以及所谓的NAT
的"缺陷".<br><br>一些应用层协议(如TCP和SIP),在它们的应用层数据中需要包含公网IP地址.拿FTP来说吧,众所周知,FTP是通过
两个不同的连接来传输控制报文和数据报文的.当传输一个文件时,FTP服务器要求通过控制报文得到即将传输的数据报文的网络层和传输层地址
(IP/PORT).如果这个时候客户主机是在NAT之后的,那么服务器端收到的ip/port将会是NAT转化前的私网IP地址,从而会导致文件传输失
效.<br>SIP(Session Initiation
Protocol)主要是来控制音频传输的,这个协议也面临同样的问题.因为SIP建立连接时,需要用到几个不同的端口来通过RTP传输音频流.而且这些
端口以及IP会被编码到音频流中,传输给服务器端,从而实现后续的通信.<br>如果没有一些特殊的技术(如STUN),那么NAT是不支持这些协议的,
这些协议经过NAT也肯定会失败.<br><span style="color: #000166;">Some
Application Layer protocols (such as FTP and SIP) send explicit network
addresses within their application data. FTP in active mode, for
example, uses separate connections for control traffic (commands) and
for data traffic (file contents). When requesting a file transfer, the
host making the request identifies the corresponding data connection by
its network layer and transport layer addresses. If the host making the
request lies behind a simple NAT firewall, the translation of the IP
address and/or TCP port number makes the information received by the
server invalid. The Session Initiation Protocol (SIP) controls Voice
over IP (VoIP) communications and suffers the same problem. SIP may use
multiple ports to set up a connection and transmit voice stream via RTP.
IP addresses and port numbers are encoded in the payload data and must
be known prior to the traversal of NATs. Without special techniques,
such as STUN, NAT behavior is unpredictable and communications may fail.</span><br><br>下
面讲一些特殊的技术,来使NAT支持这些特殊的应用层协议.<br><br>最直观的想法就是:既然NAT修改了IP/PROT,那么我们也修改应用层数
据中相应的IP/PORT.应用层网关(ALG)(硬件或软件都行)就是这样来解决这个问题的.应用层网关运行在设置了NAT的防火墙设备中,它会更新传
输数据中的IP/PORT.所以,应用层网关也必须能够解析应用层协议,而且对于每一种协议,可能需要不同的应用层网关来做.<br><span style="color: #000166;">Application Layer Gateway (ALG) software
or hardware may correct these problems. An ALG software module running
on a NAT firewall device updates any payload data made invalid by
address translation. ALGs obviously need to understand the higher-layer
protocol that they need to fix, and so each protocol with this problem
requires a separate ALG.</span><br><br>另外一个解决此问题的办法就是NAT穿透.此方法主要利用STUN或
ICE等协议或者一些和会话控制相关的特有的方法来实现.理论上NAT穿透最好能够同时适用于基于TCP和基于UDP的应用,但是基于UDP的应用相对比
较简单,更广为流传,也更适合兼容一些种类的NAT做穿透.这样,应用层协议在设计的时候,必须考虑到可支持NAT穿透.但一些其他类型的NAT(比如对
称NAT)是无论如何也不能做穿透的.<br><span style="color: #000166;">Another
possible solution to this problem is to use NAT traversal techniques
using protocols such as STUN or ICE or proprietary approaches in a
session border controller. NAT traversal is possible in both TCP- and
UDP-based applications, but the UDP-based technique is simpler, more
widely understood, and more compatible with legacy NATs. In either case,
the high level protocol must be designed with NAT traversal in mind,
and it does not work reliably across symmetric NATs or other
poorly-behaved legacy NATs.</span><br><br><br>还有一些方法,比如UPnP (Universal
Plug and Play) 或 Bonjour (NAT-PMP),但是这些方法都需要专门的NAT设备.<br><span style="color: #000166;">Other possibilities are UPnP (Universal
Plug and Play) or Bonjour (NAT-PMP), but these require the cooperation
of the NAT device.</span><br><br><br>大部分传统的客户-服务器协议(除了FTP),都不定义3层以上的数据格
式,所以,也就可以和传统的NAT兼容.实际上,在设计应用层协议的时候应尽量避免涉及到3层以上的数据,因为这样会使它兼容NAT时复杂化.<br><span style="color: #000166;">Most traditional client-server protocols
(FTP being the main exception), however, do not send layer 3 contact
information and therefore do not require any special treatment by NATs.
In fact, avoiding NAT complications is practically a requirement when
designing new higher-layer protocols today.</span><br style="color: #000166;"><br><br>NAT也会和利用ipsec加密的一些应用冲突.比如SIP电话,如果有很多SIP电话设备在
NA(P)T之后,那么在电话利用ipsc加密它们的信号时,如果也加密了port信息,那么这就意味着NAPT就不能转换port,只能转换IP.但是
这样就会导致回来的数据包都被NAT到同一个客户端,从而导致通信失败(不太明白).不过,这个问题有很多方法来解决,比如用TLS.TLS是运行在第四
层(OSI模型)的,所以它不包含port信息.也可以在UDP之内来封装ipsec,TISPAN 就是用这种方法来实现安全NAT转化的.<br><span style="color: #000166;">NATs can also cause problems where IPsec
encryption is applied and in cases where multiple devices such as SIP
phones are located behind a NAT. Phones which encrypt their signaling
with IPsec encapsulate the port information within the IPsec packet
meaning that NA(P)T devices cannot access and translate the port. In
these cases the NA(P)T devices revert to simple NAT operation. This
means that all traffic returning to the NAT will be mapped onto one
client causing the service to fail. There are a couple of solutions to
this problem, one is to use TLS which operates at level 4 in the OSI
Reference Model and therefore does not mask the port number, or to
Encapsulate the IPsec within UDP - the latter being the solution chosen
by TISPAN to achieve secure NAT traversal.</span><br><br><br>Dan
Kaminsky
在2008年的时候提出NAPT还会间接的影响DNS协议的健壮性,为了避免DNS服务器缓存中毒,在NA(p)T防火墙之后的DNS服务器最好不要转换
来自外部的DNS请求(UDP)的源端口.而对DNS缓存中毒攻击的应对措施就是使所有的DNS服务器用随机的端口来接收DNS请求.但如果NA(P)T
使DNS请求的源端口也随机化,那么在NA(P)T防火墙后面的DNS服务器还是会崩溃的.<br><span style="color: #000166;">The DNS protocol vulnerability announced by Dan
Kaminsky on 2008 July 8 is indirectly affected by NAT port mapping. To
avoid DNS server cache poisoning, it is highly desirable to not
translate UDP source port numbers of outgoing DNS requests from any DNS
server which is behind a firewall which implements NAT. The recommended
work-around for the DNS vulnerability is to make all caching DNS servers
use randomized UDP source ports. If the NAT function de-randomizes the
UDP source ports, the DNS server will be made vulnerable.</span><br><br>位
于NAT后的主机不能实现真的端对端的通信,也不能使用一些和NAT冲突的internat协议.而且从外部发起的TCP连接和一些无状态的协议(利用
udp的上层协议)也不能正常的进行,除非NAT所在设备通过相关技术支持这些协议.一些协议能够利用应用层网关或其他技术,来使只有一端处于NAT后的
通信双方正常通信.但要是双方都在NAT后就会失败.NAT也和一些隧道协议(如ipsec)冲突,因为NAT会修改ip或port,从而会使协议的完整
性校验失败.<br><span style="color: #000166;">Hosts behind NAT-enabled
routers do not have end-to-end connectivity and cannot participate in
some Internet protocols. Services that require the initiation of TCP
connections from the outside network, or stateless protocols such as
those using UDP, can be disrupted. Unless the NAT router makes a
specific effort to support such protocols, incoming packets cannot reach
their destination. Some protocols can accommodate one instance of NAT
between participating hosts ("passive mode" FTP, for example), sometimes
with the assistance of an application-level gateway (see below), but
fail when both systems are separated from the Internet by NAT. Use of
NAT also complicates tunneling protocols such as IPsec because NAT
modifies values in the headers which interfere with the integrity checks
done by IPsec and other tunneling protocols.</span><br><br><br>端对端的连接是
internet设计时的一个重要的核心的基本原则.而NAT是违背这一原则的,但是NAT在设计的时候也充分地考虑到了这些问题.现在基于ipv6的
NAT已经被广泛关注,但许多ipv6架构设计者认为ipv6应该摒弃NAT.<br><span style="color: #000166;">End-to-end connectivity has been a core principle of the
Internet, supported for example by the Internet Architecture Board.
Current Internet architectural documents observe that NAT is a violation
of the End-to-End Principle, but that NAT does have a valid role in
careful design. There is considerably more concern with the use of IPv6
NAT, and many IPv6 architects believe IPv6 was intended to remove the
need for NAT.</span><br><br><br>由于NAT的连接追踪具有短时效性.所以在特定的地址转换关系会在一小段时间后失效,
除非遵守NAT的keep-alive机制,内网主机不时的去访问外部主机.这至少会造成一些不必要的消耗,比如消耗手持设备的电量.<br><span style="color: #000166;">Because of the short-lived nature of the
stateful translation tables in NAT routers, devices on the internal
network lose IP connectivity typically within a very short period of
time unless they implement NAT keep-alive mechanisms by frequently
accessing outside hosts. This dramatically shortens the power reserves
on battery-operated hand-held devices and has thwarted more widespread
deployment of such IP-native Internet-enabled devices.</span><br style="color: #000166;"><br><br>一些IPS会直接提供给用户私网IP地址,这样用户就必须通过IPS的
NAT来和外部INTERNET通信.这样,用户实际上没有实现端对端通信,中间加了一个IPS的NAT,这有悖于Internet
Architecture Board列出的internal核心基本原则.<br><span style="color: #000166;">Some Internet service providers (ISPs) provide their customers
only with "local" IP addresses.[citation needed]Thus, these customers
must access services external to the ISP's network through NAT. As a
result, the customers cannot achieve true end-to-end connectivity, in
violation of the core principles of the Internet as laid out by the
Internet Architecture Board.</span><br style="color: #000166;"><br>NAT
最后的一个缺陷就是:NAT的推广和使用,解决了ipv4下IP地址不够用的问题,大大的推迟了IPV6的发展.<br>(说它是优点好呢,还是缺陷好
呢?)<br><span style="color: #000166;">it is possible that its
[NAT] widespread use will significantly delay the need to deploy IPv6</span><br><br>Reference:<br><a  href="http://en.wikipedia.org/wiki/Network_address_translation#Types_of_NAT" target="_blank">Network address translation</a><br><br>from:<br>http://blog.chinaunix.net/u2/86590/showart.php?id=2208148<br><img src ="http://www.cppblog.com/beautykingdom/aggbug/120225.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-13 15:28 <a href="http://www.cppblog.com/beautykingdom/archive/2010/07/13/120225.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下面socket编程的非阻塞TCP 研究</title><link>http://www.cppblog.com/beautykingdom/archive/2010/07/07/119615.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 07 Jul 2010 09:14:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/07/07/119615.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/119615.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/07/07/119615.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/119615.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/119615.html</trackback:ping><description><![CDATA[<div id="art" width="100%" style="margin: 15px;">
<p><span style="font-family: Georgia; line-height: normal;"><span style="line-height: normal;"><strong style="line-height: normal;">tcp协议本
身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据.</strong></span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal; color: #016600;"><span style="line-height: normal; font-family: 'Courier New';">在<span style="line-height: normal; color: #ff0000;">阻塞模式</span>下,
send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大
小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将
数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv);<br style="line-height: normal;"><br style="line-height: normal;"></span></span></span><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal; color: #009902;"><span style="line-height: normal; color: #006666;">在</span><span style="line-height: normal; color: #ff0102;">非阻塞模式</span><span style="line-height: normal; color: #006666;">下,send函数的过程仅仅是将数据拷
贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置errno为
EAGAIN.</span></span><br style="line-height: normal;"><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal;">linux下可用<span style="line-height: normal; color: #cc3333;">sysctl -a | grep net.ipv4.tcp_wmem</span>查看系统默
认的发送缓存大小:</span><br style="line-height: normal;"><span style="line-height: normal;">net.ipv4.tcp_wmem = 4096 16384 81920</span><br style="line-height: normal;"><span style="line-height: normal;">这
有三个值,第一个值是socket的发送缓存区分配的最少字节数,第二个值是默认值(该值会被net.core.wmem_default覆盖),缓存区
在系统负载不重的情况下可以增长到这个值,第三个值是发送缓存区空间的最大字节数(该值会被net.core.wmem_max覆盖).</span><br style="line-height: normal;"><span style="line-height: normal;">根据实际测试,
如果手工更改了net.ipv4.tcp_wmem的值,则会按更改的值来运行,否则在默认情况下,协议栈通常是按
net.core.wmem_default和net.core.wmem_max的值来分配内存的.</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal;">应用程序应该根据应用的特性在程序中更改发送缓存大小:</span><br style="line-height: normal;"><br style="line-height: normal;"></span>
<table style="table-layout: auto; line-height: normal; border-collapse: collapse;" bgcolor="#f1f1f1" border="1" bordercolor="#999999" cellpadding="0" cellspacing="0" width="95%">
    <tbody style="line-height: normal;">
        <tr style="line-height: normal;">
            <td style="font-family: Arial; visibility: visible ! important; filter: none; font-size: 12px; line-height: normal;">
            <p style="margin: 5px; line-height: 18px;"><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal;">socklen_t
            sendbuflen = 0;</span><br style="line-height: normal;"><span style="line-height: normal;">socklen_t len = sizeof(sendbuflen);</span><br style="line-height: normal;"><span style="line-height: normal;">getsockopt(clientSocket,
            SOL_SOCKET, SO_SNDBUF, (void*)&amp;sendbuflen, &amp;len);</span><br style="line-height: normal;"><span style="line-height: normal;">printf("default,sendbuf:%d\n",
            sendbuflen);</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal;">sendbuflen
            = 10240;</span><br style="line-height: normal;"><span style="line-height: normal;">setsockopt(clientSocket, SOL_SOCKET,
            SO_SNDBUF, (void*)&amp;sendbuflen, len);</span><br style="line-height: normal;"><span style="line-height: normal;">getsockopt(clientSocket,
            SOL_SOCKET, SO_SNDBUF, (void*)&amp;sendbuflen, &amp;len);</span><br style="line-height: normal;"><span style="line-height: normal;">printf("now,sendbuf:%d\n",
            sendbuflen);</span></span></p>
            </td>
        </tr>
    </tbody>
</table>
<br style="line-height: normal;"><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal;">需要注意的是,虽然将发送缓存设置
成了10k,但实际上,协议栈会将其扩大1倍,设为20k.</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal; font-size: large;"><br style="line-height: normal;">-------------------实
例分析----------------------</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal;">在
实际应用中,如果发送端是非阻塞发送,由于网络的阻塞或者接收端处理过慢,通常出现的情况是,发送应用程序看起来发送了10k的数据,但是只发送了2k到
对端缓存中,还有8k在本机缓存中(未发送或者未得到接收端的确认).那么此时,接收应用程序能够收到的数据为2k.假如接收应用程序调用recv函数获
取了1k的数据在处理,在这个瞬间,发生了以下情况之一,双方表现为:</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal; font-weight: bold;">A. 发送应用程序认为send完了10k数据,关闭了socket:</span><br style="line-height: normal;"><span style="line-height: normal;">发
送主机作为tcp的主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack),并且,发送缓存中的8k数据并不清除,依然会发送给对
端.如果接收应用程序依然在recv,那么它会收到余下的8k数据(这个前题是,接收端会在发送端FIN_WAIT1状态超时前收到余下的8k数据.),
然后得到一个对端socket被关闭的消息(recv返回0).这时,应该进行关闭.<br style="line-height: normal;"></span><br style="line-height: normal;"><span style="line-height: normal; font-weight: bold;">B. 发送应用程序再次调用send发送8k的数据:</span><br style="line-height: normal;"><span style="line-height: normal;">假
如发送缓存的空间为20k,那么发送缓存可用空间为20-8=12k,大于请求发送的8k,所以send函数将数据做拷贝后,并立即返回8192;<br style="line-height: normal;"><br style="line-height: normal;">假
如发
送缓存的空间为12k,那么此时发送缓存可用空间还有12-8=4k,send()会返回4096,应用程序发现返回的值小于请求发送的大小值后,可以认
为缓存区已满,这时必须阻塞(或通过select等待下一次socket可写的信号),如果应用程序不理会,立即再次调用send,那么会得到-1的值,
在linux下表现为errno=EAGAIN.</span><br style="line-height: normal;"><br style="line-height: normal;"><span style="line-height: normal; font-weight: bold;">C. 接收应用程序在处理完1k数据后,关闭了socket:</span><br style="line-height: normal;"><span style="line-height: normal;">接
收主机作为主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack).然后,发送应用程序会收到socket可读的信号(通常是
select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);<br style="line-height: normal;"><br style="line-height: normal;">如
果发送应用程序没有处理这个可读的信号,而是在send,那么这要分两种情况来考虑,假如是在发送端收到RST标志之后调用send,send将返回
-1,同时errno设为ECONNRESET表示对端网络已断开,</span></span><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal;"><span style="line-height: normal; font-style: italic;">但是,也有说法是进程会收到SIGPIPE信号,
该信号的默认响应动作是退出进程,如果忽略该信号,那么send是返回-1,errno为EPIPE(未证实)</span>;</span></span><span style="line-height: normal; font-size: x-small;"><span style="line-height: normal;">如果是在发送端收到RST标志之前,则send像往常一样工作;<br style="line-height: normal;"><br style="line-height: normal;">以上说的是非阻塞的
send情况,假如send是阻塞调用,并且正好处于阻塞时(例如一次性发送一个巨大的buf,超出了发送缓存),对端socket关闭,那么send将
返回成功发送的字节数,如果再次调用send,那么会同上一样.<br style="line-height: normal;"></span><br style="line-height: normal;"><span style="line-height: normal; font-weight: bold;">D. 交换机或路由器的网络断开:</span><br style="line-height: normal;"><span style="line-height: normal;">接收应用程序在处理完已收到的1k数据后,会继续从缓存区读
取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依
然没有数据可读,则认为socket已不可用.<br style="line-height: normal;"><br style="line-height: normal;"></span><span style="line-height: normal;">发
送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空间持续为0,这种情况也需要应用程序来处理.<br style="line-height: normal;"><br style="line-height: normal;"></span><span style="line-height: normal;">如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查
看sysctl项中的:</span><br style="line-height: normal;"><span style="line-height: normal;">net.ipv4.tcp_keepalive_intvl</span><br style="line-height: normal;"><span style="line-height: normal;">net.ipv4.tcp_keepalive_probes</span><br style="line-height: normal;"><span style="line-height: normal;">net.ipv4.tcp_keepalive_time</span></span></span></p>
</div>
<p style="line-height: 150%; margin: 5px;">
</p>
&nbsp;<font color="#000099"><strong>原文地址</strong></font>
<a  href="http://xufish.blogbus.com/logs/40537344.html" target="_blank">http://xufish.blogbus.com/logs/40537344.html</a><br><br>from:<br><a  href="http://blog.chinaunix.net/u2/67780/showart_2056353.html">http://blog.chinaunix.net/u2/67780/showart_2056353.html</a><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/119615.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-07 17:14 <a href="http://www.cppblog.com/beautykingdom/archive/2010/07/07/119615.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你用c实现http协议</title><link>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118839.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sun, 27 Jun 2010 15:16:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118839.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/118839.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/118839.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/118839.html</trackback:ping><description><![CDATA[<p style="margin: 5px; line-height: 150%;"><code>大家都很熟悉HTTP协议的应用，因为每天都在网络上浏览着不少东西，也都知道是HTTP协议是相当简单的。每次用
thunder之类的下载软件下载网页，当用到那个&#8220;用thunder下载全部链接&#8221;时总觉得很神奇。<br>
后来想想，其实要实现这些下载功能也并不难，只要按照HTTP协议发送request，然后对接收到的数据进行分析，如果页面上还有href之类的链接指
向标志就可以进行深一层的下载了。HTTP协议目前用的最多的是1.1
版本，要全面透彻地搞懂它就参考RFC2616文档吧。我是怕rfc文档了的,要看自己去看吧^_^<br>
源代码如下：<br>
/******* http客户端程序 httpclient.c
************/<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;<br>
<br>
//////////////////////////////httpclient.c
开始///////////////////////////////////////////<br>
<br>
<br>
/********************************************<br>
功能：搜索字符串右边起的第一个匹配字符<br>
********************************************/<br>
char * Rstrchr(char * s, char x)  {<br>
&nbsp;&nbsp;int i = strlen(s);<br>
&nbsp;&nbsp;if(!(*s))  return 0;<br>
&nbsp;&nbsp;while(s[i-1]) if(strchr(s + (i - 1), x))  return (s + (i - 1));  else i--;<br>
&nbsp;&nbsp;return 0;<br>
}<br>
<br>
/********************************************<br>
功能：把字符串转换为全小写<br>
********************************************/<br>
void ToLowerCase(char * s)  {<br>
&nbsp;&nbsp;while(s &amp;&amp; *s) {*s=tolower(*s);s++;}<br>
}<br>
<br>
/**************************************************************<br>
功能：从字符串src中分析出网站地址和端口，并得到用户要下载的文件<br>
***************************************************************/<br>
void GetHost(char * src, char * web, char * file, int * port)  {<br>
&nbsp;&nbsp;char * pA;<br>
&nbsp;&nbsp;char * pB;<br>
&nbsp;&nbsp;memset(web,
0, sizeof(web));<br>
&nbsp;&nbsp;memset(file, 0, sizeof(file));<br>
&nbsp;&nbsp;*port = 0;<br>
&nbsp;&nbsp;if(!(*src))  return;<br>
&nbsp;&nbsp;pA = src;<br>
&nbsp;&nbsp;if(!strncmp(pA, "http://", strlen("http://")))  pA = src+strlen("http://");<br>
&nbsp;&nbsp;else if(!strncmp(pA, "https://", strlen("https://")))  pA = src+strlen("https://");<br>
&nbsp;&nbsp;pB = strchr(pA, '/');<br>
&nbsp;&nbsp;if(pB)
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;memcpy(web, pA, strlen(pA) - strlen(pB));<br>
&nbsp;&nbsp;&nbsp;&nbsp;if(pB+1)  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(file, pB + 1, strlen(pB) - 1);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file[strlen(pB) - 1] = 0;<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;else  memcpy(web, pA, strlen(pA));<br>
&nbsp;&nbsp;if(pB)
web[strlen(pA) - strlen(pB)] = 0;<br>
&nbsp;&nbsp;else  web[strlen(pA)] = 0;<br>
&nbsp;&nbsp;pA = strchr(web, ':');<br>
&nbsp;&nbsp;if(pA)
*port = atoi(pA + 1);<br>
&nbsp;&nbsp;else *port =
80;<br>
}<br>
<br>
<br>
int main(int
argc, char *argv[])<br>
{<br>
&nbsp;&nbsp;int sockfd;<br>
&nbsp;&nbsp;char buffer[1024];<br>
&nbsp;&nbsp;struct sockaddr_in server_addr;<br>
&nbsp;&nbsp;struct hostent *host;<br>
&nbsp;&nbsp;int portnumber,nbytes;<br>
&nbsp;&nbsp;char host_addr[256];<br>
&nbsp;&nbsp;char host_file[1024];<br>
&nbsp;&nbsp;char local_file[256];<br>
&nbsp;&nbsp;FILE * fp;<br>
&nbsp;&nbsp;char request[1024];<br>
&nbsp;&nbsp;int send,
totalsend;<br>
&nbsp;&nbsp;int i;<br>
&nbsp;&nbsp;char * pt;<br>
<br>
&nbsp;&nbsp;if(argc!=2)<br>
&nbsp;&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,"Usage:%s web-address\a\n",argv[0]);<br>
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;printf("parameter.1
is: %s\n", argv[1]);<br>
&nbsp;&nbsp;ToLowerCase(argv[1]);/*将参数转换为全小写*/<br>
&nbsp;&nbsp;printf("lowercase
parameter.1 is: %s\n",
argv[1]);<br>
<br>
&nbsp;&nbsp;GetHost(argv[1], host_addr, host_file, &amp;portnumber);/*分析网址、端口、文件名等*/<br>
&nbsp;&nbsp;printf("webhost:%s\n", host_addr);<br>
&nbsp;&nbsp;printf("hostfile:%s\n", host_file);<br>
&nbsp;&nbsp;printf("portnumber:%d\n\n", portnumber);<br>
<br>
&nbsp;&nbsp;if((host=gethostbyname(host_addr))==NULL)/*取得主机IP地址*/<br>
&nbsp;&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,"Gethostname error, %s\n", strerror(errno));<br>
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br>
&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;/* 客户程序开始建立 sockfd描述符 */<br>
&nbsp;&nbsp;if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/<br>
&nbsp;&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));<br>
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br>
&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;/* 客户程序填充服务端的资料 */<br>
&nbsp;&nbsp;bzero(&amp;server_addr,sizeof(server_addr));<br>
&nbsp;&nbsp;server_addr.sin_family=AF_INET;<br>
&nbsp;&nbsp;server_addr.sin_port=htons(portnumber);<br>
&nbsp;&nbsp;server_addr.sin_addr=*((struct in_addr
*)host-&gt;h_addr);<br>
<br>
&nbsp;&nbsp;/* 客户程序发起连接请求 */<br>
&nbsp;&nbsp;if(connect(sockfd,(struct sockaddr *)(&amp;server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/<br>
&nbsp;&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));<br>
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br>
&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;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>
&nbsp;&nbsp;printf("%s", request);/*准备request，将要发送给主机*/<br>
<br>
&nbsp;&nbsp;/*取得真实的文件名*/<br>
&nbsp;&nbsp;if(host_file &amp;&amp; *host_file)
pt = Rstrchr(host_file, '/');<br>
&nbsp;&nbsp;else pt = 0;<br>
<br>
&nbsp;&nbsp;memset(local_file,
0, sizeof(local_file));<br>
&nbsp;&nbsp;if(pt &amp;&amp; *pt)  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;if((pt
+ 1) &amp;&amp; *(pt+1))  strcpy(local_file,
pt + 1);<br>
&nbsp;&nbsp;&nbsp;&nbsp;else  memcpy(local_file,
host_file, strlen(host_file)
- 1);<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;else if(host_file
&amp;&amp; *host_file)  strcpy(local_file, host_file);<br>
&nbsp;&nbsp;else  strcpy(local_file, "index.html");<br>
&nbsp;&nbsp;printf("local
filename to write:%s\n\n",
local_file);<br>
<br>
&nbsp;&nbsp;/*发送http请求request*/<br>
&nbsp;&nbsp;send = 0;totalsend
= 0;<br>
&nbsp;&nbsp;nbytes=strlen(request);<br>
&nbsp;&nbsp;while(totalsend &lt;
nbytes) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;send = write(sockfd, request +
totalsend, nbytes - totalsend);<br>
&nbsp;&nbsp;&nbsp;&nbsp;if(send==-1)  {printf("send error!%s\n",
strerror(errno));exit(0);}<br>
&nbsp;&nbsp;&nbsp;&nbsp;totalsend+=send;<br>
&nbsp;&nbsp;&nbsp;&nbsp;printf("%d bytes send OK!\n",
totalsend);<br>
&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;fp = fopen(local_file, "a");<br>
&nbsp;&nbsp;if(!fp)  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;printf("create file error! %s\n", strerror(errno));<br>
&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;printf("\nThe
following is the response header:\n");<br>
&nbsp;&nbsp;i=0;<br>
&nbsp;&nbsp;/* 连接成功了，接收http响应，response */<br>
&nbsp;&nbsp;while((nbytes=read(sockfd,buffer,1))==1)<br>
&nbsp;&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;if(i &lt;
4)  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(buffer[0] == '\r' || buffer[0] == '\n')  i++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else i = 0;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%c", buffer[0]);/*把http头信息打印在屏幕上*/<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;else  {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fwrite(buffer, 1, 1, fp);/*将http主体信息写入文件*/<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(i%1024
== 0)  fflush(fp);/*每1K时存盘一次*/<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;}<br>
&nbsp;&nbsp;fclose(fp);<br>
&nbsp;&nbsp;/* 结束通讯 */<br>
&nbsp;&nbsp;close(sockfd);<br>
&nbsp;&nbsp;exit(0);<br>
}</code></p>
<p style="margin: 5px; line-height: 150%;"><br><code></code></p>
<p style="margin: 5px; line-height: 150%;"><code>zj@zj:~/C_pram/practice/http_client$
ls<br>httpclient&nbsp; httpclient.c<br>zj@zj:~/C_pram/practice/http_client$
./httpclient http://www.baidu.com/<br>parameter.1 is:
http://www.baidu.com/<br>lowercase parameter.1 is: http://www.baidu.com/<br>webhost:www.baidu.com<br>hostfile:<br>portnumber:80<br><br>GET
/ HTTP/1.1<br>Accept: */*<br>Accept-Language: zh-cn<br>User-Agent:
Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)<br>Host:
www.baidu.com:80<br>Connection: Close<br><br>local filename to
write:index.html<br><br>163 bytes send OK!<br><br>The following is the
response header:<br>HTTP/1.1 200 OK<br>Date: Wed, 29 Oct 2008 10:41:40
GMT<br>Server: BWS/1.0<br>Content-Length: 4216<br>Content-Type:
text/html<br>Cache-Control: private<br>Expires: Wed, 29 Oct 2008
10:41:40 GMT<br>Set-Cookie:
BAIDUID=A93059C8DDF7F1BC47C10CAF9779030E:FG=1; expires=Wed, 29-Oct-38
10:41:40 GMT; path=/; domain=.baidu.com<br>P3P: CP=" OTI DSP COR IVA OUR
IND COM "<br><br>zj@zj:~/C_pram/practice/http_client$ ls<br>httpclient&nbsp;
httpclient.c&nbsp; index.html<br></code></p>
<code>不指定文件名字的话,默认就是下载网站默认的首页了^_^.</code>
<br><br>from:<br><a  href="http://blog.chinaunix.net/u2/76292/showart_1353805.html">http://blog.chinaunix.net/u2/76292/showart_1353805.html
</a><br><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/118839.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-06-27 23:16 <a href="http://www.cppblog.com/beautykingdom/archive/2010/06/27/118839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c语言抓取网页数据</title><link>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118838.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Sun, 27 Jun 2010 15:13:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118838.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/118838.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/06/27/118838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/118838.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/118838.html</trackback:ping><description><![CDATA[<p style="margin: 5px; line-height: 150%;"><code>#include &lt;stdio.h&gt;<br>#include
&lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#include &lt;sys/socket.h&gt;<br>#include &lt;netinet/in.h&gt;<br>#include &lt;netdb.h&gt;<br><br>#define HTTPPORT 80<br><br><br>char* head =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"GET /u2/76292/ HTTP/1.1\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Accept: */*\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Accept-Language: zh-cn\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Accept-Encoding: gzip, deflate\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"User-Agent: Mozilla/4.0 (compatible;
MSIE 6.0; Windows NT 5.1; SV1; CIBA; TheWorld)\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Host:blog.chinaunix.net\r\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Connection: Keep-Alive\r\n\r\n";<br><br>int connect_URL(char *domain,int port) <br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;int
sock;<br>&nbsp;&nbsp;&nbsp;&nbsp;struct hostent *
host; <br>&nbsp;&nbsp;&nbsp;&nbsp;struct sockaddr_in server; <br>&nbsp;&nbsp;&nbsp;&nbsp;host =
gethostbyname(domain); <br>&nbsp;&nbsp;&nbsp;&nbsp;if (host == NULL) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("gethostbyname error\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
-2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;// printf("HostName:
%s\n",host-&gt;h_name);<br><br>&nbsp;&nbsp;&nbsp;// printf("IP Address: %s\n",inet_ntoa(*((struct in_addr
*)host-&gt;h_addr)));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;sock = socket(AF_INET,SOCK_STREAM,0);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (sock
&lt; 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;{
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("invalid socket\n"); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;memset(&amp;server,0,sizeof(struct sockaddr_in));<br>&nbsp;&nbsp;&nbsp;&nbsp;memcpy(&amp;server.sin_addr,host-&gt;h_addr_list[0],host-&gt;h_length);<br>&nbsp;&nbsp;&nbsp;&nbsp;server.sin_family = AF_INET;<br>&nbsp;&nbsp;&nbsp;&nbsp;server.sin_port = htons(port);<br>&nbsp;&nbsp;&nbsp;&nbsp;return (connect(sock,(struct sockaddr *)&amp;server,sizeof(struct sockaddr)) &lt;0) ? -1 : sock;<br>}<br><br><br>int main()<br>{<br>&nbsp;&nbsp;int
sock;<br>&nbsp;&nbsp;char buf[100];<br>&nbsp;&nbsp;char *domain
= "blog.chinaunix.net";<br><br>&nbsp;&nbsp;<br>&nbsp;&nbsp;fp = fopen("test.txt","rb");<br>&nbsp;&nbsp;if(NULL == fp){<br>&nbsp;&nbsp;&nbsp;&nbsp;printf("can't open stockcode file!\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;return
-1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;sock
= connect_URL(domain,HTTPPORT);<br>&nbsp;&nbsp;&nbsp;&nbsp;if (sock
&lt;0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("connetc err\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;send(sock,head,strlen(head),0);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;while(1)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((recv(sock,buf,100,0))&lt;1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(fp,"%s",bufp); //save http data<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;fclose(fp);<br>&nbsp;&nbsp;&nbsp;&nbsp;close(sock);<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;printf("bye!\n");<br>&nbsp;&nbsp;return 0;<br>} </code></p>
<p>&nbsp;</p>
我这里是保存数据到本地硬盘 可以在这个的基础上修改,head头的定义可以自己使用wireshark抓包来看
<br><br>from:<br><a href="http://blog.chinaunix.net/u2/76292/showart.php?id=2123108">http://blog.chinaunix.net/u2/76292/showart.php?id=2123108</a>
<br><br> <img src ="http://www.cppblog.com/beautykingdom/aggbug/118838.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-06-27 23:13 <a href="http://www.cppblog.com/beautykingdom/archive/2010/06/27/118838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP的流量控制 </title><link>http://www.cppblog.com/beautykingdom/archive/2010/01/08/105213.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Fri, 08 Jan 2010 15:34:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2010/01/08/105213.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/105213.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2010/01/08/105213.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/105213.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/105213.html</trackback:ping><description><![CDATA[<div>1. 前言</div>
<div>&nbsp;</div>
<div>TCP是具备流控和可靠连接能力的协议，为防止TCP发生拥塞或为提高传输效率，在网<br>络发展早期就提出了一些相关的TCP流控和优化算法，而且也被RFC2581规定是每个<br>TCP实现时要实现的。</div>
<div>&nbsp;</div>
<div>本文中，为求方便把将&#8220;TCP分组段(segment)&#8221;都直接称为&#8220;包&#8221;。</div>
<div>&nbsp;</div>
<div>2. 慢启动(slow start)和拥塞避免(Congestion Avoidance)</div>
<div>&nbsp;</div>
<div>慢启动和拥塞避免是属于TCP发送方必须(MUST)要实现的，防止TCP发送方向网络传入大量的突发数据造成网络阻塞。</div>
<div><br>先介绍几个相关参数，是在通信双方中需要考虑但不在TCP包中体现的一些参数：</div>
<div><br><strong>拥塞窗口(congestion window，cwnd)，是指发送方在接收到对方的ACK确认前向允许网络发送的数据量，数据发送后，拥塞窗口缩小；接收到对方的ACK后，拥塞窗口相应增加，拥塞窗口越大，可发送的数据量越大。</strong>拥塞窗口初始值的RFC2581中被规定为不超过发送方MSS的两倍，而且不能超过两个TCP包，在RFC3390中更新了初始窗口大小的设置方法。</div>
<div><br><strong>通告窗口(advertised window，rwnd)，是指接收方所能接收的没来得及发ACK确认的数据量，接收方数据接收后，通告窗口缩小；发送ACK后，通告窗口相应扩大。</strong></div>
<div><br>慢启动阈值(slow start threshold, ssthresh)，用来判断是否要使用慢启动或拥塞避免算法来控制流量的一个参数，也是随通信过程不断变化的。</div>
<div><br>当cwnd &lt; ssthresh时，拥塞窗口值已经比较小了，表示未经确认的数据量增大，需要启动慢启动算法；当cwnd &gt; ssthresh时，可发送数据量大，需要启动拥塞避免算法。</div>
<div><br><strong>拥塞窗口cwnd是根据发送的数据量自动减小的，但扩大就需要根据对方的接收情况进行扩大，慢启动和拥塞避免算法都是描述如何扩大该值的。</strong></div>
<div><br>在启动慢启动算法时，TCP发送方接收到对方的ACK后拥塞窗口最多每次增加一个发送方MSS字节的数值，当拥塞窗口超过sshresh后或观察到拥塞才停止算法。</div>
<div><br>启动拥塞避免算法时，拥塞窗口在一个连接往返时间RTT内增加一个最大TCP包长度的量，一般实现时用以下公式计算：</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cwnd += max(SMSS*SMSS/cwnd, 1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; （2.1)</div>
<div>SMSS为发送方MSS。</div>
<div><br>TCP发送方检测到数据包丢失时，需要调整ssthresh，一般按下面公式计算：</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ssthresh = max (FlightSize / 2, 2*SMSS)&nbsp;&nbsp;&nbsp; (2.2)</div>
<div>其中FlightSize表示已经发送但还没有被确认的数据量。</div>
<div>&nbsp;</div>
<div>3. 快速重传(fast retransmit)和快速恢复(fast recovery)</div>
<div><br>TCP接收方收到错序的TCP包时要发送复制的ACK包回应，提示发送方可能出现网络丢包；发送方<br>收到连续3个重复的ACK包后启动快速重传算法，根据确认号快速重传那个可能丢失的包而不必等<br>重传定时器超时后再重传，普通的重传是要等到重传定时器超时还没收到ACK才进行的。这个算<br>法是TCP发送方应该(SHOULD)实现的，不是必须。TCP发送方进行了快速重传后进入快速恢复阶段<br>，直到没再接收重复的ACK包。</div>
<div><br>快速重传和快速恢复具体过程为：<br>1. 当收到第3个重复的ACK包时，ssthreh值按公式2.2重新设置；</div>
<div>2. 重传丢失的包后，将拥塞窗口cwnd设置为sshresh+3*SMSS，人工扩大了拥塞窗口；</div>
<div>3. 对于每个接收到的重复的ACK包，cwnd相应增加SMSS，扩大拥塞窗口；</div>
<div>4. 如果新的拥塞窗口cwnd值和接收方的通告窗口值允许的话，可以继续发新包；</div>
<div>5. 当收到下一个ACK确认了新数据时，将cwnd大小调整为sshresh，减少窗口；对接收方<br>&nbsp;&nbsp; 来说，接收到重发的TCP包后就要发此ACK确认当前接收的数据。</div>
<div>&nbsp;</div>
<div>4. 结论</div>
<div>这些算法重点在于保持网络的可靠性和可用性，防止网络阻塞造成的网络崩溃，是相对<br>比较保守的。</div>
<div><br>5. 附录讨论</div>
<div><br>A君: 这些算法都是针对通信双方的事, 但如果从开发防火墙等中间设备的角度来看, <br>&nbsp;&nbsp;&nbsp;&nbsp; 中间设备有必要考虑这些么?<br>端木: 这个...我好象也看不出必要性，因为算法的参数都是在双方内部而不在TCP数据包<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中体现...但应该会让中间设备轻松点，这个就象在马路开车，这些算法就是交规</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 让你开得规矩点，交警只关心你开车的情况，而不管你开的是什么车，开得好交警</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 也轻松。好车可以让你很容易开好，但差车也可以开好。</div>
<div><br>A君: 这些算法原型提出也很早了, 最早是88年的事, 当时网络都处于初级阶段, 有个</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; 9600bps的猫就很牛了, 计算机性能也很差, 因此实施这些算法还有点用; 但现</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; 在过了快20年了, 百兆都快淘汰, 千兆, 万兆网络都快普及了, 即使PC机的内存</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; 也都上G了,再规矩这种几K级别的数据量有意思么? 就好象现在喷气式战斗机都到</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; 第4代了, 再研究螺旋桨战斗机还有意思么?<br>端木: 这个...这个就象病毒库了, 里面不也有无数的DOS时代的病毒, 你以后这辈子估计<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 都见不着的，但没有哪个防病毒厂商会把这些病毒从库中剔除，库是只增不减的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有这么个东西也是一样，正因为平时没用，谁也不注意，知道了就可以吹一吹，</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 尤其拿去唬唬人是很有效的！</div>
<div><br>A君: 你真无聊!<br>端木: You got it! 不无聊干吗写博客啊! </div>
<div><br>端木: 搞技术有时候是很悲哀的一件事，必须牵扯七大姑八大姨的很多老东西，也就是向下<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兼容，到一定程度将成为进一步发展的最大障碍，讲一个从smth看到的不是笑话</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 的笑话：</div>
<div><br><em>&nbsp;&nbsp;&nbsp; 现代铁路的铁轨间距是4英尺8点5英寸，铁轨间距采用了电车轮距的标准，而电车轮距<br>的标准则沿袭了马车的轮距标准。 <br>&nbsp;&nbsp;&nbsp; 马车的轮距为何是4英尺8点5英寸？原来，英国的马路辙迹的宽度是4英尺8点5英寸。<br>如果马车改用其他尺寸的轮距，轮子很快就会在英国的老马路上撞坏。 <br>&nbsp;&nbsp;&nbsp; 英国马路的辙迹宽度又从何而来？这可以上溯到古罗马时期。整个欧洲(包括英国)的老路都是罗马人为其军队铺设的，4英尺8点5英寸正是罗马战车的宽度。 <br>&nbsp;&nbsp;&nbsp; 罗马战车的宽度又是怎么来的？答案很简单，它是牵引一辆战车的两匹马的屁股的总宽度。 <br>&nbsp;&nbsp;&nbsp; 段子到这里还没有结束。美国航天飞机的火箭助推器也摆脱不了马屁股的纠缠———火箭助推器造好之后要经过铁路运送，而铁路上必然有一些隧道，隧道的宽度又是根据铁轨的宽度而来。代表着尖端科技的火箭助推器的宽度，竟然被两匹马的屁股的总宽度决定了。<br>转自：<br>http://www.cppblog.com/prayer/archive/2009/04/20/80527.html<br></em></div><img src ="http://www.cppblog.com/beautykingdom/aggbug/105213.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-08 23:34 <a href="http://www.cppblog.com/beautykingdom/archive/2010/01/08/105213.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>流量控制和拥塞控制</title><link>http://www.cppblog.com/beautykingdom/archive/2009/12/30/104460.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 30 Dec 2009 08:54:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/12/30/104460.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/104460.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/12/30/104460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/104460.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/104460.html</trackback:ping><description><![CDATA[拥塞（Congestion）指的是在包交换网络中由于传送的包数目太多，而存贮转发节点的资源有限而造成网络传输性能下降的情况。拥塞的一种极端情况是死锁（Deadlock），退出死锁往往需要网络复位操作。 <br>流量控制（Flow Control）指的是在一条通道上控制发送端发送数据的数量及速度使其不超过接收端所能承受的能力，这个能力主要指接收端接收数据的速率及接收数据缓冲区的大小。通常采用停等法或滑动窗口法控制流量。 <br>流量控制是针对端系统中资源受限而设置的；拥塞控制是针对中间节点资源受限而设置的。<br><img src ="http://www.cppblog.com/beautykingdom/aggbug/104460.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-30 16:54 <a href="http://www.cppblog.com/beautykingdom/archive/2009/12/30/104460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用wget下载文件或目录或者是整个网站</title><link>http://www.cppblog.com/beautykingdom/archive/2009/12/22/103663.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 21 Dec 2009 17:04:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/12/22/103663.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/103663.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/12/22/103663.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/103663.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/103663.html</trackback:ping><description><![CDATA[wget -m -nH -b -q -P /home/web http://domain
<br>具体参数的含义还没有man，等man过之后再添加进来哈<br><br><img src ="http://www.cppblog.com/beautykingdom/aggbug/103663.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-22 01:04 <a href="http://www.cppblog.com/beautykingdom/archive/2009/12/22/103663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>http请求的详细过程---理解计算机网络&lt;转&gt;</title><link>http://www.cppblog.com/beautykingdom/archive/2009/10/21/99142.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Wed, 21 Oct 2009 15:05:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/10/21/99142.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/99142.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/10/21/99142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/99142.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/99142.html</trackback:ping><description><![CDATA[<span style="FONT-FAMILY: '微软雅黑','黑体',Arial,Helvetica,Sans-Serif">&nbsp;
<p><strong><font size=3>一个http请求的详细过程</font></strong></p>
<p><font size=3>我们来看当我们在浏览器输入</font><a href="http://www.mycompany.com:8080/mydir/index.html"><font color=#189a0f size=3><u>http://www.mycompany.com:8080/mydir/index.html</u></font></a><font size=3>,幕后所发生的一切。</font></p>
<p><font size=3>首先http是一个应用层的协议，在这个层的协议，只是一种通讯规范，也就是因为双方要进行通讯，大家要事先约定一个规范。</font></p>
<p><font size=3>1.连接 当我们输入这样一个请求时，首先要建立一个socket连接，因为socket是通过ip和端口建立的，所以之前还有一个DNS解析过程，把</font><a href="http://www.mycompany.com/"><font color=#189a0f size=3><u>www.mycompany.com</u></font></a><font size=3>变成ip，如果url里不包含端口号，则会使用该协议的默认端口号。</font></p>
<p><font size=3>DNS的过程是这样的：首先我们知道我们本地的机器上在配置网络时都会填写DNS，这样本机就会把这个url发给这个配置的DNS服务器，如果能够找到相应的url则返回其ip，否则该DNS将继续将该解析请求发送给上级DNS，整个DNS可以看做是一个树状结构，该请求将一直发送到根直到得到结果。现在已经拥有了目标ip和端口号，这样我们就可以打开socket连接了。</font></p>
<p><font size=3>2.请求 连接成功建立后，开始向web服务器发送请求，这个请求一般是GET或POST命令（POST用于FORM参数的传递）。GET命令的格式为：　　GET 路径/文件名 HTTP/1.0<br>文件名指出所访问的文件，HTTP/1.0指出Web浏览器使用的HTTP版本。现在可以发送GET命令：</font></p>
<p><font size=3>GET /mydir/index.html HTTP/1.0，</font></p>
<p><font size=3>3.应答 web服务器收到这个请求，进行处理。从它的文档空间中搜索子目录mydir的文件index.html。如果找到该文件，Web服务器把该文件内容传送给相应的Web浏览器。</font></p>
<p><font size=3>为了告知浏览器，，Web服务器首先传送一些HTTP头信息，然后传送具体内容（即HTTP体信息），HTTP头信息和HTTP体信息之间用一个空行分开。<br>常用的HTTP头信息有：<br>　　① HTTP 1.0 200 OK 　这是Web服务器应答的第一行，列出服务器正在运行的HTTP版本号和应答代码。代码"200 OK"表示请求完成。<br>　　② MIME_Version:1.0　它指示MIME类型的版本。<br>　　③ content_type:类型　这个头信息非常重要，它指示HTTP体信息的MIME类型。如：content_type:text/html指示传送的数据是HTML文档。<br>　　④ content_length:长度值　它指示HTTP体信息的长度（字节）。</font></p>
<p><br><font size=3>4.关闭连接：当应答结束后，Web浏览器与Web服务器必须断开，以保证其它Web浏览器能够与Web服务器建立连接。</font></p>
<p><br><strong><font size=3>下面我们具体分析其中的数据包在网络中漫游的经历</font></strong></p>
<p><font size=3>在网络分层结构中，各层之间是严格单向依赖的。&#8220;服务&#8221;是描述各层之间关系的抽象概念，即网络中各层向紧邻上层提供的一组操作。下层是服务提供者，上层是请求服务的用户。服务的表现形式是原语（primitive），如系统调用或库函数。系统调用是操作系统内核向网络应用程序或高层协议提供的服务原语。网络中的n层总要向n+1层提供比n-1层更完备的服务，否则n层就没有存在的价值。 </font></p>
<p><font size=3>传输层实现的是&#8220;端到端&#8221;通信，引进网间进程通信概念，同时也要解决差错控制，流量控制，数据排序（报文排序），连接管理等问题，为此提供不同的服务方式。通常传输层的服务通过系统调用的方式提供，以socket的方式。对于客户端，要想建立一个socket连接，需要调用这样一些函数socket() bind() connect(),然后就可以通过send()进行数据发送。</font></p>
<p><font color=#008000 size=3><strong>现在看数据包在网络中的穿行过程：</strong></font></p>
<p><font color=#008000 size=3>应用层</font></p>
<p><font size=3>首先我们可以看到在应用层，根据当前的需求和动作，结合应用层的协议，有我们确定发送的数据内容，我们把这些数据放到一个缓冲区内，然后形成了应用层的报文<strong>da<wbr>ta</strong>。</font></p>
<p><font color=#008000 size=3>传输层</font></p>
<p><font size=3>这些数据通过传输层发送，比如tcp协议。所以它们会被送到传输层处理，在这里报文打上了传输头的包头，主要包含端口号，以及tcp的各种制信息，这些信息是直接得到的，因为接口中需要指定端口。这样就组成了tcp的数据传送单位<strong>segment</strong>。tcp是一种端到端的协议，利用这些信息，比如tcp首部中的序号确认序号，根据这些数字，发送的一方不断的进行发送等待确认，发送一个数据段后，会开启一个计数器，只有当收到确认后才会发送下一个，如果超过计数时间仍未收到确认则进行重发，在接受端如果收到错误数据，则将其丢弃，这将导致发送端超时重发。通过tcp协议，控制了数据包的发送序列的产生，不断的调整发送序列，实现流控和数据完整。</font></p>
<p><font color=#008000 size=3>网络层</font></p>
<p><font size=3>然后待发送的数据段送到网络层，在网络层被打包，这样封装上了网络层的包头，包头内部含有源及目的的ip地址，该层数据发送单位被称为<strong>packet</strong>。网络层开始负责将这样的数据包在网络上传输，如何穿过路由器，最终到达目的地址。在这里，根据目的ip地址，就需要查找下一跳路由的地址。首先在本机，要查找本机的路由表，在windows上运行route print就可以看到当前路由表内容，有如下几项：<br>Active Routes Default Route Persistent Route.</font></p>
<p><font size=3>整个查找过程是这样的:<br>(1)根据目的地址，得到目的网络号，如果处在同一个内网，则可以直接发送。<br>(2)如果不是，则查询路由表，找到一个路由。<br>(3)如果找不到明确的路由，此时在路由表中还会有默认网关，也可称为缺省网关，IP用缺省的网关地址将一个数据传送给下一个指定的路由器，所以网关也可能是路由器，也可能只是内网向特定路由器传输数据的网关。<br>(4)路由器收到数据后，它再次为远程主机或网络查询路由，若还未找到路由，该数据包将发送到该路由器的缺省网关地址。而数据包中包含一个最大路由跳数，如果超过这个跳数，就会丢弃数据包，这样可以防止无限传递。路由器收到数据包后，只会查看网络层的包裹数据，目的ip。所以说它是工作在网络层，传输层的数据对它来说则是透明的。</font></p>
<p><font size=3>如果上面这些步骤都没有成功，那么该数据报就不能被传送。如果不能传送的数据报来自本机，那么一般会向生成数据报的应用程序返回一个&#8220;主机不可达&#8221;或 &#8220;网络不可达&#8221;的错误。</font></p>
<p><font size=3></font>&nbsp;</p>
<p><font color=#339966 size=3>以windows下主机的路由表为例，看路由的查找过程<br>======================================================================<br>Active Routes:<br>Network Destination&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Netmask&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Gateway&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Interface&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Metric<br>0.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10<br>127.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;255.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br>192.168.1.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 255.255.255.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10<br>192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;255.255.255.255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 127.0.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;10<br>192.168.1.255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;255.255.255.255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10<br>&nbsp;224.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;240.0.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10<br>255.255.255.255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;255.255.255.255&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1<br>Default Gateway:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.2</font></p>
<p><font color=#339966 size=3>Network Destination 目的网段&nbsp; <br>Netmask 子网掩码&nbsp; <br>Gateway 下一跳路由器入口的ip，路由器通过interface和gateway定义一调到下一个路由器的链路，通常情况下，interface和gateway是同一网段的。<br>Interface 到达该目的地的本路由器的出口ip（对于我们的个人pc来说，通常由机算机A的网卡，用该网卡的IP地址标识，当然一个pc也可以有多个网卡）。</font></p>
<p><font color=#339966 size=3>网关这个概念，主要用于不同子网间的交互，当两个子网内主机A,B要进行通讯时，首先A要将数据发送到它的本地网关，然后网关再将数据发送给B所在的网关，然后网关再发送给B。<br>默认网关，当一个数据包的目的网段不在你的路由记录中，那么，你的路由器该把那个数据包发送到哪里！缺省路由的网关是由你的连接上的default gateway决定的，也就是我们通常在网络连接里配置的那个值。</font></p>
<p><font color=#339966 size=3>通常interface和gateway处在一个子网内，对于路由器来说，因为可能具有不同的interface,当数据包到达时，根据Network Destination寻找匹配的条目，如果找到，interface则指明了应当从该路由器的那个接口出去，gateway则代表了那个子网的网关地址。</font></p>
<p><font color=#339966 size=3>第一条&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0.0.0&nbsp;&nbsp; 0.0.0.0&nbsp;&nbsp; 192.168.1.2&nbsp;&nbsp;&nbsp; 192.168.1.101&nbsp;&nbsp; 10<br>0.0.0.0代表了缺省路由。该路由记录的意思是：当我接收到一个数据包的目的网段不在我的路由记录中，我会将该数据包通过192.168.1.101这个接口发送到192.168.1.2这个地址，这个地址是下一个路由器的一个接口，这样这个数据包就可以交付给下一个路由器处理，与我无关。该路由记录的线路质量 10。当有多个条目匹配时，会选择具有较小Metric值的那个。</font></p>
<p><font color=#339966 size=3>第三条&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 192.168.1.0&nbsp;&nbsp; 255.255.255.0&nbsp; 192.168.1.101&nbsp;&nbsp; 192.168.1.101&nbsp; 10<br>直联网段的路由记录：当路由器收到发往直联网段的数据包时该如何处理，这种情况，路由记录的interface和gateway是同一个。当我接收到一个数据包的目的网段是192.168.1.0时，我会将该数据包通过192.168.1.101这个接口直接发送出去，因为这个端口直接连接着192.168.1.0这个网段，该路由记录的线路质量 10&nbsp;（因interface和gateway是同一个，表示数据包直接传送给目的地址，不需要再转给路由器）。</font></p>
<p><font color=#339966 size=3>一般就分这两种情况，目的地址与当前路由器接口是否在同一子网。如果是则直接发送，不需再转给路由器，否则还需要转发给下一个路由器继续进行处理。</font></p>
<p><font size=3></font>&nbsp;</p>
<p><font size=3>查找到下一跳ip地址后，还需要知道它的mac地址，这个地址要作为链路层数据装进链路层头部。这时需要arp协议，具体过程是这样的，查找arp缓冲，windows下运行arp -a可以查看当前arp缓冲内容。如果里面含有对应ip的mac地址，则直接返回。否则需要发生arp请求，该请求包含源的ip和mac地址，还有目的地的ip地址，在网内进行广播，所有的主机会检查自己的ip与该请求中的目的ip是否一样，如果刚好对应则返回自己的mac地址，同时将请求者的ip mac保存。这样就得到了目标ip的mac地址。</font></p>
<p><font color=#008000 size=3>链路层</font></p>
<p><font size=3>将mac地址及链路层控制信息加到数据包里，形成<strong>Frame</strong>，Frame在链路层协议下，完成了相邻的节点间的数据传输，完成连接建立，控制传输速度，数据完整。</font></p>
<p><font color=#008000 size=3>物理层</font></p>
<p><font size=3>物理线路则只负责该数据以<strong>bit</strong>为单位从主机传输到下一个目的地。</font></p>
<p><font size=3>下一个目的地接受到数据后，从物理层得到数据然后经过逐层的解包 到 链路层 到 网络层，然后开始上述的处理，在经网络层 链路层 物理层将数据封装好继续传往下一个地址。</font></p>
<p><font size=3>在上面的过程中，可以看到有一个路由表查询过程，而这个路由表的建立则依赖于路由算法。也就是说路由算法实际上只是用来路由器之间更新维护路由表，真正的数据传输过程并不执行这个算法，只查看路由表。这个概念也很重要，需要理解常用的路由算法。而整个tcp协议比较复杂，跟链路层的协议有些相似，其中有很重要的一些机制或者概念需要认真理解，比如编号与确认，流量控制，重发机制，发送接受窗口。</font></p>
<p><font size=3></font>&nbsp;</p>
<p><strong><font size=3>tcp/ip基本模型及概念</font></strong></p>
<p><br><strong><font size=3>物理层</font></strong></p>
<p><font size=3>设备，中继器（repeater）,集线器（hub）。对于这一层来说，从一个端口收到数据，会转发到所有端口。</font></p>
<p><br><strong><font size=3>链路层</font></strong></p>
<p><font size=3>协议：SDLC（Synchronous Da<wbr>ta Link Control）HDLC（High-level Da<wbr>ta Link Control） ppp协议独立的链路设备中最常见的当属网卡，网桥也是链路产品。集线器MODEM的某些功能有人认为属于链路层，对此还有些争议认为属于物理层设备。除此之外，所有的交换机都需要工作在数据链路层，但仅工作在数据链路层的仅是二层交换机。其他像三层交换机、四层交换机和七层交换机虽然可对应工作在OSI的三层、四层和七层，但二层功能仍是它们基本的功能。</font></p>
<p><font size=3>因为有了MAC地址表，所以才充分避免了冲突，因为交换机通过目的MAC地址知道应该把这个数据转发到哪个端口。而不会像HUB一样，会转发到所有滴端口。所以，交换机是可以划分冲突域滴。</font></p>
<p><br><strong><font size=3>网络层</font></strong></p>
<p><font size=3>四个主要的协议:&nbsp;&nbsp; <br>网际协议IP：负责在主机和网络之间寻址和路由数据包。&nbsp;&nbsp;&nbsp;&nbsp; <br>地址解析协议ARP：获得同一物理网络中的硬件主机地址。&nbsp;&nbsp;&nbsp;&nbsp; <br>网际控制消息协议ICMP：发送消息，并报告有关数据包的传送错误。&nbsp;&nbsp;&nbsp;&nbsp; <br>互联组管理协议IGMP：被IP主机拿来向本地多路广播路由器报告主机组成员。</font></p>
<p><font size=3>该层设备有三层交换机，路由器。</font></p>
<p><br><strong><font size=3>传输层</font></strong></p>
<p><font size=3>两个重要协议 TCP 和 UDP 。</font></p>
<p><font size=3>端口概念：TCP/UDP 使用 IP 地址标识网上主机，使用端口号来标识应用进程，即 TCP/UDP 用主机 IP 地址和为应用进程分配的端口号来标识应用进程。端口号是 16 位的无符号整数， TCP 的端口号和 UDP 的端口号是两个独立的序列。尽管相互独立，如果 TCP 和 UDP 同时提供某种知名服务，两个协议通常选择相同的端口号。这纯粹是为了使用方便，而不是协议本身的要求。利用端口号，一台主机上多个进程可以同时使用 TCP/UDP 提供的传输服务，并且这种通信是端到端的，它的数据由 IP 传递，但与 IP 数据报的传递路径无关。网络通信中用一个三元组可以在全局唯一标志一个应用进程：（协议，本地地址，本地端口号）。</font></p>
<p><font size=3>也就是说tcp和udp可以使用相同的端口。</font></p>
<p><font size=3>可以看到通过(协议,源端口，源ip，目的端口，目的ip)就可以用来完全标识一组网络连接。</font></p>
<p><strong><font size=3>应用层</font></strong></p>
<p><font size=3>基于tcp：Telnet FTP SMTP DNS HTTP <br>基于udp：RIP NTP（网落时间协议）和DNS （DNS也使用TCP）SNMP TFTP</font></p>
<p><font size=3></font>&nbsp;</p>
<p><font size=3>参考文献：</font></p>
<p><font size=3>读懂本机路由表 </font><a href="http://hi.baidu.com/thusness/blog/item/9c18e5bf33725f0818d81f52.html"><font color=#189a0f size=3><u>http://hi.baidu.com/thusness/blog/item/9c18e5bf33725f0818d81f52.html</u></font></a></p>
<p><font size=3>Internet 传输层协议 </font><a href="http://www.cic.tsinghua.edu.cn/jdx/book6/3.htm"><font color=#189a0f size=3><u>http://www.cic.tsinghua.edu.cn/jdx/book6/3.htm</u></font></a><font size=3>&nbsp;计算机网络 谢希仁</font></p>
<br>转自：<br><a href="http://blog.chinaunix.net/u2/67780/showart_2065190.html">http://blog.chinaunix.net/u2/67780/showart_2065190.html</a></span>
<img src ="http://www.cppblog.com/beautykingdom/aggbug/99142.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-10-21 23:05 <a href="http://www.cppblog.com/beautykingdom/archive/2009/10/21/99142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP三次握手/四次挥手详解&lt;转&gt;</title><link>http://www.cppblog.com/beautykingdom/archive/2009/10/20/99062.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 20 Oct 2009 13:15:00 GMT</pubDate><guid>http://www.cppblog.com/beautykingdom/archive/2009/10/20/99062.html</guid><wfw:comment>http://www.cppblog.com/beautykingdom/comments/99062.html</wfw:comment><comments>http://www.cppblog.com/beautykingdom/archive/2009/10/20/99062.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/beautykingdom/comments/commentRss/99062.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/beautykingdom/services/trackbacks/99062.html</trackback:ping><description><![CDATA[
<span style="border-collapse: collapse; font-family: song, Verdana; font-size: 12px; "><font size="3">1</font><div style="font-size: 10pt; position: relative; "><wbr><font size="3">、建立连接协议（三次握手）</font><wbr><font size="3"><br style="font: normal normal normal 12px/normal song, Verdana; ">（1）客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。<br style="font: normal normal normal 12px/normal song, Verdana; ">（2） 服务器端回应客户端的，这是三次握手中的第2个报文，这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应；同时又标志SYN给客户端，询问客户端是否准备好进行数据通讯。<br style="font: normal normal normal 12px/normal song, Verdana; ">（3） 客户必须再次回应服务段一个ACK报文，这是报文段3。<br style="font: normal normal normal 12px/normal song, Verdana; ">2</font><wbr><font size="3">、连接终止协议（四次挥手）</font><wbr><font size="3"><br style="font: normal normal normal 12px/normal song, Verdana; ">　 　由于TCP连接是全双工的，因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动，一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭，而另一方执行被动关闭。<br style="font: normal normal normal 12px/normal song, Verdana; ">　（1） TCP客户端发送一个FIN，用来关闭客户到服务器的数据传送（报文段4）。<br style="font: normal normal normal 12px/normal song, Verdana; ">　（2） 服务器收到这个FIN，它发回一个ACK，确认序号为收到的序号加1（报文段5）。和SYN一样，一个FIN将占用一个序号。<br style="font: normal normal normal 12px/normal song, Verdana; ">　（3） 服务器关闭客户端的连接，发送一个FIN给客户端（报文段6）。<br style="font: normal normal normal 12px/normal song, Verdana; ">　（4） 客户段发回ACK报文确认，并将确认序号设置为收到序号加1（报文段7）。<br style="font: normal normal normal 12px/normal song, Verdana; ">CLOSED: 这个没什么好说的了，表示初始状态。<br style="font: normal normal normal 12px/normal song, Verdana; ">LISTEN: 这个也是非常容易理解的一个状态，表示服务器端的某个SOCKET处于监听状态，可以接受连接了。<br style="font: normal normal normal 12px/normal song, Verdana; ">SYN_RCVD: 这个状态表示接受到了SYN报文，在正常情况下，这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态，很短暂，基本 上用netstat你是很难看到这种状态的，除非你特意写了一个客户端测试程序，故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时，当收到客户端的ACK报文后，它会进入到ESTABLISHED状态。<br style="font: normal normal normal 12px/normal song, Verdana; ">SYN_SENT: 这个状态与SYN_RCVD遥想呼应，当客户端SOCKET执行CONNECT连接时，它首先发送SYN报文，因此也随即它会进入到了SYN_SENT状 态，并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。<br style="font: normal normal normal 12px/normal song, Verdana; ">ESTABLISHED：这个容易理解了，表示连接已经建立了。<br style="font: normal normal normal 12px/normal song, Verdana; ">FIN_WAIT_1: 这个状态要好好解释一下，其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别 是：FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时，它想主动关闭连接，向对方发送了FIN报文，此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后，则进入到FIN_WAIT_2状态，当然在实际的正常情况下，无论对方何种情况下，都应该马 上回应ACK报文，所以FIN_WAIT_1状态一般是比较难见到的，而FIN_WAIT_2状态还有时常常可以用netstat看到。<br style="font: normal normal normal 12px/normal song, Verdana; ">FIN_WAIT_2：上面已经详细解释了这种状态，实际上FIN_WAIT_2状态下的SOCKET，表示半连接，也即有一方要求close连接，但另外还告诉对方，我暂时还有点数据需要传送给你，稍后再关闭连接。<br style="font: normal normal normal 12px/normal song, Verdana; ">TIME_WAIT: 表示收到了对方的FIN报文，并发送出了ACK报文，就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下，收到了对方同时带 FIN标志和ACK标志的报文时，可以直接进入到TIME_WAIT状态，而无须经过FIN_WAIT_2状态。<br style="font: normal normal normal 12px/normal song, Verdana; ">CLOSING: 这种状态比较特殊，实际情况中应该是很少见，属于一种比较罕见的例外状态。正常情况下，当你发送FIN报文后，按理来说是应该先收到（或同时收到）对方的 ACK报文，再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后，并没有收到对方的ACK报文，反而却也收到了对方的FIN报文。什 么情况下会出现此种情况呢？其实细想一下，也不难得出结论：那就是如果双方几乎在同时close一个SOCKET的话，那么就出现了双方同时发送FIN报 文的情况，也即会出现CLOSING状态，表示双方都正在关闭SOCKET连接。<br style="font: normal normal normal 12px/normal song, Verdana; ">CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢？当对方close一个SOCKET后发送FIN报文给自己，你系统毫无疑问地会回应一个ACK报文给对 方，此时则进入到CLOSE_WAIT状态。接下来呢，实际上你真正需要考虑的事情是察看你是否还有数据发送给对方，如果没有的话，那么你也就可以 close这个SOCKET，发送FIN报文给对方，也即关闭连接。所以你在CLOSE_WAIT状态下，需要完成的事情是等待你去关闭连接。<br style="font: normal normal normal 12px/normal song, Verdana; ">LAST_ACK: 这个状态还是比较容易好理解的，它是被动关闭一方在发送FIN报文后，最后等待对方的ACK报文。当收到ACK报文后，也即可以进入到CLOSED可用状态了。<br style="font: normal normal normal 12px/normal song, Verdana; ">最后有2个问题的回答，我自己分析后的结论（不一定保证100%正确）<br style="font: normal normal normal 12px/normal song, Verdana; ">1、 为什么建立连接协议是三次握手，而关闭连接却是四次握手呢？<br style="font: normal normal normal 12px/normal song, Verdana; ">这 是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后，它可以把ACK和SYN（ACK起应答作用，而SYN起同步作用）放在一 个报文里来发送。但关闭连接时，当收到对方的FIN报文通知时，它仅仅表示对方没有数据发送给你了；但未必你所有的数据都全部发送给对方了，所以你可以未 必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后，再发送FIN报文给对方来表示你同意现在可以关闭连接了，所以它这里的ACK报文 和FIN报文多数情况下都是分开发送的。<br style="font: normal normal normal 12px/normal song, Verdana; ">2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态？<br style="font: normal normal normal 12px/normal song, Verdana; ">这是因为： 虽然双方都同意关闭连接了，而且握手的4个报文也都协调和发送完毕，按理可以直接回到CLOSED状态（就好比从SYN_SEND状态到 ESTABLISH状态那样）；但是因为我们必须要假想网络是不可靠的，你无法保证你最后发送的ACK报文会一定被对方收到，因此对方处于 LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文，而重发FIN报文，所以这个TIME_WAIT状态的作用就是用来重发可能丢失的 ACK报文。</font></div><div style="font-size: 10pt; position: relative; "><span  style="font-size: medium;">转自：</span></div><div style="font-size: 10pt; position: relative; "><span  style="font-size: medium;"><a href="http://blog.chinaunix.net/u2/67780/showart.php?id=2071265">http://blog.chinaunix.net/u2/67780/showart.php?id=2071265</a></span></div></span><img src ="http://www.cppblog.com/beautykingdom/aggbug/99062.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-10-20 21:15 <a href="http://www.cppblog.com/beautykingdom/archive/2009/10/20/99062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>