关于接受连接
服务程序最常做的一个事情是接受客户端的连接。AcceptEx函数是Winsock API中唯一可以使用重叠IO方式接受Socket连接的函数。AccpetEx要求一个传入一个socket 作为它的参数。普通的同步accept函数,新的SOCKET是作为返回值得到的。AcceptEx函数作为一个重叠操作,接收Socket应该提前被创建(但不需要绑定和或连接),并传入此API。
(AcceptEx原形,加粗的即为需要传入的socket
BOOL AcceptEx(
SOCKET sListenSocket,
SOCKET sAcceptSocket,
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
);
AcceptEx函数是Winsock API中唯一可以使用重叠IO方式接受Socket连接的函数。AccpetEx要求一个传入一个socket 作为它的参数。普通的同步accept函数,新的SOCKET是作为返回值得到的。AcceptEx函数作为一个重叠操作,接收Socket应该提前被创建(但不需要绑定和或连接),并传入此API。
(AcceptEx原形,加粗的即为需要传入的socket
BOOL AcceptEx(
SOCKET sListenSocket,
SOCKET sAcceptSocket,
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
);
AcceptEx原形,加粗的即为需要传入的socket
BOOL AcceptEx(
SOCKET sListenSocket,
SOCKET sAcceptSocket,
PVOID lpOutputBuffer,
DWORD dwReceiveDataLength,
DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength,
LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped
);
)
使用AcceptEx的例程可能是这个样子的:
AcceptEx的例程可能是这个样子的:
do {
-Wait for a previous AcceptEx to complete //等待前一个AcceptEx完成
等待前一个AcceptEx完成
-Create a new socket and associate it with the completion port //创建一个新的Socket并将其关联
创建一个新的Socket并将其关联
//到完成端口
到完成端口
-Allocate context structure etc. //初始化相关的环境信息结构
初始化相关的环境信息结构
-Post an AcceptEx request. //进入AcceptEx请求。
}while(TRUE);
进入AcceptEx请求。
}while(TRUE);
while(TRUE);
一个服务器一直具备足够的AcceptEx调用,这样就可以立刻响应客户机的连接。AcceptEx操作的数量取决于服务器的策略。如果要满足高连接率(比如大量的短暂连接或爆发性的流量)的话,当然比不常发生连接的程序需要更多的AcceptEx入口。聪明的策略就是根据流量改变AcceptEx调用的数量,而避免只使用一个确定的数目。
在Win2000上,Winsock提供了一些帮助,用来判断AcceptEx调用的数量是否跟不上需要。当创建一个监听Socket之后,使用WSAEventSelect函数把它和一个FD_ACCEPT事件关联,如果没有accept未决的调用正在进行,一旦有请求到来,该事件(FD_ACCEPT)就会发生。因此此事件可以用来告诉开发人员:还需要进行更多的AcceptEx操作,或者由此探测到一个有异常行为的远端实体。注意:此机制在NT上是无效的。
使用AcceptEx的显著好处是:在一次连接中就可以获取客户端的数据,见AcceptEx的lpOutputBuffer参数。这意味着如果客户端连接并立刻发送数据的话,AcceptEx将在客户端连接成功和数据发送之后才完成。这个功能同时导致的问题是:AcceptEx必须等待数据接受完成才能返回。因为一个带等待数据接受完成才能返回。因为一个带Output缓冲的AcceptEx函数并非一个“原子”操作,而是两步的过程:接受连接和等待数据。所以程序在数据接受之前并不会知道连接成功。当然客户端也可以连接到服务器而不马上发送数据,如果这样的连接过多,服务器将开始拒绝合法的连接,因为没有可用的未决的Accept操作入口。这也是一种常用的方法,通过拒绝访问,防止恶意攻击和海量连接。
在正在接受连接的线程中,可以检查AcceptEx调用传入的socket,调用getsockopt检查其SO_CONNECT_TIME,该值返回的是socket连接的时间,没有连接的时候返回-1。
根据WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
AcceptEx调用,这样就可以立刻响应客户机的连接。AcceptEx操作的数量取决于服务器的策略。如果要满足高连接率(比如大量的短暂连接或爆发性的流量)的话,当然比不常发生连接的程序需要更多的AcceptEx入口。聪明的策略就是根据流量改变AcceptEx调用的数量,而避免只使用一个确定的数目。
在Win2000上,Winsock提供了一些帮助,用来判断AcceptEx调用的数量是否跟不上需要。当创建一个监听Socket之后,使用WSAEventSelect函数把它和一个FD_ACCEPT事件关联,如果没有accept未决的调用正在进行,一旦有请求到来,该事件(FD_ACCEPT)就会发生。因此此事件可以用来告诉开发人员:还需要进行更多的AcceptEx操作,或者由此探测到一个有异常行为的远端实体。注意:此机制在NT上是无效的。
使用AcceptEx的显著好处是:在一次连接中就可以获取客户端的数据,见AcceptEx的lpOutputBuffer参数。这意味着如果客户端连接并立刻发送数据的话,AcceptEx将在客户端连接成功和数据发送之后才完成。这个功能同时导致的问题是:AcceptEx必须等待数据接受完成才能返回。因为一个带等待数据接受完成才能返回。因为一个带Output缓冲的AcceptEx函数并非一个“原子”操作,而是两步的过程:接受连接和等待数据。所以程序在数据接受之前并不会知道连接成功。当然客户端也可以连接到服务器而不马上发送数据,如果这样的连接过多,服务器将开始拒绝合法的连接,因为没有可用的未决的Accept操作入口。这也是一种常用的方法,通过拒绝访问,防止恶意攻击和海量连接。
在正在接受连接的线程中,可以检查AcceptEx调用传入的socket,调用getsockopt检查其SO_CONNECT_TIME,该值返回的是socket连接的时间,没有连接的时候返回-1。
根据WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
Win2000上,Winsock提供了一些帮助,用来判断AcceptEx调用的数量是否跟不上需要。当创建一个监听Socket之后,使用WSAEventSelect函数把它和一个FD_ACCEPT事件关联,如果没有accept未决的调用正在进行,一旦有请求到来,该事件(FD_ACCEPT)就会发生。因此此事件可以用来告诉开发人员:还需要进行更多的AcceptEx操作,或者由此探测到一个有异常行为的远端实体。注意:此机制在NT上是无效的。
使用AcceptEx的显著好处是:在一次连接中就可以获取客户端的数据,见AcceptEx的lpOutputBuffer参数。这意味着如果客户端连接并立刻发送数据的话,AcceptEx将在客户端连接成功和数据发送之后才完成。这个功能同时导致的问题是:AcceptEx必须等待数据接受完成才能返回。因为一个带等待数据接受完成才能返回。因为一个带Output缓冲的AcceptEx函数并非一个“原子”操作,而是两步的过程:接受连接和等待数据。所以程序在数据接受之前并不会知道连接成功。当然客户端也可以连接到服务器而不马上发送数据,如果这样的连接过多,服务器将开始拒绝合法的连接,因为没有可用的未决的Accept操作入口。这也是一种常用的方法,通过拒绝访问,防止恶意攻击和海量连接。
在正在接受连接的线程中,可以检查AcceptEx调用传入的socket,调用getsockopt检查其SO_CONNECT_TIME,该值返回的是socket连接的时间,没有连接的时候返回-1。
根据WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
AcceptEx的显著好处是:在一次连接中就可以获取客户端的数据,见AcceptEx的lpOutputBuffer参数。这意味着如果客户端连接并立刻发送数据的话,AcceptEx将在客户端连接成功和数据发送之后才完成。这个功能同时导致的问题是:AcceptEx必须等待数据接受完成才能返回。因为一个带等待数据接受完成才能返回。因为一个带Output缓冲的AcceptEx函数并非一个“原子”操作,而是两步的过程:接受连接和等待数据。所以程序在数据接受之前并不会知道连接成功。当然客户端也可以连接到服务器而不马上发送数据,如果这样的连接过多,服务器将开始拒绝合法的连接,因为没有可用的未决的Accept操作入口。这也是一种常用的方法,通过拒绝访问,防止恶意攻击和海量连接。
在正在接受连接的线程中,可以检查AcceptEx调用传入的socket,调用getsockopt检查其SO_CONNECT_TIME,该值返回的是socket连接的时间,没有连接的时候返回-1。
根据WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
AcceptEx调用传入的socket,调用getsockopt检查其SO_CONNECT_TIME,该值返回的是socket连接的时间,没有连接的时候返回-1。
根据WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
WSAEventSelect机制所带来的特性,我们可以很容易的判断是否应该检查传到AcceptEx函数的socket句柄的连接时间。如果在一定时间里,AcceptEx没有从某个连接中收到数据,AcceptEx可以通过关闭该socket来断开连接。在不紧急的情况下,程序不应该关闭一个AcceptEx里处于未连接状态的socket ,因为系统考虑到性能问题,关联在AcceptEx上的内核态数据结构不会被释放,直到一个新的连接到来或监听socket本身都关闭了。
乍看起来,一个发出AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
AcceptEx请求的线程同时也可以是一个关联在完成端口上,并且处理其他完成IO事件的工作线程。然而,最好不要设计这样一个线程。在winsocket2的层次结构上有一个副作用,那就是一个socket/WSASocket API的开销是相当可观的,每个AccepEx都需要创建一个新的socket,所以最好创建一个单独的跟其他IO处理无关的线程来调用AcceptEx。当然,你还可以利用这个线程来进行其他的工作如创建事件log
关于AcceptEx要注意的最后一个事情是:Winsock2的其他供应商不一定会实现AcceptEx函数。同样情况也包括的其他Microsoft的特定APIs如TransmitFile,GetAcceptExSockAddrs以及其他Microsoft将在以后版本的windows里。在运行WinNT和Win2000的系统上,这些APIs在Microsoft提供的DLL(mswsock.dll)里实现,可以通过链接mswsock.lib或者通过WSAioctl的SIO_GET_EXTENSION_FUNCTION_POINTER操作动态调用这些扩展APIs.
AcceptEx要注意的最后一个事情是:Winsock2的其他供应商不一定会实现AcceptEx函数。同样情况也包括的其他Microsoft的特定APIs如TransmitFile,GetAcceptExSockAddrs以及其他Microsoft将在以后版本的windows里。在运行WinNT和Win2000的系统上,这些APIs在Microsoft提供的DLL(mswsock.dll)里实现,可以通过链接mswsock.lib或者通过WSAioctl的SIO_GET_EXTENSION_FUNCTION_POINTER操作动态调用这些扩展APIs.
未获取函数指针就调用函数(如直接连接mswsock..lib并直接调用AcceptEx)的消耗是很大的,因为AcceptEx 实际上是存在于Winsock2结构体系之外的。每次应用程序常试在服务提供层上(mswsock之上)调用AcceptEx时,都要先通过WSAIoctl获取该函数指针。如果要避免这个很影响性能的操作,应用程序最好是直接从服务提供层通过WSAIoctl先获取这些APIs的指针。
mswsock..lib并直接调用AcceptEx)的消耗是很大的,因为AcceptEx 实际上是存在于Winsock2结构体系之外的。每次应用程序常试在服务提供层上(mswsock之上)调用AcceptEx时,都要先通过WSAIoctl获取该函数指针。如果要避免这个很影响性能的操作,应用程序最好是直接从服务提供层通过WSAIoctl先获取这些APIs的指针。
TransmitFile 和TransmitPackets函数
和TransmitPackets函数
Winsock提供了两个专为文件和内存数据传输而优化过的函数。TransmitFile API在WinNT和Win2000均有效,而TransmitPackets作为一个新的扩展函数,将在未来版本的windows里实现。
提供了两个专为文件和内存数据传输而优化过的函数。TransmitFile API在WinNT和Win2000均有效,而TransmitPackets作为一个新的扩展函数,将在未来版本的windows里实现。
TransmitFile可以把文件的内容通过socket传输。一般情况下,如果应用程序通过socket传输文件,首先要用CreateFile打开文件,并循环调用ReadFile和WSASend函数,读取一段数据然后发送,直到整个文件发送完毕。这样的效率很低,因为ReadFile和WSASend调用都需要系统在用户态和核心态之间进行转换。TransmitFile则只需要知道需要传输的文件句柄和要传输的字节数,只有CreateFile打开文件获得句柄这个向核心态跃迁的这一个额外开销。如果你的程序需要通过socket发送大量文件,建议使用此函数。
函数的原形如下:
可以把文件的内容通过socket传输。一般情况下,如果应用程序通过socket传输文件,首先要用CreateFile打开文件,并循环调用ReadFile和WSASend函数,读取一段数据然后发送,直到整个文件发送完毕。这样的效率很低,因为ReadFile和WSASend调用都需要系统在用户态和核心态之间进行转换。TransmitFile则只需要知道需要传输的文件句柄和要传输的字节数,只有CreateFile打开文件获得句柄这个向核心态跃迁的这一个额外开销。如果你的程序需要通过socket发送大量文件,建议使用此函数。
函数的原形如下:
BOOL TransmitFile(
SOCKET hSocket,
HANDLE hFile,
DWORD nNumberOfBytesToWrite,
DWORD nNumberOfBytesPerSend,
LPOVERLAPPED lpOverlapped,
LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,
DWORD dwFlags
);
TransmitPackets API比TransmitFile API更进一步,允许调用者一次指定多个文件句柄和内存缓冲区,并进行传输。原形如下:
比TransmitFile API更进一步,允许调用者一次指定多个文件句柄和内存缓冲区,并进行传输。原形如下:
BOOL TransmitPackets(
SOCKET hSocket,
LPTRANSMIT_PACKET_ELEMENT lpPacketArray,
DWORD nElementCount,
DWORD nSendSize,
LPOVERLAPPED lpOverlapped,
DWORD dwFlags
);
lpPacketArray包含结构体的数组。每个入口点指定一个需要被传输的文件句柄或内存缓冲,该结构的成员如下:
包含结构体的数组。每个入口点指定一个需要被传输的文件句柄或内存缓冲,该结构的成员如下:
typedef struct _TRANSMIT_PACKETS_ELEMENT {
DWORD dwElFlags;
DWORD cLength;
union {
struct {
LARGE_INTEGER nFileOffset;
HANDLE hFile;
};
PVOID pBuffer;
};
} TRANSMIT_FILE_BUFFERS;
各成员的名字都是自解释的。dwEIFlags成员指明结构体里的元素是一个文件句柄(TF_ELEMENT_FILE)还是一个内存缓冲(TF_ELEMENT_MEMORY)。cLength成员表示要传输的字节数(对于文件句柄,0则表示文件内所有的数据)。一个未命名的联合体(union)包含内存缓冲指针或文件句柄(以及指定的偏移量)。
使用这两个函数的其他好处是,你可以通过指定TF_REUSE_SOCKET标志(必须同时指定TF_DISCONNECT标志)重用socket句柄。一旦API函数完成数据传输,连接就会在传输点的层次上断开,然后该socket就可以被AcceptEx重新使用。这样就可以减少反复创建socket和的次数,优化了效率。
使用这两个函数要注意的是,在WinNT Workstation版本或win2000 Professional版本上,并不能实现优化性能。必须在winNT,win2000 Server ,Win2000 Advanced Server 或Win2000 Data Center版本上才能实现。
dwEIFlags成员指明结构体里的元素是一个文件句柄(TF_ELEMENT_FILE)还是一个内存缓冲(TF_ELEMENT_MEMORY)。cLength成员表示要传输的字节数(对于文件句柄,0则表示文件内所有的数据)。一个未命名的联合体(union)包含内存缓冲指针或文件句柄(以及指定的偏移量)。
使用这两个函数的其他好处是,你可以通过指定TF_REUSE_SOCKET标志(必须同时指定TF_DISCONNECT标志)重用socket句柄。一旦API函数完成数据传输,连接就会在传输点的层次上断开,然后该socket就可以被AcceptEx重新使用。这样就可以减少反复创建socket和的次数,优化了效率。
使用这两个函数要注意的是,在WinNT Workstation版本或win2000 Professional版本上,并不能实现优化性能。必须在winNT,win2000 Server ,Win2000 Advanced Server 或Win2000 Data Center版本上才能实现。
TF_REUSE_SOCKET标志(必须同时指定TF_DISCONNECT标志)重用socket句柄。一旦API函数完成数据传输,连接就会在传输点的层次上断开,然后该socket就可以被AcceptEx重新使用。这样就可以减少反复创建socket和的次数,优化了效率。
使用这两个函数要注意的是,在WinNT Workstation版本或win2000 Professional版本上,并不能实现优化性能。必须在winNT,win2000 Server ,Win2000 Advanced Server 或Win2000 Data Center版本上才能实现。
WinNT Workstation版本或win2000 Professional版本上,并不能实现优化性能。必须在winNT,win2000 Server ,Win2000 Advanced Server 或Win2000 Data Center版本上才能实现。
来实现一个服务方案
在前几章里,我们介绍了一些有益于改善性能提高扩展性的APIs和方法,以及可能遇到的资源瓶颈。这对你有用吗?当然,首先取决于你的服务端和客户端的设计。在设计时,你对服务端和客户端的控制越得力,你就能更好的避免瓶颈
让我们来看一种简单的用例,在这个用例里我们设计一个服务器,这个服务器处理客户端的连接,然后客户端发送一次数据,并期待服务端的回应,然后客户端断开连接。
我们的设计是:服务器创建一个监听socket,并和一个完成端口相关联,然后创建和CPU同等数量的工作线程,以及一个专门用来进行AcceptEx调用的线程。既然我们知道客户端一旦连接马上就会发送数据,那么准备一个接受缓冲区会有利于工作的进行。当然,不要忘记经常的检查正在连接的socket的SO_CONNECT_TIME值,避免死连接。
本设计中重要的一项是决定需要显形调用多少个AcceptEx。因为每个AcceptEx操作都需要一个接收缓冲区,大量的页面将被锁定(还记得每个重叠操作都会消耗一些非分页内存,并且会将一些数据缓冲锁定到内存里吗)。没有公式和具体的准则指导如何确定究竟允许多少个AcceptEx操作。最好的方案就是使这个数目成为可调的,通过性能测试,寻找一个在典型的环境下最好的值
现在已经确定服务器是如何处理连接的了,下一步就是发送数据。影响发送数据的重要因素就是你期望服务器能够并发的处理连接数。一般来说,服务器应该限制并发的连接数量,以及显式的send调用。越多的连接数意味着越多的非分页内存的使用,并发的send调用也应该被限制,避免冲击系统的可分页内存锁定极限。连接数和并发的send调用限制也都应该是程序可调节的。
APIs和方法,以及可能遇到的资源瓶颈。这对你有用吗?当然,首先取决于你的服务端和客户端的设计。在设计时,你对服务端和客户端的控制越得力,你就能更好的避免瓶颈
让我们来看一种简单的用例,在这个用例里我们设计一个服务器,这个服务器处理客户端的连接,然后客户端发送一次数据,并期待服务端的回应,然后客户端断开连接。
我们的设计是:服务器创建一个监听socket,并和一个完成端口相关联,然后创建和CPU同等数量的工作线程,以及一个专门用来进行AcceptEx调用的线程。既然我们知道客户端一旦连接马上就会发送数据,那么准备一个接受缓冲区会有利于工作的进行。当然,不要忘记经常的检查正在连接的socket的SO_CONNECT_TIME值,避免死连接。
本设计中重要的一项是决定需要显形调用多少个AcceptEx。因为每个AcceptEx操作都需要一个接收缓冲区,大量的页面将被锁定(还记得每个重叠操作都会消耗一些非分页内存,并且会将一些数据缓冲锁定到内存里吗)。没有公式和具体的准则指导如何确定究竟允许多少个AcceptEx操作。最好的方案就是使这个数目成为可调的,通过性能测试,寻找一个在典型的环境下最好的值
现在已经确定服务器是如何处理连接的了,下一步就是发送数据。影响发送数据的重要因素就是你期望服务器能够并发的处理连接数。一般来说,服务器应该限制并发的连接数量,以及显式的send调用。越多的连接数意味着越多的非分页内存的使用,并发的send调用也应该被限制,避免冲击系统的可分页内存锁定极限。连接数和并发的send调用限制也都应该是程序可调节的。
socket,并和一个完成端口相关联,然后创建和CPU同等数量的工作线程,以及一个专门用来进行AcceptEx调用的线程。既然我们知道客户端一旦连接马上就会发送数据,那么准备一个接受缓冲区会有利于工作的进行。当然,不要忘记经常的检查正在连接的socket的SO_CONNECT_TIME值,避免死连接。
本设计中重要的一项是决定需要显形调用多少个AcceptEx。因为每个AcceptEx操作都需要一个接收缓冲区,大量的页面将被锁定(还记得每个重叠操作都会消耗一些非分页内存,并且会将一些数据缓冲锁定到内存里吗)。没有公式和具体的准则指导如何确定究竟允许多少个AcceptEx操作。最好的方案就是使这个数目成为可调的,通过性能测试,寻找一个在典型的环境下最好的值
现在已经确定服务器是如何处理连接的了,下一步就是发送数据。影响发送数据的重要因素就是你期望服务器能够并发的处理连接数。一般来说,服务器应该限制并发的连接数量,以及显式的send调用。越多的连接数意味着越多的非分页内存的使用,并发的send调用也应该被限制,避免冲击系统的可分页内存锁定极限。连接数和并发的send调用限制也都应该是程序可调节的。
AcceptEx。因为每个AcceptEx操作都需要一个接收缓冲区,大量的页面将被锁定(还记得每个重叠操作都会消耗一些非分页内存,并且会将一些数据缓冲锁定到内存里吗)。没有公式和具体的准则指导如何确定究竟允许多少个AcceptEx操作。最好的方案就是使这个数目成为可调的,通过性能测试,寻找一个在典型的环境下最好的值
现在已经确定服务器是如何处理连接的了,下一步就是发送数据。影响发送数据的重要因素就是你期望服务器能够并发的处理连接数。一般来说,服务器应该限制并发的连接数量,以及显式的send调用。越多的连接数意味着越多的非分页内存的使用,并发的send调用也应该被限制,避免冲击系统的可分页内存锁定极限。连接数和并发的send调用限制也都应该是程序可调节的。
send调用。越多的连接数意味着越多的非分页内存的使用,并发的send调用也应该被限制,避免冲击系统的可分页内存锁定极限。连接数和并发的send调用限制也都应该是程序可调节的。
在本例的情况里,不必要去取消每个socket的接收缓冲,因为接收事件仅仅在AcceptEx调用中发生。保证每个socket都有一个接收缓冲不会造成什么危害。一旦客户端/服务器在最初的一次请求(由AcceptEx完成)之后进行交互,发送更多的数据,那么取消接收缓冲更是一个很不好的做法。除非你能保证这些数据都是在每个连接的重叠IO接收里完成的。
结束语:
重复:开发一个可扩展的Winsock服务器并非十分困难的。仅仅是开始一个监听socket,接收连接,并且进行重叠发送和接收的IO操作。最大的挑战就是管理系统资源,限制重叠Io的数量,避免内存危机。遵循这几个原则,就能帮助你开发高性能,可扩展的服务程序。
socket的接收缓冲,因为接收事件仅仅在AcceptEx调用中发生。保证每个socket都有一个接收缓冲不会造成什么危害。一旦客户端/服务器在最初的一次请求(由AcceptEx完成)之后进行交互,发送更多的数据,那么取消接收缓冲更是一个很不好的做法。除非你能保证这些数据都是在每个连接的重叠IO接收里完成的。
结束语:
重复:开发一个可扩展的Winsock服务器并非十分困难的。仅仅是开始一个监听socket,接收连接,并且进行重叠发送和接收的IO操作。最大的挑战就是管理系统资源,限制重叠Io的数量,避免内存危机。遵循这几个原则,就能帮助你开发高性能,可扩展的服务程序。
Winsock服务器并非十分困难的。仅仅是开始一个监听socket,接收连接,并且进行重叠发送和接收的IO操作。最大的挑战就是管理系统资源,限制重叠Io的数量,避免内存危机。遵循这几个原则,就能帮助你开发高性能,可扩展的服务程序。