﻿<?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++博客-牵着老婆满街逛-随笔分类-网络编程</title><link>http://www.cppblog.com/tx7do/category/1517.html</link><description>危机感是一种强大前进的动力！&lt;/br&gt;
GMail/GTalk: yanglinbo#google.com;&lt;/br&gt;
MSN/Email: tx7do#yahoo.com.cn;&lt;/br&gt;
QQ: 3 0 3 3 9 6 9 2 0 .</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:16:26 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:16:26 GMT</pubDate><ttl>60</ttl><item><title>AIX上的aio(转贴) </title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9530.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 04:04:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9530.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9530.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9530.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9530.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9530.html</trackback:ping><description><![CDATA[
		<p>
				<a href="http://www.loveunix.net/discuz/viewthread.php?tid=51746&amp;extra=page%3D1">
						<font color="#000080">http://www.loveunix.net/discuz/viewthread.php?tid=51746&amp;extra=page%3D1</font>
				</a>
				<br />
		</p>
		<p>
		</p>
		<table style="TABLE-LAYOUT: fixed; WORD-WRAP: break-word" height="100%" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td valign="top">
										<span style="FONT-SIZE: 13px">
												<b>Asynchronous I/O Subsystem</b>
												<br />
												<br />Synchronous I/O occurs while you wait. Applications processing cannot continue until the I/O operation is complete.<br /><br />In contrast, asynchronous I/O operations run in the background and do not block user applications. This improves performance, because I/O operations and applications processing can run simultaneously.<br /><br />Using asynchronous I/O will usually improve your I/O throughput, especially when you are storing data in raw logical volumes (as opposed to Journaled file systems). The actual performance, however, depends on how many server processes are running that will handle the I/O requests.<br /><br />Many applications, such as databases and file servers, take advantage of the ability to overlap processing and I/O. These asynchronous I/O operations use various kinds of devices and files. Additionally, multiple asynchronous I/O operations can run at the same time on one or more devices or files.<br /><br />Each asynchronous I/O request has a corresponding control block in the application's address space. When an asynchronous I/O request is made, a handle is established in the control block. This handle is used to retrieve the status and the return values of the request.<br /><br />Applications use the aio_read and aio_write subroutines to perform the I/O. Control returns to the application from the subroutine, as soon as the request has been queued. The application can then continue processing while the disk operation is being performed.<br /><br />A kernel process (kproc), called a server, is in charge of each request from the time it is taken off the queue until it completes. The number of servers limits the number of disk I/O operations that can be in progress in the system simultaneously.<br /><br />The default values are minservers=1 and maxservers=10. In systems that seldom run applications that use asynchronous I/O, this is usually adequate. For environments with many disk drives and key applications that use asynchronous I/O, the default is far too low. The result of a deficiency of servers is that disk I/O seems much slower than it should be. Not only do requests spend inordinate lengths of time in the queue, but the low ratio of servers to disk drives means that the seek-optimization algorithms have too few requests to work with for each drive.<br /><br /><b>Note:</b><br />Asynchronous I/O will not work if the control block or buffer is created using mmap (mapping segments).<br /><br />In AIX 5.2 there are two Asynchronous I/O Subsystems. The original AIX AIO, now called LEGACY AIO, has the same function names as the posix compliant POSIX AIO. The major differences between the two involve different parameter passing. Both subsytems are defined in the /usr/include/sys/aio.h file. The _AIO_AIX_SOURCE macro is used to distinguish between the two versions.<br /><br /><b>Note:</b><br />The _AIO_AIX_SOURCE macro used in the /usr/include/sys/aio.h file must be defined when using this file to compile an aio application with the LEGACY AIO function definitions. The default compile using the aio.h file is for an application with the new POSIX AIO definitions. To use the LEGACY AIO function defintions do the following in the source file: <br />#define _AIO_AIX_SOURCE <br />#include &lt;sys/aio.h&gt;<br />or when compiling on the command line, type the following: <br />xlc ... -D_AIO_AIX_SOURCE ... classic_aio_program.c<br /><br />For each aio function there is a legacy and a posix definition. LEGACY AIO has an additional aio_nwait function, which although not a part of posix definitions has been included in POSIX AIO to help those who want to port from LEGACY to POSIX definitions. POSIX AIO has an additional aio_fsync function, which is not included in LEGACY AIO. For a list of these functions, see Asynchronous I/O Subroutines.<br /><br /><b>How Do I Know if I Need to Use AIO?</b><br /><br />Using the vmstat command with an interval and count value, you can determine if the CPU is idle waiting for disk I/O. The wa column details the percentage of time the CPU was idle with pending local disk I/O.<br /><br />If there is at least one outstanding I/O to a local disk when the wait process is running, the time is classified as waiting for I/O. Unless asynchronous I/O is being used by the process, an I/O request to disk causes the calling process to block (or sleep) until the request has been completed. Once a process's I/O request completes, it is placed on the run queue.<br /><br />A wa value consistently over 25 percent may indicate that the disk subsystem is not balanced properly, or it may be the result of a disk-intensive workload.<br /><br /><b>Note:</b><br />AIO will not relieve an overly busy disk drive. Using the iostat command with an interval and count value, you can determine if any disks are overly busy. Monitor the %tm_act column for each disk drive on the system. On some systems, a %tm_act of 35.0 or higher for one disk can cause noticeably slower performance. The relief for this case could be to move data from more busy to less busy disks, but simply having AIO will not relieve an overly busy disk problem.<br /><br /><b>SMP Systems</b><br />For SMP systems, the us, sy, id and wa columns are only averages over all processors. But keep in mind that the I/O wait statistic per processor is not really a processor-specific statistic; it is a global statistic. An I/O wait is distinguished from idle time only by the state of a pending I/O. If there is any pending disk I/O, and the processor is not busy, then it is an I/O wait time. Disk I/O is not tracked by processors, so when there is any I/O wait, all processors get charged (assuming they are all equally idle).<br /><br /><b>How Many AIO Servers Am I Currently Using?</b><br />To determine you how many Posix AIO Servers (aios) are currently running, type the following on the command line:<br />pstat -a | grep posix_aioserver | wc -l <br /><br /><b>Note:</b><br />You must run this command as the root user.<br />To determine you how many Legacy AIO Servers (aios) are currently running, type the following on the command line:<br />pstat -a | egrep ' aioserver' | wc -l  <br />Note:<br />You must run this command as the root user.<br />If the disk drives that are being accessed asynchronously are using either the Journaled File System (JFS) or the Enhanced Journaled File System (JFS2), all I/O will be routed through the aios kprocs.<br />If the disk drives that are being accessed asynchronously are using a form of raw logical volume management, then the disk I/O is not routed through the aios kprocs. In that case the number of servers running is not relevant.<br /><br />However, if you want to confirm that an application that uses raw logic volumes is taking advantage of AIO, you can disable the fast path option via SMIT. When this option is disabled, even raw I/O will be forced through the aios kprocs. At that point, the pstat command listed in preceding discussion will work. You would not want to run the system with this option disabled for any length of time. This is simply a suggestion to confirm that the application is working with AIO and raw logical volumes.<br />At releases earlier than AIX 4.3, the fast path is enabled by default and cannot be disabled.<br />How Many AIO Servers Do I Need?<br />Here are some suggested rules of thumb for determining what value to set maximum number of servers to:<br />1.        The first rule of thumb suggests that you limit the maximum number of servers to a number equal to ten times the number of disks that are to be used concurrently, but not more than 80. The minimum number of servers should be set to half of this maximum number. <br />2.        Another rule of thumb is to set the maximum number of servers to 80 and leave the minimum number of servers set to the default of 1 and reboot. Monitor the number of additional servers started throughout the course of normal workload. After a 24-hour period of normal activity, set the maximum number of servers to the number of currently running aios + 10, and set the minimum number of servers to the number of currently running aios - 10. <br />In some environments you may see more than 80 aios KPROCs running. If so, consider the third rule of thumb.<br />3.        A third suggestion is to take statistics using vmstat -s before any high I/O activity begins, and again at the end. Check the field iodone. From this you can determine how many physical I/Os are being handled in a given wall clock period. Then increase the maximum number of servers and see if you can get more iodones in the same time period.<br />Prerequisites<br />To make use of asynchronous I/O the following fileset must be installed:<br />bos.rte.aio<br />To determine if this fileset is installed, use:<br />lslpp -l bos.rte.aio<br />You must also make the aio0 or posix_aio0 device available using SMIT.<br />smit chgaio<br />smit chgposixaio<br /><br />STATE to be configured at system restart available<br />or<br />smit aio<br />smit posixaio<br /><br />Configure aio now<br />Functions of Asynchronous I/O<br />Functions provided by the asynchronous I/O facilities are:<br />•        Large File-Enabled Asynchronous I/O <br />•        Nonblocking I/O <br />•        Notification of I/O completion <br />•        Cancellation of I/O requests<br />Large File-Enabled Asynchronous I/O<br />The fundamental data structure associated with all asynchronous I/O operations is struct aiocb. Within this structure is the aio_offset field which is used to specify the offset for an I/O operation.<br />Due to the signed 32-bit definition of aio_offset, the default asynchronous I/O interfaces are limited to an offset of 2G minus 1. To overcome this limitation, a new aio control block with a signed 64-bit offset field and a new set of asynchronous I/O interfaces has been defined. These 64–bit definitions end with "64".<br />The large offset-enabled asynchronous I/O interfaces are available under the _LARGE_FILES compilation environment and under the _LARGE_FILE_API programming environment. For further information, see Writing Programs That Access Large Files in AIX 5L Version 5.3 General Programming Concepts: Writing and Debugging Programs.<br />Under the _LARGE_FILES compilation environment, asynchronous I/O applications written to the default interfaces see the following redefinitions:<br />Item        Redefined To Be        Header File<br />struct aiocb        struct aiocb64        sys/aio.h<br />aio_read()        aio_read64()        sys/aio.h<br />aio_write()        aio_write64()        sys/aio.h<br />aio_cancel()        aio_cancel64()        sys/aio.h<br />aio_suspend()        aio_suspend64()        sys/aio.h<br />aio_listio()        aio_listio64()        sys/aio.h<br />aio_return()        aio_return64()        sys/aio.h<br />aio_error()        aio_error64()        sys/aio.h<br />For information on using the _LARGE_FILES environment, see Porting Applications to the Large File Environment in AIX 5L Version 5.3 General Programming Concepts: Writing and Debugging Programs<br />In the _LARGE_FILE_API environment, the 64-bit API interfaces are visible. This environment requires recoding of applications to the new 64-bit API name. For further information on using the _LARGE_FILE_API environment, see Using the 64-Bit File System Subroutines in AIX 5L Version 5.3 General Programming Concepts: Writing and Debugging Programs<br />Nonblocking I/O<br />After issuing an I/O request, the user application can proceed without being blocked while the I/O operation is in progress. The I/O operation occurs while the application is running. Specifically, when the application issues an I/O request, the request is queued. The application can then resume running before the I/O operation is initiated.<br />To manage asynchronous I/O, each asynchronous I/O request has a corresponding control block in the application's address space. This control block contains the control and status information for the request. It can be used again when the I/O operation is completed.<br />Notification of I/O Completion<br />After issuing an asynchronous I/O request, the user application can determine when and how the I/O operation is completed. This information is provided in three ways:<br />•        The application can poll the status of the I/O operation. <br />•        The system can asynchronously notify the application when the I/O operation is done. <br />•        The application can block until the I/O operation is complete.<br />Polling the Status of the I/O Operation<br />The application can periodically poll the status of the I/O operation. The status of each I/O operation is provided in the application's address space in the control block associated with each request. Portable applications can retrieve the status by using the aio_error subroutine.The aio_suspend subroutine suspends the calling process until one or more asynchronous I/O requests are completed.<br />Asynchronously Notifying the Application When the I/O Operation Completes<br />Asynchronously notifying the I/O completion is done by signals. Specifically, an application may request that a SIGIO signal be delivered when the I/O operation is complete. To do this, the application sets a flag in the control block at the time it issues the I/O request. If several requests have been issued, the application can poll the status of the requests to determine which have actually completed.<br />Blocking the Application until the I/O Operation Is Complete<br />The third way to determine whether an I/O operation is complete is to let the calling process become blocked and wait until at least one of the I/O requests it is waiting for is complete. This is similar to synchronous style I/O. It is useful for applications that, after performing some processing, need to wait for I/O completion before proceeding.<br />Cancellation of I/O Requests<br />I/O requests can be canceled if they are cancelable. Cancellation is not guaranteed and may succeed or not depending upon the state of the individual request. If a request is in the queue and the I/O operations have not yet started, the request is cancellable. Typically, a request is no longer cancelable when the actual I/O operation has begun.<br />Asynchronous I/O Subroutines<br /><b>Note:</b>The 64-bit APIs are as follows:<br />The following subroutines are provided for performing asynchronous I/O:<br />Subroutine        Purpose<br />aio_cancel or aio_cancel64<br />Cancels one or more outstanding asynchronous I/O requests.<br />aio_error or aio_error64<br />Retrieves the error status of an asynchronous I/O request.<br />aio_fsync<br />Synchronizes asynchronous files.<br />lio_listio or lio_listio64<br />Initiates a list of asynchronous I/O requests with a single call.<br />aio_nwait<br />Suspends the calling process until n asynchronous I/O requests are completed.<br />aio_read or aio_read64<br />Reads asynchronously from a file.<br />aio_return or aio_return64<br />Retrieves the return status of an asynchronous I/O request.<br />aio_suspend or aio_suspend64<br />Suspends the calling process until one or more asynchronous I/O requests is completed.<br />aio_write or aio_write64<br />Writes asynchronously to a file.<br />Order and Priority of Asynchronous I/O Calls<br /><br />An application may issue several asynchronous I/O requests on the same file or device. However, because the I/O operations are performed asynchronously, the order in which they are handled may not be the order in which the I/O calls were made. The application must enforce ordering of its own I/O requests if ordering is required.<br /><br />Priority among the I/O requests is not currently implemented. The aio_reqprio field in the control block is currently ignored.<br /><br />For files that support seek operations, seeking is allowed as part of the asynchronous read or write operations. The whence and offset fields are provided in the control block of the request to set the seek parameters. The seek pointer is updated when the asynchronous read or write call returns.<br />Subroutines Affected by Asynchronous I/O<br /><br />The following existing subroutines are affected by asynchronous I/O:<br />•        The close subroutine <br />•        The exit subroutine <br />•        The exec subroutine <br />•        The fork subroutine<br /><br />If the application closes a file, or calls the _exit or exec subroutines while it has some outstanding I/O requests, the requests are canceled. If they cannot be canceled, the application is blocked until the requests have completed. When a process calls the fork subroutine, its asynchronous I/O is not inherited by the child process.<br /><br />One fundamental limitation in asynchronous I/O is page hiding. When an unbuffered (raw) asynchronous I/O is issued, the page that contains the user buffer is hidden during the actual I/O operation. This ensures cache consistency. However, the application may access the memory locations that fall within the same page as the user buffer. This may cause the application to block as a result of a page fault. To alleviate this, allocate page aligned buffers and do not touch the buffers until the I/O request using it has completed.<br /><br /><b>Changing Attributes for Asynchronous I/O</b><br />You can change attributes relating to asynchronous I/O using the chdev command or SMIT. Likewise, you can use SMIT to configure and remove (unconfigure) asynchronous I/O. (Alternatively, you can use the mkdev and rmdev commands to configure and remove asynchronous I/O). To start SMIT at the main menu for asynchronous I/O, enter smit aio or smit posixaio.<br /><br /><b>MINIMUM number of servers </b><br />Indicates the minimum number of kernel processes dedicated to asynchronous I/O processing. Because each kernel process uses memory, this number should not be large when the amount of asynchronous I/O expected is small. <br /><br /><b>MAXIMUM number of servers per cpu </b><br />Indicates the maximum number of kernel processes per cpu that are dedicated to asynchronous I/O processing. This number when multiplied by the number of cpus indicates the limit on I/O requests in progress at one time, and represents the limit for possible I/O concurrency. <br /><br /><b>Maximum number of REQUESTS </b><br />Indicates the maximum number of asynchronous I/O requests that can be outstanding at one time. This includes requests that are in progress as well as those that are waiting to be started. The maximum number of asynchronous I/O requests cannot be less than the value of AIO_MAX, as defined in the /usr/include/sys/limits.h file, but it can be greater. It would be appropriate for a system with a high volume of asynchronous I/O to have a maximum number of asynchronous I/O requests larger than AIO_MAX. <br /><br /><b>Server PRIORITY </b><br />Indicates the priority level of kernel processes dedicated to asynchronous I/O. The lower the priority number is, the more favored the process is in scheduling. Concurrency is enhanced by making this number slightly less than the value of PUSER, the priority of a normal user process. It cannot be made lower than the values of PRI_SCHED. <br />Because the default priority is (40+nice), these daemons will be slightly favored with this value of (39+nice). If you want to favor them more, make changes slowly. A very low priority can interfere with the system process that require low priority.<br />Attention: Raising the server PRIORITY (decreasing this numeric value) is not recommended because system hangs or crashes could occur if the priority of the AIO servers is favored too much. There is little to be gained by making big priority changes.<br />PUSER and PRI_SCHED are defined in the /usr/include/sys/pri.h file.<br /><br /><b>STATE to be configured at system restart </b><br />Indicates the state to which asynchronous I/O is to be configured during system initialization. The possible values are: <br />•        defined, which indicates that the asynchronous I/O will be left in the defined state and not available for use <br />•        available, which indicates that asynchronous I/O will be configured and available for use<br /><br /><b>STATE of FastPath </b><br />The AIO Fastpath is used only on character devices (raw logical volumes) and sends I/O requests directly to the underlying device. The file system path used on block devices uses the aio kprocs to send requests through file system routines provided to kernel extensions. Disabling this option forces all I/O activity through the aios kprocs, including I/O activity that involves raw logical volumes. In AIX 4.3 and earlier, the fast path is enabled by default and cannot be disabled. <br /><br /><b>64-bit Enhancements</b><br />Asynchronous I/O (AIO) has been enhanced to support 64-bit enabled applications. On 64-bit platforms, both 32-bit and 64-bit AIO can occur simultaneously.<br />The struct aiocb, the fundamental data structure associated with all asynchronous I/O operation, has changed. The element of this struct, aio_return, is now defined as ssize_t. Previously, it was defined as an int. AIO supports large files by default. An application compiled in 64-bit mode can do AIO to a large file without any additional #define or special opening of those files.</span>
										<br />
										<br />
										<br />
								</td>
						</tr>
						<tr>
								<td style="HEIGHT: 20em" valign="bottom">
										<img src="http://www.loveunix.net/discuz/images/common/sigline.gif" />
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<span style="FONT-SIZE: 13px">看来很久之前的以讹传讹是在是影响太厉害了。<br />1、aio server不是进程，是kernel threading，所以效率才高，要不不如用用户态的thread或者processor实现AIO算了。<br />2  and 3 显然find使用的syscall是read而不是aio_read，所以只能用阻塞IO，就算你配置了AIO ENABLE也不可能出现你说的3的情况。<br />4、最大的以讹传讹，AIO可以用于RAW DEVICE，而且效果更好。因为大多数的UNIX的AIO实现是这样的，如果对RAW DEVICE用aio_read aio_write是，是用kernel threading，效率最高；但是对于FS，只能用lwp 或者thread模拟，效率低很多。所以在使用INFORMIX DB2等等DBMS的时候，要充分发挥aio的效果，应该用raw device做数据容器。<br />5、再次强调，aio server是kernel threading，和用户自己创建的thread或者process都不同。当然AIO SERVER没有必要配置的太多，要根据 应用和使用的磁盘控制器数量等决定。<br /></span>
		</p>
<img src ="http://www.cppblog.com/tx7do/aggbug/9530.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 12:04 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9530.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>iocp进行SOCKET通信(转载) </title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9529.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 04:03:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9529.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9529.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9529.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9529.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9529.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转载于hxzb7215191 的BLOG		当然TCP方式的模型还有事件选择模型。就是把所有的网络事件和我们的一个程序里定义的事件梆定。这个有它的好处，可能可以让我们更好的写一个线程来管理接收与发送。现在来讲一下一个完成端口模型。		  完成端口   一个完成端口其实就是一个通知队列，由操作系统把已经完成的重叠I/O请求的通知 放入其中。当某项I/O操作一旦完成，某个可以对该操作结果进行处...&nbsp;&nbsp;<a href='http://www.cppblog.com/tx7do/archive/2006/07/07/9529.html'>阅读全文</a><img src ="http://www.cppblog.com/tx7do/aggbug/9529.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 12:03 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9529.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IOCP , kqueue , epoll ... 有多重要？</title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9528.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 04:02:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9528.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9528.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9528.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9528.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9528.html</trackback:ping><description><![CDATA[
		<div class="entry-body">
				<p>设计 mmo 服务器，我听过许多老生常谈，说起处理大量连接时， select 是多么低效。我们应该换用 iocp (windows), kqueue(freebsd), 或是 epoll(linux) 。的确，处理大量的连接的读写，select 是够低效的。因为 kernel 每次都要对 select 传入的一组 socket 号做轮询，那次在上海，以陈榕的说法讲，这叫鬼子进村策略。一遍遍的询问“鬼子进村了吗？”，“鬼子进村了吗？”... 大量的 cpu 时间都耗了进去。（更过分的是在 windows 上，还有个万恶的 64 限制。）</p>
				<p>使用 kqueue 这些，变成了派一些个人去站岗，鬼子来了就可以拿到通知，效率自然高了许多。不过最近我在反思，真的需要以这些为基础搭建服务器吗？</p>
		</div>
		<div class="entry-more" id="more">
				<p>刚形成的一个思路是这样的：</p>
				<p>我们把处理外部连接和处理游戏逻辑分摊到两个服务器上处理，为了后文容易表述，暂时不太严谨的把前者称为连接服务器，后者叫做逻辑服务器。</p>
				<p>连接服务器做的事情可以非常简单，只是把多个连接上的数据汇集到一起。假设同时连接总数不超过 65536 个，我们只需要把每个连接上的数据包加上一个两字节的数据头就可以表识出来。这个连接服务器再通过单个连接和逻辑服务器通讯就够了。</p>
				<p>那么连接服务器尽可以用最高效的方式处理数据，它的逻辑却很简单，代码量非常的小。而逻辑服务器只有一个外部连接，无论用什么方式处理都不会慢了。</p>
				<p>进一步，我们可以把这个方法扩展开。假定我们逻辑以 10Hz 的频率处理逻辑。我们就让连接服务器以 10Hz 的脉冲把汇总的数据周期性的发送过去，先发一个长度信息再发数据包。即使一个脉冲没有外部数据，也严格保证至少发一个 0 的长度信息。额外的，连接服务器还需要控制每个脉冲的数据总流量，不至于一次发送数据超过逻辑服务器处理的能力。</p>
				<p>那么，逻辑服务器甚至可以用阻塞方式调用 recv 收取这些数据，连 select 也省了。至于数据真的是否会被接收方阻塞，就由连接服务器的逻辑保证了。</p>
				<p>说到阻塞接收，我跟一个同事讨论的时候，他严重担心这个的可靠性，不希望因为意外把逻辑服务器挂在一个 system call 上。他列举了许多可能发生的意外情况，不过我个人是不太担心的，原因不想在这里多解释。当然我这样设计，主要不是为了节省一个 select 的调用，而是希望方便调试。（当然，如果事实证明这样不可行，修改方案也很容易）</p>
				<p>因为阻塞接收可以保证逻辑服务器的严格时序性，当我们把两个服务器中的通讯记录下来，以后可以用这些数据完全重现游戏逻辑的过程，无论怎么调试运行，都可以保证逻辑服务器的行为是可以完全重现的。即，每 0.1s 接受已知的数据包，然后处理它们。</p>
				<p>这样做，逻辑服务器对网络层的代码量的需求也大大减少了，可以更专心的构建逻辑。<br /><a href="http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html">http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html</a></p>
		</div>
<img src ="http://www.cppblog.com/tx7do/aggbug/9528.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 12:02 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9528.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ACE高效PROACTOR编程框架一ClientHandle </title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9517.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 03:46:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9517.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9517.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9517.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9517.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9517.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>1、WIN32下面用proactor可以达到几乎RAW　IOCP的效率，由于封装关系，应该是差那么一点。<br /><br /></p>
				<p>客户端处理类的常规写法：<br />//处理客户端连接消息<br />class ClientHandler : public ACE_Service_Handler<br />{<br />public:<br /> /**构造函数<br />  *<br />  *<br />  */<br /> ClientHandler(unsigned int client_recv_buf_size=SERVER_CLIENT_RECEIVE_BUF_SIZE)<br />  :_read_msg_block(client_recv_buf_size),_io_count(0)<br /> {<br /> }</p>
				<p>
						<br /> ~ClientHandler(){}</p>
				<p> /**<br />  *初始化，因为可能要用到ClientHandler内存池，而这个池又不一定要用NEW<br />  */<br /> void init();</p>
				<p> /**清理函数，因为可能要用到内存池<br />  *<br />  */<br /> void fini();<br /></p>
				<p>//检查是否超时的函数</p>
				<p> void check_time_out(time_t cur_time);</p>
				<p>public:</p>
				<p> /**客户端连接服务器成功后调用<br />  *<br />  * \param handle 套接字句柄<br />  * \param &amp;message_block 第一次读到的数据（未用）<br />  */<br /></p>
				<p>//由Acceptor来调用！！！<br /> virtual void open (ACE_HANDLE handle,ACE_Message_Block &amp;message_block);</p>
				<p> /**处理网络读操作结束消息<br />  *<br />  * \param &amp;result 读操作结果<br />  */<br /> virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &amp;result);</p>
				<p>
						<br /> /**处理网络写操作结束消息<br />  *<br />  * \param &amp;result 写操作结果<br />  */<br /> virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &amp;result);</p>
				<p>private:</p>
				<p>//**生成一个网络读请求<br />  *<br />  * \param void <br />  * \return 0-成功，-1失败<br />  */<br /> int  initiate_read_stream  (void);</p>
				<p> /**生成一个写请求<br />  *<br />  * \param mb 待发送的数据<br />  * \param nBytes 待发送数据大小<br />  * \return 0－成功，－1失败<br />  */<br /> int  initiate_write_stream (ACE_Message_Block &amp; mb, size_t nBytes );<br /> <br /> /**<br />  *<br />  * \return 检查是否可以删除，用的是一个引用计数。每一个外出IO的时候＋1，每一个IO成功后－1<br />  */<br /> int check_destroy();<br /> <br /> //异步读<br /> ACE_Asynch_Read_Stream _rs;</p>
				<p> //异步写<br /> ACE_Asynch_Write_Stream _ws;</p>
				<p> //接收缓冲区只要一个就够了，因为压根就没想过要多读，我直到现在也不是很清楚为什么要多读，多读的话要考虑很多问题<br /> ACE_Message_Block _read_msg_block;</p>
				<p> //套接字句柄,这个可以不要了，因为基类就有个HANDLER在里面的。<br /> //ACE_HANDLE _handle;</p>
				<p> //一个锁，客户端反正有东东要锁的，注意，要用ACE_Recursive_Thread_Mutex而不是用ACE_Thread_Mutex，这里面是可以重入的，而且在WIN32下是直接的EnterCriticalSection，可以达到很高的效率<br /> ACE_Recursive_Thread_Mutex _lock;<br /> <br /> //在外IO数量,其实就是引用计数啦，没啥的。为0的时候就把这个东东关掉啦。<br /> long _io_count;<br /></p>
				<p>//检查超时用的，过段时间没东东就CLOSE他了。</p>
				<p> time_t _last_net_io;</p>
				<p>private:<br /></p>
				<p>//本来想用另外一种模型的，只用1个或者2个外出读，后来想想，反正一般内存都是足够的，就不管了。</p>
				<p> //ACE_Message_Block _send_msg_blocks[2];</p>
				<p> //ACE_Message_Block &amp;_sending_msg_block;</p>
				<p> //ACE_Message_Block &amp;_idle_msg_block;</p>
				<p>private:<br /> <br />public:<br />//TODO:move to prriva and use friend class!!!<br /></p>
				<p>//只是为了效率更高，不用STL的LIST是因为到现在我没有可用的Node_Allocator，所以效率上会有问题。<br /> ClientHandler *_next;</p>
				<p> ClientHandler *next(){return _next;}</p>
				<p> void next(ClientHandler *obj){_next=obj;}</p>
				<p>};<br /></p>
				<p>
						<br />//这是具体实现，有些地方比较乱，懒得管了，锁的有些地方不对。懒得改了，反正在出错或者有瓶颈的时候再做也不迟。</p>
				<p>void ClientHandler::handle_read_stream (const ACE_Asynch_Read_Stream::Result &amp;result)<br />{<br /> _last_net_io=ACE_OS::time(NULL);<br /> int byterecved=result.bytes_transferred ();<br /> if ( (result.success ()) &amp;&amp; (byterecved != 0))<br /> {<br />  //ACE_DEBUG ((LM_DEBUG,  "Receiver completed:%d\n",byterecved));<br /></p>
				<p>//处理完数据<br />  if(handle_received_data()==true)<br />  {<br />   //ACE_DEBUG ((LM_DEBUG,  "go on reading...\n"));<br /></p>
				<p>//把东东推到头部，处理粘包<br />   _read_msg_block.crunch();<br />   initiate_read_stream();<br />  }<br /> }<br /></p>
				<p>//这个地方不想用ACE_Atom_op，因为反正要有一个锁，而且一般都会用锁，不管了。假如不在意的话，应该直接用ACE_Atom_Op以达到最好的效率</p>
				<p> {<br />  ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br />  _io_count--;<br /> }<br /> check_destroy ();<br />}</p>
				<p>void ClientHandler::init()<br />{<br /></p>
				<p>//初始化数据，并不在构造函数里做。<br /> _last_net_io=ACE_OS::time(NULL);<br /> _read_msg_block.rd_ptr(_read_msg_block.base());<br /> _read_msg_block.wr_ptr(_read_msg_block.base());<br /> this-&gt;handle(ACE_INVALID_HANDLE);<br />}</p>
				<p>bool ClientHandler::handle_received_data()<br />{<br /></p>
				<p>...........自己处理<br /> return true;<br />}</p>
				<p>
						<br />//==================================================================<br />void ClientHandler::handle_write_stream (const ACE_Asynch_Write_Stream::Result &amp;result)<br />{<br /> //发送成功，RELEASE掉<br /> //这个不可能有多个RELEASE，直接XX掉<br /> //result.message_block ().release ();<br /> MsgBlockManager::get_instance().release_msg_block(&amp;result.message_block());</p>
				<p> {<br />  ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br />  _io_count--;<br /> }<br /> check_destroy ();<br />}</p>
				<p>//bool ClientHandler::destroy () <br />//{<br />// FUNC_ENTER;<br />// ClientManager::get_instance().release_client_handle(this);<br />// FUNC_LEAVE;<br />// return false ;<br />//}</p>
				<p>
						<br />int  ClientHandler::initiate_read_stream  (void)<br />{<br /> ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br /></p>
				<p>//考虑到粘包的呀<br /> if (_rs.read (_read_msg_block, _read_msg_block.space()) == -1)<br /> {<br />  ACE_ERROR_RETURN ((LM_ERROR,"%p\n","ACE_Asynch_Read_Stream::read"),-1);<br /> }<br /> _io_count++;<br /> return 0;<br />}</p>
				<p>/**生成一个写请求<br />*<br />* \param mb 待发送的数据<br />* \param nBytes 待发送数据大小<br />* \return 0－成功，－1失败<br />*/<br />int  ClientHandler::initiate_write_stream (ACE_Message_Block &amp; mb, size_t nBytes )<br />{<br /> ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br /> if (_ws.write (mb , nBytes ) == -1)<br /> {<br />  mb.release ();<br />  ACE_ERROR_RETURN((LM_ERROR,"%p\n","ACE_Asynch_Write_File::write"),-1);<br /> }<br /> _io_count++;<br /> return 0;<br />}</p>
				<p>void ClientHandler::open (ACE_HANDLE handle,ACE_Message_Block &amp;message_block)<br />{<br /> //FUNC_ENTER;<br /> _last_net_io=ACE_OS::time(NULL);<br /> _io_count=0;<br /> if(_ws.open(*this,this-&gt;handle())==-1)<br /> {<br />  ACE_ERROR ((LM_ERROR,"%p\n","ACE_Asynch_Write_Stream::open"));<br /> }<br /> else if (_rs.open (*this, this-&gt;handle()) == -1)<br /> {<br />  ACE_ERROR ((LM_ERROR,"%p\n","ACE_Asynch_Read_Stream::open"));<br /> }<br /> else<br /> {<br />  initiate_read_stream ();<br /> }</p>
				<p> check_destroy();<br /> //FUNC_LEAVE;<br />}</p>
				<p>void ClientHandler::fini()<br />{<br />}</p>
				<p>void ClientHandler::check_time_out(time_t cur_time)<br />{<br /> //ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br /> //ACE_DEBUG((LM_DEBUG,"cur_time is %u,last io is %u\n",cur_time,_last_net_io));</p>
				<p> //检测是否已经为0了<br /> if(this-&gt;handle()==ACE_INVALID_HANDLE)<br />  return;<br /> if(cur_time-_last_net_io&gt;CLIENT_TIME_OUT_SECONDS)<br /> {<br />  ACE_OS::shutdown(this-&gt;handle(),SD_BOTH);<br />  ACE_OS::closesocket(this-&gt;handle());<br />  this-&gt;handle(ACE_INVALID_HANDLE);<br /> }<br />}</p>
				<p>int ClientHandler::check_destroy()<br />{<br /> {<br />  ACE_Guard&lt;ACE_Recursive_Thread_Mutex&gt; locker (_lock);<br />  if (_io_count&gt; 0)<br />   return 1;<br /> }<br /> ACE_OS::shutdown(this-&gt;handle(),SD_BOTH);<br /> ACE_OS::closesocket(this-&gt;handle());<br /> this-&gt;handle(ACE_INVALID_HANDLE);<br /></p>
				<p>//这个地方给内存池吧。<br /> ClientManager::get_instance().release_client_handle(this);<br /> //delete this;<br /> return 0;<br />}</p>
		</div>
<img src ="http://www.cppblog.com/tx7do/aggbug/9517.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 11:46 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9517.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在WinSock上使用IOCP</title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9518.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 03:46:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9518.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9518.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9518.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9518.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9518.html</trackback:ping><description><![CDATA[在WinSock上使用IOCP <br />本文章假设你已经理解WindowsNT的I/O模型以及I/O完成端口(IOCP)，并且比较熟悉将要用到的API，如果你打算学习IOCP，请参考Jeffery Richter的Advanced Windows(第三版)，第15章I/O设备，里面有极好的关于完成端口的讨论以及对即将使用API的说明。 <br />IOCP提供了一个用于开发高效率和易扩展程序的模型。Winsock2提供了对IOCP的支持，并在WindowsNT平台得到了完整的实现。然而IOCP是所有WindowsNT I/O模型中最难理解和实现的，为了帮助你使用IOCP设计一个更好的Socket服务，本文提供了一些诀窍。 <br /><br />Tip 1:使用Winsock2 IOCP函数例如WSASend和WSARecv，如同Win32文件I/O函数，例如WriteFile和ReadFile。 <br />微软提供的Socket句柄是一个可安装文件系统(IFS)句柄，因此你可以使用Win32的文件I/O函数调用这个句柄，然而，将Socket句柄和文件系统联系起来，你不得不陷入很多的Kernal/User模式转换的问题中，例如线程的上下文转换，花费的代价还包括参数的重新排列导致的性能降低。 <br />因此你应该使用只被Winsock2中IOCP允许的函数来使用IOCP。在ReadFile和WriteFile中会发生的额外的参数重整以及模式转换只会发生在一种情况下，那就是如果句柄的提供者并没有将自己的WSAPROTOCOL_INFO结构中的DwServiceFlags1设置为XP1_IFS_HANDLES。 <br />注解：即使使用WSASend和WSARecv，这些提供者仍然具有不可避免的额外的模式转换，当然ReadFile和WriteFile需要更多的转换。 <br /><br />TIP 2: 确定并发工作线程数量和产生的工作线程总量。 <br />并发工作线程的数量和工作线程的数量并不是同一概念。你可以决定IOCP使用最多2个的并发线程以及包括10个工作线程的线程池。工作线程池拥有的线程多于或者等于并发线程的数量时，工作线程处理队列中一个封包的时候可以调用win32的Wait函数，这样可以无延迟的处理队列中另外的封包。 <br />如果队列中有正在等待被处理的封包，系统将会唤醒一个工作线程处理他，最后，第一个线程确认正在休眠并且可以被再次调用，此时，可调用线程数量会多于IOCP允许的并发线程数量(例如,NumberOFConcurrentThreads)。然而，当下一个线程调用GetQueueCompletionStatus并且进入等待状态，系统不会唤醒他。一般来说，系统会试图保持你设定的并发工作线程数量。 <br />一般来讲，每拥有一个CPU，在IOCP中你可以使用一个并发工作线程，要做到这点，当你第一次初始化IOCP的时候，可以在调用CreateIOCompletionPort的时候将NumberOfConcurrentThreads设置为0。 <br /><br />TIP 3：将一个提交的I/O操作和完成封包的出列联系起来。 <br />当对一个封包进行出列，可以调用GetQueuedCompletionStatus返回一个完成Key和一个复合的结构体给I/O。你可以分别的使用这两个结构体来返回一个句柄和一个I/O操作信息，当你将IOCP提供的句柄信息注册给Socket，那么你可以将注册的Socket句柄当做一个完成Key来使用。为每一个I/O的"extend"操作提供一个包含你的应用程序IO状态信息的复合结构体。当然，必须确定你为每个的I/O提供的是唯一的复合结构体。当I/O完成的时候，会返回一个指向结构体的指针。 <br /><br />TIP 4:I/O完成封包队列的行为 <br />IOCP中完成封包队列的等待次序并不决定于Winsock2 I/O调用产生的顺序。如果一个Winsock2的I/O调用返回了SUCCESS或者IO_PENDING，那么他保证当I/O操作完成后，完成封包会进入IOCP的等待队列，而不管Socket句柄是否已经关闭。如果你关闭了socket句柄，那么将来调用WSASend,WSASendTo,WSARecv和WSARecvFrom会失败并返回一个不同于SUCCES或者IO_PENDING的代码，这时将不会产生一个完成封包。而在这种情况下，前一次使用GetQueuedCompletionStatus提交的I/O操作所得到的完成封包，会显示一个失败的信息。 <br />如果你删除了IOCP本身，那么不会有任何I/O请求发送给IOCP，因为IOCP的句柄已经不可用，尽管系统底层的IOCP核心结构并不会在所有已提交I/O请求完成之前被移除。 <br /><br />TIP5:IOCP的清除 <br />很重要的一件事是使用复合I/O时候的IOCP清除：如果一个I/O操作尚未完成，那么千万不要释放该操作创建的复合结构体。HasOverlappedIoCompleted函数可以帮助你检查一个I/O操作是否已经完成。 <br />关闭服务一般有两种情况，第一种你并不关心尚未结束的I/O操作的完成状态，你只希望尽可能快的关闭他。第二种，你打算关闭服务，但是你需要获知未结束I/O操作的完成状态。 <br />第一种情况你可以调用PostQueueCompletionStatus(N次，N等于你的工作线程数量)来提交一个特殊的完成封包，他通知所有的工作线程立即退出，关闭所有socket句柄和他们关联的复合结构体，然后关闭完成端口(IOCP)。在关闭复合结构体之前使用HasOverlappedIOCompleted检查他的完成状态。如果一个socket关闭了，所有基于他的未结束的I/O操作会很快的完成。 <br />在第二种情况，你可以延迟工作线程的退出来保证所有的完成封包可以被适当的出列。你可以首先关闭所有的socket句柄和IOCP。可是，你需要维护一个未完成I/O的数字，以便你的线程可以知道可以安全退出的时间。尽管当队列中有很多完成封包在等待的时候，活动的工作线程不能立即退出，但是在IOCP服务中使用全局I/O计数器并且使用临界区保护他的代价并不会象你想象的那样昂贵。 <br /><br />INFO: Design Issues When Using IOCP in a Winsock Server <br /><br />适用于 <br /><br />This article was previously published under Q192800 <br /><br />SUMMARY <br /><br />This article assumes you already understand the I/O model of the Windows NT I/O Completion Port (IOCP) and are familiar with the related APIs. If you want to learn IOCP, please see Advanced Windows (3rd edition) by Jeffery Richter, chapter 15 Device I/O for an excellent discussion on IOCP implementation and the APIs you need to use it. <br /><br /><br /><br />An IOCP provides a model for developing very high performance and very scalable server programs. Direct IOCP support was added to Winsock2 and is fully implemented on the Windows NT platform. However, IOCP is the hardest to understand and implement among all Windows NT I/O models. To help you design a better socket server using IOCP, a number of tips are provided in this article. <br /><br />MORE INFORMATION <br /><br />TIP 1: Use Winsock2 IOCP-capable functions, such as WSASend and WSARecv, over Win32 file I/O functions, such as WriteFile and ReadFile. <br /><br /><br /><br />Socket handles from Microsoft-based protocol providers are IFS handles so you can use Win32 file I/O calls with the handle. However, the interactions between the provider and file system involve many kernel/user mode transition, thread context switches, and parameter marshals that result in a significant performance penalty. You should use only Winsock2 IOCP- capable functions with IOCP. <br /><br /><br /><br />The additional parameter marshals and mode transitions in ReadFile and WriteFile only occur if the provider does not have XP1_IFS_HANDLES bit set in dwServiceFlags1 of its WSAPROTOCOL_INFO structure. <br /><br /><br /><br />NOTE: These providers have an unavoidable additional mode transition, even in the case of WSASend and WSARecv, although ReadFile and WriteFile will have more of them. <br /><br /><br /><br />TIP 2: Choose the number of the concurrent worker threads allowed and the total number of the worker threads to spawn. <br /><br /><br /><br />The number of worker threads and the number of concurrent threads that the IOCP uses are not the same thing. You can decide to have a maximum of 2 concurrent threads used by the IOCP and a pool of 10 worker threads. You have a pool of worker threads greater than or equal to the number of concurrent threads used by the IOCP so that a worker thread handling a dequeued completion packet can call one of the Win32 "wait" functions without delaying the handling of other queued I/O packets. <br /><br /><br /><br />If there are completion packets waiting to be dequeued, the system will wake up another worker thread. Eventually, the first thread satisfies it's Wait and it can be run again. When this happens, the number of the threads that can be run is higher than the concurrency allowed on the IOCP (for example, NumberOfConcurrentThreads). However, when next worker thread calls GetQueueCompletionStatus and enters wait status, the system does not wake it up. In other words, the system tries to keep your requested number of concurrent worker threads. <br /><br /><br /><br />Typically, you only need one concurrent worker thread per CPU for IOCP. To do this, enter 0 for NumberOfConcurrentThreads in the CreateIoCompletionPort call when you first create the IOCP. <br /><br /><br /><br />TIP 3: Associate a posted I/O operation with a dequeued completion packet. <br /><br /><br /><br />GetQueuedCompletionStatus returns a completion key and an overlapped structure for the I/O when dequeuing a completion packet. You should use these two structures to return per handle and per I/O operation information, respectively. You can use your socket handle as the completion key when you register the socket with the IOCP to provide per handle information. To provide per I/O operation "extend" the overlapped structure to contain your application-specific I/O-state information. Also, make sure you provide a unique overlapped structure for each overlapped I/O. When an I/O completes, the same pointer to the overlapped I/O structure is returned. <br /><br /><br /><br />TIP 4: I/O completion packet queuing behavior. <br /><br /><br /><br />The order in which I/O completion packets are queued in the IOCP is not necessarily the same order the Winsock2 I/O calls were made. Additionally, if a Winsock2 I/O call returns SUCCESS or IO_PENDING, it is guaranteed that a completion packet will be queued to the IOCP when the I/O completes, regardless of whether the socket handle is closed. After you close a socket handle, future calls to WSASend, WSASendTo, WSARecv, or WSARecvFrom will fail with a return code other than SUCCESS or IO_PENDING, which will not generate a completion packet. The status of the completion packet retrieved by GetQueuedCompletionStatus for I/O previously posted could indicate a failure in this case. <br /><br /><br /><br />If you delete the IOCP itself, no more I/O can be posted to the IOCP because the IOCP handle itself is invalid. However, the system's underlying IOCP kernel structures do not go away until all successfully posted I/Os are completed. <br /><br /><br /><br />TIP 5: IOCP cleanup. <br /><br /><br /><br />The most important thing to remember when performing ICOP cleanup is the same when using overlapped I/O: do not free an overlapped structure if the I/O for it has not yet completed. The HasOverlappedIoCompleted macro allows you to detect if an I/O has completed from its overlapped structure. <br /><br /><br /><br />There are typically two scenarios for shutting down a server. In the first scenario, you do not care about the completion status of outstanding I/Os and you just want to shut down as fast as you can. In the second scenario, you want to shut down the server, but you do need to know the completion status of each outstanding I/O. <br /><br /><br /><br />In the first scenario, you can call PostQueueCompletionStatus (N times, where N is the number of worker threads) to post a special completion packet that informs the worker thread to exit immediately, close all socket handles and their associated overlapped structures, and then close the completion port. Again, make sure you use HasOverlappedIoCompleted to check the completion status of an overlapped structure before you free it. If a socket is closed, all outstanding I/O on the socket eventually complete quickly. <br /><br /><br /><br />In the second scenario, you can delay exiting worker threads so that all completion packets can be properly dequeued. You can start by closing all socket handles and the IOCP. However, you need to maintain a count of the number of outstanding I/Os so that your worker thread can know when it is safe to exit the thread. The performance penalty of having a global I/O counter protected with a critical section for an IOCP server is not as bad as might be expected because the active worker thread does not switch out if there are more completion packets waiting in the queue. <img src ="http://www.cppblog.com/tx7do/aggbug/9518.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 11:46 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9518.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>心跳包</title><link>http://www.cppblog.com/tx7do/archive/2006/07/07/9516.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 07 Jul 2006 03:45:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/07/07/9516.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/9516.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/07/07/9516.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/9516.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/9516.html</trackback:ping><description><![CDATA[一般是用来判断对方（设备，进程或其它网元）是否正常动行，一般采用定时发送简单的通讯包，如果在指定时间段内未收到对方响应，则判断对方已经当掉。用于检测TCP的异常断开。<br /><p>一般是用来判断对方（设备，进程或其它网元）是否正常动行，一般采用定时发送简单的通讯包，如果在指定时间段内未收到对方响应，则判断对方已经当掉。用于检测TCP的异常断开。</p><p><br />基本原因是服务器端不能有效的判断客户端是否在线也就是说，服务器无法区分客户端是长时间在空闲，还是已经掉线的情况.所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。</p><p>代码就是每隔几分钟发送一个固定信息给服务端，服务端收到后回复一个固定信息<br />如果服务端几分钟内没有收到客户端信息则视客户端断开。比如有些通信软件长时间不使用，要想知道它的状态是在线还是离线就需要心跳包，定时发包收包。</p><p>发包方：可以是客户也可以是服务端，看哪边实现方便合理。一般是客户端。服务器也可以定时轮询发心跳下去。<br /><br />一般来说，出于效率的考虑，是由客户端主动向服务器端发包，而不是相反。</p><img src ="http://www.cppblog.com/tx7do/aggbug/9516.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-07-07 11:45 <a href="http://www.cppblog.com/tx7do/archive/2006/07/07/9516.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll Scalability Web Page</title><link>http://www.cppblog.com/tx7do/archive/2006/05/28/7761.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Sun, 28 May 2006 01:58:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/05/28/7761.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/7761.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/05/28/7761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/7761.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/7761.html</trackback:ping><description><![CDATA[
		<center>
				<h2>
						<tt>epoll Scalability Web Page</tt>
				</h2>
		</center>
		<p>
				<tt>
						<ul>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#intro">Introduction</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#ifacedesc">Interface Description</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#man">Man Pages</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#testing">Testing</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#dphttpd">dphttpd</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#dphsmpres">dphttpd SMP results</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#dphupres">dphttpd UP results</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#pipetest">pipetest</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#pipetestres">pipetest results</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#compare">Recent comparison results</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#conc">Analysis and Conclusions</a>
								</li>
								<li>
										<a href="http://lse.sourceforge.net/epoll/index.html#ack">Acknowledgements</a>
								</li>
						</ul>
				</tt>
		</p>
		<p>
		</p>
		<a name="intro">
				<center>
						<h2>
								<tt>Introduction</tt>
						</h2>
				</center>
		</a>
		<p>
				<tt>
						<a href="mailto:davidel@xmailserver.org">Davide Libenzi</a> wrote an event poll implementation and described it at <a href="http://www.xmailserver.org/linux-patches/nio-improve.html">the /dev/epoll home page here.</a> His performance testing led to the conclusion that epoll scaled linearly regardless of the load as defined by the number of dead connections. However, the main hindrance to having epoll accepted into the mainline Linux kernel by Linus Torvalds was the interface to epoll being in /dev. Therefore, a new interface to epoll was added via three new system calls. That interface will hereafter be referred to as sys_epoll. <a href="http://www.xmailserver.org/linux-patches/nio-improve.html#patches">Download sys_epoll here.</a></tt>
		</p>
		<br />
		<p>
				<a name="ifacedesc">
						<center>
								<h2>
										<tt>sys_epoll Interface </tt>
								</h2>
						</center>
				</a>
				<br />
		</p>
		<ul>
				<li>
						<tt>int epoll_create(int maxfds);</tt>
						<br />
						<br />
						<p>
								<tt>The system call epoll_create() creates a sys_epoll "object" by allocating space for "maxfds" descriptors to be polled. The sys_epoll "object" is referenced by a file descriptor, and this enables the new interface to :</tt>
						</p>
						<p>
								<tt>
										<ul>
												<li>Maintain compatibility with the existing interface 
</li>
												<li>Avoid the creation of a epoll_close() syscall 
</li>
												<li>Reuse 95% of the existing code 
</li>
												<li>Inherit the file* automatic clean up code</li>
										</ul>
								</tt>
						</p>
						<p>
						</p>
				</li>
				<li>
						<tt>int epoll_ctl(int epfd, int op, int fd, unsigned int events);</tt>
						<br />
						<br />
						<p>
								<tt>The system call epoll_ctl() is the controller interface. The "op" parameter is either EP_CTL_ADD, EP_CTL_DEL or EP_CTL_MOD. The parameter "fd" is the target of the operation. The last parameter, "events", is used in both EP_CTL_ADD and EP_CTL_MOD and represents the event interest mask. </tt>
						</p>
				</li>
				<li>
						<tt>int epoll_wait(int epfd, struct pollfd * events, int maxevents, int timeout); </tt>
						<br />
						<br />
						<p>
								<tt>The system call epoll_wait() waits for events by allowing a maximum timeout, "timeout", in milliseconds and returns the number of events ( struct pollfd ) that the caller will find available at "*events". </tt>
						</p>
				</li>
		</ul>
		<p>
		</p>
		<a name="man">
				<center>
						<h2>
								<tt>sys_epoll Man Pages </tt>
						</h2>
				</center>
		</a>
		<ul>
				<li>Postscript : 
<ul><li><a href="http://www.xmailserver.org/linux-patches/epoll.ps">epoll.ps</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_create.ps">epoll_create.ps</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_ctl.ps">epoll_ctl.ps</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_wait.ps">epoll_wait.ps</a><br /></li></ul></li>
				<li>ASCI Text : 
<ul><li><a href="http://www.xmailserver.org/linux-patches/epoll.txt">epoll.txt</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_create.txt">epoll_create.txt</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_ctl.txt">epoll_ctl.txt</a><br /></li><li><a href="http://www.xmailserver.org/linux-patches/epoll_wait.txt">epoll_wait.txt</a><br /></li></ul></li>
		</ul>
		<a name="testing">
				<center>
						<h2>
								<tt>Testing </tt>
						</h2>
				</center>
		</a>
		<p>
				<tt>We tested using two applications: 
<ul><li>dphttpd 
</li><li>pipetest</li></ul><a name="dphttpd"><center><h2><tt>dphttpd </tt></h2></center></a><h2><tt>Software </tt></h2><p><tt>The http client is httperf from David Mosberger. <a href="http://www.hpl.hp.com/personal/David_Mosberger/httperf.html">Download httperf here.</a> The http server is dphttpd from Davide Libenzi. <a href="http://www.xmailserver.org/linux-patches/ephttpd-0.1.tar.gz">Download dphttpd here.</a> The deadconn client is also provided by Davide Libenzi. <a href="http://www.xmailserver.org/linux-patches/deadconn_last.c">Download deadconn here.</a></tt></p><p><tt>Two client programs (deadconn_last and httperf) run on the client machine and establish connections to the HTTP server (dphttpd) running on the server machine. Connections established by deadconn_last are "dead". These send a single HTTP get request at connection setup and remain idle for the remainder of the test. Connections established by httperf are "active". These continuously send HTTP requests to the server at a fixed rate. httperf reports the rate at which the HTTP server replies to its requests. This reply rate is the metric reported on the Y-axis of the graphs below.</tt></p><p><tt>For the tests, the number of active connections is kept constant and the number of dead connections increased from 0 to 60000 (X-axis of graphs below). Consequently, dphttpd spends a fixed amount of time responding to requests and a variable amount of time looking for requests to service. The mechanism used to look for active connections amongst all open connections is one of standard poll(), /dev/epoll or sys_epoll. As the number of dead connections is increased, the scalability of these mechanisms starts impacting dphttpd's reply rate, measured by httperf.</tt></p><h2><tt>dphttpd SMP </tt></h2><h2><tt>Server </tt></h2><p><tt><ul><li>Hardware: 8-way PIII Xeon 700MHz, 2.5 GB RAM, 2048 KB L2 cache 
</li><li>OS : RedHat 7.3 with 2.5.44 kernel, patched with ONE of: 
<ul><li>sys_epoll-2.5.44-0.9.diff <a href="http://www.xmailserver.org/linux-patches/sys_epoll-2.5.44-0.9.diff">To reproduce download here.</a><br /><a href="http://www.xmailserver.org/linux-patches/nio-improve.html#patches">To run the latest sys_epoll patch download here. </a></li><li>ep_patch-2.5.44-0.32.diff <a href="http://www.xmailserver.org/linux-patches/ep_patch-2.5.44-0.32.diff">Download /dev/epoll</a></li><li>unpatched kernel <a href="http://www.kernel.org/pub/linux/kernel/v2.5/linux-2.5.44.tar.gz">Download 2.5.44.</a></li></ul></li><li>/proc/sys/fs/file-max = 131072 
</li><li>/proc/sys/net/ipv4/tcp_fin_timeout = 15 
</li><li>/proc/sys/net/ipv4/tcp_max_syn_backlog = 16384 
</li><li>/proc/sys/net/ipv4/tcp_tw_reuse = 1 
</li><li>/proc/sys/net/ipv4/tcp_tw_recycle = 1 
</li><li>/proc/sys/net/ipv4/ip_local_port_range = 1024 65535 
</li><li># ulimit -n 131072 
</li><li># dphttpd --maxfds 20000 --stksize 4096 
</li><li>default size of reply = 128 bytes</li></ul></tt></p><p></p><h2><tt>Client </tt></h2><p><tt><ul><li>Hardware: 4-way PIII Xeon 500MHz, 3 GB RAM, 512 KB L2 cache 
</li><li>OS : RedHat 7.3 with 2.4.18-3smp kernel 
</li><li>/proc/sys/fs/file-max = 131072 
</li><li>/proc/sys/net/ipv4/tcp_fin_timeout = 15 
</li><li>/proc/sys/net/ipv4.tcp_tw_recycle = 1 
</li><li>/proc/sys/net/ipv4.tcp_max_syn_backlog = 16384 
</li><li>/proc/sys/net/ipv4.ip_local_port_range = 1024 65535 
</li><li># ulimit -n 131072 
</li><li># deadconn_last $SERVER $SVRPORT num_connections <br />where num_connections is one of 0, 50, 100, 200, 400, 800, 1000, 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000. 
</li><li>After deadconn_last reports num_connections established <br /># httperf --server=$SERVER --port=$SVRPORT --think-timeout 5 --timeout 5 --num-calls 20000 --num-conns 100 --hog --rate 100</li></ul></tt></p><p></p><a name="dphsmpres"><h2><tt>Results for dphttpd SMP </tt></h2></a><center><img height="480" src="http://lse.sourceforge.net/epoll/dph-smp.png" width="640" /></center><h2><tt>dphttpd UP </tt></h2><h2><tt>Server </tt></h2><p><tt><ul><li>1-way PIII, 866MHz, 256 MB RAM 
</li><li>OS: 2.5.44 gcc 2.96 (RedHat 7.3) 
</li><li>/proc/sys/fs/file-max = 65536 
</li><li>/proc/sys/net/ipv4/tcp_fin_timeout = 15 
</li><li>/proc/sys/net/ipv4/tcp_tw_recycle = 1 
</li><li># ulimit -n 65536 
</li><li># dphttpd --maxfds 20000 --stksize 4096 
</li><li>default size of reply = 128 bytes</li></ul><ul></ul></tt></p><p></p><h2><tt>Client </tt></h2><p><tt><ul><li>1-way PIII, 866MHz, 256 MB RAM 
</li><li>OS: 2.4.18, gcc 2.96 (RedHat 7.3) 
</li><li>/proc/sys/fs/file-max = 65536 
</li><li>/proc/sys/net/ipv4/tcp_fin_timeout = 15 
</li><li>/proc/sys/net/ipv4.tcp_tw_recycle = 1 
</li><li># ulimit -n 65536 
</li><li># deadconn_last $SERVER $SVRPORT num_connections <br />where num_connections is one of 0, 50, 100, 200, 400, 800, 1000, 2000, 4000, 6000, 8000, 10000, 12000, 14000, 16000. 
</li><li>After deadconn_last reports num_connections established <br /># httperf --server=$SERVER --port=$SVRPORT --think-timeout 5 --timeout 5 --num-calls 20000 --num-conns 100 --hog --rate 100 </li></ul></tt></p><p></p><h2><tt>Results for dphttpd UP </tt></h2><a name="dphupres"><center><img height="480" src="http://lse.sourceforge.net/epoll/dph-up.png" width="640" /></center></a><br /><br /><a name="pipetest"><center><h2><tt>Pipetest </tt></h2></center></a><p><tt>David Stevens added support for sys_epoll to Ben LaHaise's original pipetest.c application. <a href="http://www.kvack.org/~blah/ols2002.tar.gz">Download Ben LaHaise's Ottawa Linux Symposium 2002 paper including pipetest.c here.</a><a href="http://prdownloads.sourceforge.net/lse/sys_epoll-pipetest.patch?download">Download David Steven's patch to add sys_epoll to pipetest.c here.</a></tt></p><br /><h2><tt>Pipetest SMP </tt></h2><h2><tt>System and Configuration </tt></h2><p><tt><ul><li>8-way PIII Xeon 900MHz, 4 GB RAM, 2048 KB L2 cache 
</li><li>OS: RedHat 7.2 with 2.5.44 kernel, patched with one of: 
<ul><li>sys_epoll-2.5.44-0.9.diff <a href="http://www.xmailserver.org/linux-patches/sys_epoll-2.5.44-0.9.diff">To reproduce download here.</a><br /><a href="http://www.xmailserver.org/linux-patches/nio-improve.html#patches">To run the latest sys_epoll patch download here. </a></li><li>async poll ported to 2.5.44 <a href="http://prdownloads.sourceforge.net/lse/aiopoll-2.5.44-1.patch?download">download here</a>.</li></ul></li><li>/proc/sys/fs/file-max = 65536 
</li><li># ulimit -n 65536 
</li><li># pipetest [poll|aio-poll|sys-epoll] num_pipes message_threads max_generation 
<ul><li>where num_pipes is one of 10, 100, 500, or 1000-16000 in increments of 1000 
</li><li>message_threads is 1 
</li><li>max_generantion is 300</li></ul></li></ul></tt></p><p></p><a name="pipetestres"><h2><tt>Results for pipetest on an SMP </tt></h2></a><center><img height="480" src="http://lse.sourceforge.net/epoll/ppt-smp.png" width="640" /></center><h2><tt>Results for Pipetest on a UP </tt></h2><p><tt>Same Hardware and Configuration as with the SMP pipetest above <br />with CONFIG_SMP = n being the only change.</tt></p><center><img height="480" src="http://lse.sourceforge.net/epoll/ppt-up.png" width="640" /></center><a name="compare"><center><h2><tt>sys_epoll stability comparisons Oct 30, 2002 </tt></h2></center><br /></a><p><tt>Following are performance results comparing version 0.14 of the <a href="http://www.xmailserver.org/linux-patches/sys_epoll-2.5.44-0.14.diff">(Download v0.14 here)</a> to the version, v0.9, originally used for the performance testing outlined above. <a href="http://www.xmailserver.org/linux-patches/sys_epoll-2.5.44-0.9.diff">(Download v0.9 here)</a> Testing was done using two measures: <a href="http://lse.sourceforge.net/epoll/index.html#pipetest">pipetest details here.</a> and <a href="http://lse.sourceforge.net/epoll/index.html#dphttpd">dphttpd details here.</a>. <br /><center><img height="480" src="http://lse.sourceforge.net/epoll/ppt-compare.png" width="640" /></center><center><img height="480" src="http://lse.sourceforge.net/epoll/dph-compare.png" width="640" /></center></tt></p><p></p><a name="conc"><center><h2><tt>Analysis and Conclusion </tt></h2></center></a><p><tt>The system call interface to epoll performs as well as the /dev interface to epoll. In addition sys_epoll scales better than poll and AIO poll for large numbers of connections. Other points in favour of sys_epoll are:</tt></p><p><tt><ul><li>sys_epoll is compatible with synchronous read() and write() and thus makes it usable with existing libraries that have not migrated to AIO. 
</li><li>Applications using poll() can be easily migrated to sys_epoll while continuing to use the existing I/O infrastructure. 
</li><li>sys_epoll will be invisible to people who don't want to use it. 
</li><li>sys_epoll has a low impact on the existing source code. <pre><tt>
arch/i386/kernel/entry.S  |    4
fs/file_table.c           |    4
fs/pipe.c                 |   36 +
include/asm-i386/poll.h   |    1
include/asm-i386/unistd.h |    3
include/linux/fs.h        |    4
include/linux/list.h      |    5
include/linux/pipe_fs_i.h |    4
include/linux/sys.h       |    2
include/net/sock.h        |   12
net/ipv4/tcp.c            |    4
</tt></pre></li></ul></tt></p><p></p><p><tt>Due to these factors sys_epoll should be seriously considered for inclusion in the mainline Linux kernel.</tt></p><a name="ack"><center><h2><tt>Acknowledgments </tt></h2></center></a><p><tt>Thank You to: <a href="mailto:davidel@xmailserver.org">Davide Libenzi</a> Who wrote /dev/epoll, sys_epoll and dphttpd.<br />He was an all around great guy to work with.<br />Also Thanks to the following people who helped with testing and this web site: <br /><a href="mailto:nagar@watson.ibm.com">Shailabh Nagar</a>, <a href="mailto:plars@austin.ibm.com">Paul Larson</a> , <a href="mailto:hannal@us.ibm.com">Hanna Linder</a>, and <a href="mailto:dlstevens@us.ibm.com">David Stevens.</a></tt><br /></p><br /></tt>
		</p>
<img src ="http://www.cppblog.com/tx7do/aggbug/7761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-05-28 09:58 <a href="http://www.cppblog.com/tx7do/archive/2006/05/28/7761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IOCP中的socket错误和资源释放处理方法</title><link>http://www.cppblog.com/tx7do/archive/2006/04/20/5972.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Thu, 20 Apr 2006 10:52:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/04/20/5972.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/5972.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/04/20/5972.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/5972.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/5972.html</trackback:ping><description><![CDATA[本文出处：<a href="http://blog.csdn.net/sodme"><font color="#666699">http://blog.csdn.net/sodme</font></a><br />声明：本文可以不经作者同意任意转载、复制、传播，但任何对本文的引用均须保留本文的作者、出处及本行声明信息！谢谢！<br /><br />前言:  <br />错误处理和socket释放, 是IOCP编程中的一大难点. 本文试图就IOCP设计中经常遇到的这个难题展开论述并寻找其解决方案, 事实上, 文中所述的解决方式不仅仅适用于IOCP, 它同样适用于EPOLL等多种服务器编程的网络模型中, 前提是: 领会这种处理方式的实质.<br /><br />正文:<br />在使用IOCP开发时, 大家经常遇到的一个难题是与socket相关的缓冲区释放不当带来的错误, 这种错误通常是由于多次对同一个指针执行了delete操作引起的. 比如, 当在执行wsasend或wsarecv返回了非pending的错误信息时, 我们就要对此错误进行处理, 通常情况下, 我们会想到执行这两步操作: <br />a. 释放此次操作使用的缓冲区数据(如果不释放可能造成内存泄漏); <br />b. 关闭当前操作所使用的socket. <br />而另一方面, 我们可能也会在get函数(GetQueuedCompletionStatus)的处理中, 当get函数返回值为FALSE时也作这两步相同的操作.  此时, 就会造成对同一缓冲区的重复释放, 问题由此产生.<br /><br />解决的方法, 可以有这几种: <br />1. 对数据缓冲区使用引用计数机制; <br />2. 在clientsock的对象设计机制上使释放操作线性化. <br />关于这两种方法, 任何一种如果要详细说清, 可能篇幅都会比较长, 笔者并无耐心和精力将每一个细节都一一道来, 在此仅选第2种方案的关键步骤和核心思想来与大家分享.<br /><br />由前面对问题的描述可以看出, 造成多次释放的原因可能是在执行收发操作和GET函数返回值为FALSE时, 我们重复执行了释放操作. 很自然地, 我们会想到,  能不能把这两次释放合并成一次释放,  这样不就没问题了吗?  yes,  这个思路是没问题的.  但要想让这个思路能变成现实,  需要在设计机制上对这个思路进行一定的支持.<br /><br />首先,  我们假设, 是在get函数返回时统一进行相应的释放和关闭操作.<br /><br />如果在执行wsasend操作时, 发生了非pending错误(io操作正在进行中), 而此时我们如果不释放资源, 那至少得让IOCP在GET返回时得知这个错误和发生错误时的缓冲区指针. 通知IOCP的方式, 是使用post函数(PostQueuedCompletionStatus)向IOCP抛一个特殊标志的消息, 这个特殊标志可以通过get函数的第二个参数, 即: 传送字节数来表示, 可以选择任何一个不可能出现的值, 比如任何一个跟它的初始值不相等的负数.  当然, 如果你通过单句柄数据或单IO数据来传递也是可以的. 而发生错误的这个缓冲区指针, 我们是必须要通过单句柄数据或单IO数据来传递的. 但是, 从整个缓冲区的管理机制上来说, 我不推荐这样的离散缓冲区机制, 我的建议是: 把收发缓冲区或数据队列与相应的clientsocket对象相绑定, 释放操作写在该对象的析构函数里, 这样当释放clientsocket对象时就释放了这些缓冲区.<br /><br />ok, 这样一来, 在get函数里, 有三种情况需要执行释放逻辑:<br />1. get的返回值为FALSE;<br />2. 传送字节数为0;<br />3. 接收到刚才我们post的那个错误类型消息.<br /><br />把释放操作全放在get函数里以后, 对释放操作的处理, 就比较统一了. 当然, 为了实现真正的线性化和元子化, 在释放操作的最终执行逻辑上, 还需要对释放代码加锁以实现线程互斥(当然, 这是在你开了多个工作者线程的情况下). <img src ="http://www.cppblog.com/tx7do/aggbug/5972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-04-20 18:52 <a href="http://www.cppblog.com/tx7do/archive/2006/04/20/5972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll与iocp的异同之处 </title><link>http://www.cppblog.com/tx7do/archive/2006/04/20/5973.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Thu, 20 Apr 2006 10:52:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/archive/2006/04/20/5973.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/5973.html</wfw:comment><comments>http://www.cppblog.com/tx7do/archive/2006/04/20/5973.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/5973.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/5973.html</trackback:ping><description><![CDATA[
		<p>本文作者：sodme<br />本文出处：<a href="http://blog.csdn.net/sodme"><font color="#666699">http://blog.csdn.net/sodme</font></a><br />声明：本文可以不经作者同意任意转载、复制、引用。但任何对本文的引用，均须注明本文的作者、出处以及本行声明信息。<br /><br />目前国内的网游研发，在服务器使用的开发平台方面，win和linux的比例各占多少，我一时半会也没有准确数据，但从我了解的这么多公司情况来看，用win系统的还是比较多一点，这些企业一般都是比较单纯的网游公司，而用linux的则多数是一些传统的互联网公司，比如网易和腾讯。<br /><br />网游服务器用win还是linux，向来都是大家关注的话题。我想，原因可能很多，但此处不想过多论述这个问题，为避免多费口舌，我还是明确表明一下自己的观点：我是推荐用linux作开发的，虽然我也是刚转来作linux平台下的开发。<br /><br />那么，说具体一点。但凡作过比较深入的网络编程的人，都会知道，在win平台下，高效的IO模型是IOCP，而在linux底下则是epoll。那么，epoll与iocp之间到底有哪些异同之处呢？<br /><br />首先，我们看一下它们相同的地方。<br /><br />两者都是处理异步IO的高效模型，这种高效，除了“异步处理”这个共同的特征之外，二者都可以通过指针携带应用层数据：在IOCP里，应用层数据可以通过单句柄数据和单IO数据来与IOCP底层通信；而在epoll里，可以通过epoll_data里的"void *ptr"来传递。这是一种很重要的思想，也是它们高效的原因所在：当事件的通知到来时，它不仅告诉你发生了什么样的事件，还同时告诉这次事件所操作的数据是哪些。<br /><br />那么，epoll和iocp到底又有什么不同呢？<br /><br />以我目前粗浅的使用经验来看，至少可以得到以下结论：<br /><br />1.iocp是在IO操作完成之后，才通过get函数返回这个完成通知的；而epoll则<font color="#ff0000">不是在IO操作完成之后才通知你</font>，它的工作原理是，你如果想进行IO操作时，先向epoll查询是否可读或可写，如果处于可读或可写状态后，epoll会通过epoll_wait函数通知你，此时你再进行进一步的recv或send操作。<br /><br />2.在1的基础上，我们其实可以看到，epoll仅仅是一个异步事件的通知机制，其本身并不作任何的IO读写操作，它只负责告诉你是不是可以读或可以写了，而具体的读写操作，还要应用层自己来作；但iocp的封装就要多一些，它不仅会有完成之后的事件通知，更重要的是，它同时封装了一部分的IO控制逻辑。从这一点上来看，iocp的封装似乎更全面一点，但是，换个角度看，epoll仅提供这种机制也是非常好的，它保持了事件通知与IO操作之间彼此的独立性，使得epoll的使用更加灵活。<br /><br />这只是我初步使用epoll开发过程中的体会，以后有更深的体会时还会发上来跟大家分享。</p>
<img src ="http://www.cppblog.com/tx7do/aggbug/5973.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-04-20 18:52 <a href="http://www.cppblog.com/tx7do/archive/2006/04/20/5973.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>