【发布时间】:2018-06-27 20:10:46
【问题描述】:
我使用 WinAPI 为套接字操作编写了 C++ 服务。客户端和服务之间的通信很简单,在连接到服务之后客户端发送一些数据,然后服务很快就会发送响应。
服务端的主套接字(ListenSocket)是这样创建的:
SOCKET ListenSocket = socket(pAddrResult->ai_family, pAddrResult->ai_socktype, pAddrResult->ai_protocol);
/* err checking */
/* Setting socket I/O mode to non-blocking */
u_long nonBlockingMode = 1;
opResult = ioctlsocket(ListenSocket, FIONBIO, &nonBlockingMode);
/* err checking */
主套接字上的选择接受循环:
while(...)
{
if(select(0, &readSet, NULL, NULL, &timeout) == 1)
{
ClientSocket = accept(ListenSocket, NULL, NULL); // SOCKET type, declared in global scope.
/* err checking */
break;
}
}
在中断循环服务开始从ClientSocket 读取并在该服务在此套接字上发送响应之后:
int iSendResult = send(ClientSocket, (const char*) messageBuffer.getData(), messageBuffer.getSize(), 0);
/* err checking */
/* FIX sleep(50) */
int opResult = shutdown(ClientSocket, SD_BOTH);
/* err checking */
closesocket(ClientSocket);
closesocket() 之后进程返回到 select-accept 循环并等待另一个连接。
我的问题是有时客户端接收到的数据不足,并获取“连接已被远程主机关闭”的信息。 经过调试和一些研究,我在 MSDN 上找到了这个注释:
使用带有 SD_SEND 或 SD_BOTH 的 closesocket 或关闭函数会导致在控制通道上发送 RELEASE 信号。由于 ATM 使用单独的信号和数据通道,RELEASE 信号可能会在最后一个数据到达其目的地之前到达远端,从而导致该数据丢失。一种可能的解决方案是在最后发送的数据与 ATM 套接字的 closesocket 或关闭函数调用之间设置足够的延迟。
延迟 你说微软? :( 好的,我已经实现了短暂的延迟 (/* FIX sleep(50) */),问题就消失了。
但这一点都不能让我满意。我想删除这个sleep() 并确保缓冲区在发送后完全刷新,并且我可以安全地shutdown() 和closesocket()。
我试图让主套接字不阻塞,希望send() 会阻塞直到完成,但我错了 - 没有效果。
如果没有sleep(),我还能做些什么来让它工作吗?
【问题讨论】:
-
让客户端关闭连接?
-
客户端是否发送确认数据已收到,然后关闭连接?或者,正如@Sid 所建议的那样;让客户端关闭连接。
-
DisconnectEx和TF_REUSE_SOCKET标志完美运行。你可以用它来代替shutdown。在这种情况下,您也不能关闭但重用相同的套接字 -
@SidS,但是如果客户端保持连接试图执行拒绝服务怎么办? RbMm,谢谢你,我试试这个方法。
-
这实际上在 Winsock 文档中有所介绍:Graceful Shutdown, Linger Options, and Socket Closure
标签: c++ c sockets winapi client-server