acceptex

时间:2024-06-30 18:51:01编辑:coo君

为什么完成端口是win

1. 完成端口的实质
个人感觉完成端口就是一个“闹钟”,它可以被当做任何事情的提醒设备。比如说,(没有试过)ReadFile可以异步操作,可以将这个异步操作的完成这个事件的提醒交给完成端口来完成。所以,原文中所使用的完成端口,只是将这个“闹钟”用在了网络传输的提醒上。这也就说明了,尽管我们可以使用完成端口来完成各种提醒,但是主框架仍然是Socket那套流程。
为了更清晰地表达,请看下面的描述(从上往下)。这是作者的代码的API调用流程。其实我不太喜欢作者的第三部分的那些流程图,因为其中用了太多的自定义函数而掩盖了底层的API调用。现在我把他还原。
初始化库,WSAStart()
创建完成端口,CreateIoCompletionPort(-1,...);
创建工作线程,CreateThread()
创建listen socket,WSASocket
绑定listen socket 至完成端口,CreateIoCompletionPort(socket, …)
listen socket与本地端口绑定 ,bind()
开始监听,listen()
向listen socket投递accept请求,AcceptEx()
干其他事情或者等待结束命令
通信结束,通知Worker线程们退出,PostQueuedCompletionStatus()

至于Working 线程的做法和原先一样。
可以看到,尽管我们说完成端口的性能是最好的,然而它只是一个时间走得特别准的“闹钟”而已。 网络编程的基本框架还是原来的那套。

2. 关于Accept投递和WSARev投递
整偏博文里我看得最头痛的就是这个“投递”。它到底是什么?
刚才说了,这里只是把完成端口作为“提醒”机制。具体来说,就是listen之后,通常的调用是如下面所示的
While(1)
{
Socket = Accept();
_beginthreadex(…); // 传入socket
}

只不过,这样的调用就是阻塞型调用。而使用完成端口的话,就是将这个Accept(当然,源代码中使用更复杂的AcceptEx,其实差不多的)的调用(其实在内部,就相当于一个要求被accept的请求)委托给完成端口,然后主线程马上返回,并且让后者来提醒之前已经创建好的、在线程函数中调用了GetQueuedCompletionStatus函数的某个线程。当然,至于是哪个线程拿到了,那就是系统调度问题,这对我们来说是黑盒的。这就是那个AcceptEx投递的含义了。说得通俗点,所谓的Accept投递,就是告诉完成端口,“我——listen socket,向你提交一个请求,如果你有客户端的连结请求了,请立即告诉我”。所以可以说,AcceptEx投递就是listen socket的一个IO请求而已。
同样的道理,如果我们不是让完成端口去通知Accept完成,而是去让它通知Receive操作完成,那就是原文中的“投递WSARev操作”了。显然,这是两种操作。

3. 关于工作线程中的一些关键点
那么作者在“详细”那一节第六步中说的那个难点是怎么回事呢?我的理解是这样的。负责监听完成端口的线程(就是之前已经创建好的、在线程函数中调用了GetQueuedCompletionStatus函数的某个线程),它们是在GetQueuedCompletionStatus返回之前是不知道到底是那个操作被“闹钟”了,再加上之前说过的,具体是哪个线程被“闹钟”是我们所不知道的。所以说,你根本就无法判断某个线程会被用做干嘛。所以就需要一种机制来分别了。作者采用的方法是,自定义一些说明性的信息,然后在投递的时候附带送过来,这样在接受的时候就可以通过这些附加信息加以区别了。这就是那个PER_SOCKET_CONTEXT和PER_IO_CONTEXT的作用了。

具体来说,如果是为投递Accept而准备的闹钟,那么某个线程GetQueuedCompletionStatus返回之后,将会获得和Accept投递有关的Socket和此请求的附加信息。和accept投递有关的socket是哪个呢?自然是用于做listen的那个socket(GetQueuedCompletionStatus函数的第三个参数返回它的上下文——其实就是一个和它相匹配的补充性结构,那是在将socket和完成端口绑定时传入的第三个参数)。
注意到一个socket上面可以有很多个请求(类比一个客户端/服务器连结上可以发送多次数据,每一次发送数据都需要通过一个“发送数据请求”),从而在listen socket上面,我们之前投递了多个acceptEx请求,那么到底是哪个请求到来了呢?其实对于AcceptEx请求而言,哪一个并不那么重要,反正大家的工作都是创建一个客户端/服务器连结socket,然后进行通信。然而如果真的重要了,那么就需要这个值了(比如说不同连结需要不同操作之类)。具体来说,GetQueuedCompletionStatus
倒数第二个参数OVERLAPPED*,返回的就是和这个请求相关的信息。它其实就是我们在投递这个请求的时候所附加给完成端口的说明性材料。注意看,这是一个OVERLAPPED结构指针,应该和我们自定义的PER_IO_CONTEXT没有关系。实则不然,回顾CIOCPModel::_PostAccept里面AcceptEx调用的最后一个参数实际上是,&pAcceptIoContext->m_Overlapped。也就是说这里传回来的是PER_IO_CONTEXT的第一个成员m_Overlapped的地址。通过位于_WorkThread函数中的风骚宏CONTAINING_RECORD
就能够得到这个PER_IO_CONTEXT的首地址啦。
另外,如果投递的是WSARev请求呢?这个附加信息怎么穿进去?一样的啦,就是通过WSARev函数的OVERLAPPED结构体参数。这个结构体的实际作用,就是在异步IO操作之后,提供给你一些信息的。我们可以通过继承这个结构(或者和这里的做法一致——采用风骚宏CONTAINING_RECORD)来拓展它,让它包含我们需要的信息。
接下去的事情就好办了,根据自定义结构PER_IO_CONTEXT的成员m_OpType了解一些信息,知道这个回应,到底是listen socket的accept请求的回应(ACCEPT_POSTED)还是真正的客户端/服务器连结socket的发送或者接收请求的回应(RECV_POSTED和SEND_POSTED)。

如果是对accept请求的回应,那么返回的socket context和io context都是这个accept请求的,所以作者强调在_DoAccept中是一定不能够动这两个结构的。然而,如果是普通连结的发送/接收请求的回应,那么当然就是另外一回事情了。


IOCP运行一段时间后,AcceptEx不返回,该怎么处理

IOCP服务端运行一段时间,客户端连不上了,但Telnet能成功,Connect能执行成功,发送数据失败,有点像是GetQueuedCompletionStatus没返回,但各个工作线程正常.AcceptEx投递时第四个参数有一个数据头大的数据。运行5-6个小时候会出现上述情况,请问题怎么回事,有什解决办法呀?

再加点日志看看,之前遇到过这种情况:
在Windows2003上完成AcceptEX投递后,直接返回错误,而错误处理逻辑中有个分支
没有再次投递足够的AcceptEx出去,导致客户端连不上。
根据listen的第二个参数,用telnet连接此属两个连接,如果超出数量后连接失败,则很有可能是AcceptEx没有被投递


求AcceptEx与完成端口的配合使用代码!加分!

以下是msdn上的例子:


#include
#include "winsock2.h"
#include "mswsock.h"

void main() {
//----------------------------------------
// Declare and initialize variables
WSADATA wsaData;
HANDLE hCompPort;
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
WSAOVERLAPPED olOverlap;

SOCKET ListenSocket, AcceptSocket;
sockaddr_in service;
char lpOutputBuf[1024];
int outBufLen = 1024;
DWORD dwBytes;

//----------------------------------------
// Initialize Winsock
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if( iResult != NO_ERROR )
printf("Error at WSAStartup\n");

//----------------------------------------
// Create a handle for the completion port
hCompPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, (u_long)0, 0 );

//----------------------------------------
// Create a listening socket
ListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): ListenSocket\n");
WSACleanup();
return;
}

//----------------------------------------
// Associate the listening socket with the completion port
CreateIoCompletionPort((HANDLE)ListenSocket, hCompPort, (u_long)0, 0);

//----------------------------------------
// Bind the listening socket to the local IP address
// and port 27015
hostent* thisHost;
char* ip;
u_short port;
port = 27015;
thisHost = gethostbyname("");
ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);

service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip); service.sin_port = htons(port);

if ( bind( ListenSocket,(SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
printf("bind failed\n");
closesocket(ListenSocket);
return;
}

//----------------------------------------
// Start listening on the listening socket
if (listen( ListenSocket, 100 ) == SOCKET_ERROR) {
printf("error listening\n");
}
printf("Listening on address: %s:%d\n", ip, port);

//----------------------------------------
// Load the AcceptEx function into memory using WSAIoctl.
// The WSAIoctl function is an extension of the ioctlsocket()
// function that can use overlapped I/O. The function's 3rd
// through 6th parameters are input and output buffers where
// we pass the pointer to our AcceptEx function. This is used
// so that we can call the AcceptEx function directly, rather
// than refer to the Mswsock.lib library.
WSAIoctl(ListenSocket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&lpfnAcceptEx,
sizeof(lpfnAcceptEx),
&dwBytes,
NULL,
NULL);

//----------------------------------------
// Create an accepting socket
AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (AcceptSocket == INVALID_SOCKET) {
printf("Error creating accept socket.\n");
WSACleanup();
return;
}

//----------------------------------------
// Empty our overlapped structure and accept connections.
memset(&olOverlap, 0, sizeof(olOverlap));

lpfnAcceptEx(ListenSocket,
AcceptSocket,
lpOutputBuf,
outBufLen - ((sizeof(sockaddr_in) + 16) * 2),
sizeof(sockaddr_in) + 16,
sizeof(sockaddr_in) + 16,
&dwBytes,
&olOverlap);

//----------------------------------------
// Associate the accept socket with the completion port
CreateIoCompletionPort((HANDLE)AcceptSocket, hCompPort, (u_long)0, 0);

//----------------------------------------
// Continue on to use send, recv, TransmitFile(), etc.,.
...

}

下载:http://www.microsoft.com/downloads/details.aspx?FamilyId=AF0A6060-6566-408F-9F11-EA2C80B8CAA0&displaylang=en


如何查看Apache错误日志

检查错误方法:进入cmd 然后进入 Apache安装目录(具体为你自己的安装目录)。
(引号中的Apache2修改为你的Apache服务名,我的是2.2.4版,服务名就是Apache2,可以到计算机服务里找)
提示133行有问题时(提示:Syntax error on line 133 of ………..),打开Apache安装目录\conf\httpd.conf 找到第133行的ServerAdmin (没有工具确定行数的按ctrl+F用”ServerAdmin”关键字查找,一般会有两个,下面那个就是)这行在其后空一格,随便加个名字,例如,加上abc后,就可以解决了.
如果是端口占用(提示:(OS 10048)通常每个套接字地址(协议/网络地址/端口)只允许使用一次。),先退出Apache,在httpd.conf中搜索Listen 80 ,将80改成8080或者别的端口号,重新运行一下Apache,这下应该能够启动了.


apache假死,重启就可以访问,怎么办

参考:netsh winsock reset命令,作用是重置 Winsock 目录。如果一台机器上的Winsock协议配置有问题的话将会导致网络连接等问题,就需要用netsh winsock reset命令来重置Winsock目录借以恢复网络。
这个命令可以重新初始化网络环境,以解决由于软件冲突、病毒原因造成的参数错误问题。 netsh是一个能够通过命令行操作几乎所有网络相关设置的接口,比如设置IP,DNS,网卡,无线网络等,Winsock是系统内部目录,Winsock是Windows网络编程接口,winsock工作在应用层,它提供与底层传输协议无关的高层数据传输编程接口,reset是对Winsock的重置操作。
当执行完winsock的命令重启计算机后,需要重新配置IP。


上一篇:西班牙王后索菲亚

下一篇:saintpaulon