假设一种情况我发出两次WSASend,一次投递10字节数据,里面全是a,第二次投递10字节数据,里面全是b我们期待的顺序应该是一共发出去20字节的数据,前10字节是a,后10字节是b但是由于WSASend有可能投递不完整,可能第一次只投递出去了5字节,我们在IOCP线程中捕获到了投递不完整,然后投递剩余的数据~~那么最终的顺序是不是就变成了,一共20字节数据,前5字节是a,然后10字节的b,最后5字节为a由于这种情况不好模拟,所以来问问,我分析的对吗?
调试欢乐多
---
同意,包要自己在应用层设计和解析.
我一直以为iocp是以队列方式处理所有的io的!
没看懂你的第三种方法,我看到的都类似第一种方法。
后天,我干脆把dwIosize<len的情况作坏包丢弃了。因为这种机率实在太小。
这种情况还是很多的,估计你在局域网用吧?
----------------------------
接收不完整是正常的 现在说的是发送一定数据量的数据时能不能一次发送完 也就是GetQueuedCompletionStatus返回时dwIoSize是不是发送的数据的长度
而执行IO的先后取决于系统的IO排队。一般来讲早的IO请求先执行,一个IRP处理完再处理下一个。也就是说,无论如何也不可能出现数据交错。
什么意思?也就是说不会出现我描述的那种情况?
不会出现这种可能,所谓的乱序只是针对多个线程而言,但即使是多线程,先投递的WSASend也会在完成后才会执行后投递的WSASend,所以这种担心是没有必要的,即:数据不会被截断只不过你需要注意在多个线程时如何来保证这种先后顺序而已 ---------------------------------------------------------------------------
错了,在SOCKET里有一个系统缓冲区,假设这个缓冲区是4K,现在是空的,我现在A线程(发送线程)调用WSASend发送8K的数据,那么只会发送4K出去,在工作线程中,调用的GET函数的返回, 告诉你只发送出去4K,你在这个工作线程里,继续准备发送剩下的4K。这时候,A线程又在这个SOCKET上发送了一个4K的数据,并且顺利发出,A线程这个4K数据发送完成后,你的工作线程才发送第一次发送剩下的4K数据,这样,到了收数据的那边,本来正确的是8K+4K,现在成了4K+4K+4K数据了。你可以参考一下SDK里IOCP的那个例子里,他处理的上一次没发送完成的数据。
这是那个SDK里的一段代码:
case ClientIoWrite: //
// a write operation has completed, determine if all the data intended to be
// sent actually was sent.
//
lpIOContext->IOOperation = ClientIoWrite;
lpIOContext->nSentBytes += dwIoSize;
dwFlags = 0;
if( lpIOContext->nSentBytes < lpIOContext->nTotalBytes ) { //
// the previous write operation didn't send all the data,
// post another send to complete the operation
//
buffSend.buf = lpIOContext->Buffer + lpIOContext->nSentBytes;
buffSend.len = lpIOContext->nTotalBytes - lpIOContext->nSentBytes;
nRet = WSASend (lpPerSocketContext->Socket, &buffSend, 1,
&dwSendNumBytes, dwFlags, &(lpIOContext->Overlapped), NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("WSASend() failed: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) Send partially completed (%d bytes), Recv posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
} else { //
// previous write operation completed for this socket, post another recv
//
lpIOContext->IOOperation = ClientIoRead;
dwRecvNumBytes = 0;
dwFlags = 0;
buffRecv.buf = lpIOContext->Buffer,
buffRecv.len = MAX_BUFF_SIZE;
nRet = WSARecv(lpPerSocketContext->Socket, &buffRecv, 1,
&dwRecvNumBytes, &dwFlags, &lpIOContext->Overlapped, NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("WSARecv() failed: %d\n", WSAGetLastError());
CloseClient(lpPerSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) Send completed (%d bytes), Recv posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
}
break;
|'aaaaa'|A2--->C|'aaaaa'|A1本计划发送10个'a',侧是第一次send只发出了5个,继而A2赶到,发送了5个'b',并且全部发送成功,继而A1才发送后5个'a',那么本意发送的是'aaaaaaaaaabbbbb',而接收后就成了
'aaaaa'+'bbbbb'+'aaaaa'.
对于这种情况完全是有可能的。只不过是这种设计本身就存在问题。在这个问题上面,最好是为每个连接使用一个发送队列。对于整个发送队列确实也至少理论上是阻塞。而这样子设计对于整个服务来讲仍然是合理的,毕竟整个服务考虑的是众连接对于服务器CPU和IO资源的最大利用率,而并不关心个别连接在这当中的效率如何,至少次之,并且即便是使用了两个线程发送,那么对于这个插队的设计,也需要进步一讨论可行性。不仅会影响其它连接的利益,同时对本连接而言也是无利可图的,并且在服务器的设计上来说,也是不符合架构设计的。没有哪个服务设计的初衷是为了某一个连接(客户)而设计的,否则就不需要使用IOCP了,而直接使用单连接去维护。即使要挤那么不如一次就单一向一个客户开放。
单一线程如果出现插队,就是你程序设计的问题,为什么第一次的数据没发完就去发第二次呢?
如果安顺序投递不会出现插队
也就是最多出现aaaaa bbbbb的情况
单一线程如果出现插队,就是你程序设计的问题,为什么第一次的数据没发完就去发第二次呢?
如果安顺序投递不会出现插队==================================
请注意是多个线程单一客户,并不是单一线程出现插队,单一线程是不可能插队的。
不会的,发送顺序肯定和你投递顺序一致的,这个是系统保证的,但完成通知的次序是不保证和投递次序一致的。
同意上面的观点,这个是微软的"WINDOWS网络编程(第2版)"上面的原话.
这个问题与并不需要与IOCP关联起来。
如果第二次的数据发出去了,那么第一次没发完的就没了,不可能出现在第二次的后面
也就是最多出现aaaaa bbbbb的情况
不会aaaaa bbbbb aaaaa