【问题标题】:How to deal with short reads with Winsock2?如何用 Winsock2 处理短读?
【发布时间】:2013-03-02 20:26:03
【问题描述】:

我在使用 Windows 的 Winsock2 通过网络接收数据时遇到问题。我正在尝试使用简单的客户端和服务器系统来实现文件传输程序。使用我们当前的代码,最后进来的数据包不会附加到文件中,因为它不是缓冲区的大小。因此,文件传输并不完全,会引发错误并中断。它并不总是最后一个数据包,有时会更早。

这是服务器代码的sn-p:

int iResult;
ifstream sendFile(path,  ifstream::binary);
char* buf;

if (sendFile.is_open()) {
printf("File Opened!\n");
// Sends the file
while (sendFile.good()) {
    buf = new char[1024];
    sendFile.read(buf, 1024);
    iResult = send(AcceptSocket, buf, (int)strlen(buf)-4, 0 );
    if (iResult == SOCKET_ERROR) {
        wprintf(L"send failed with error: %d\n", WSAGetLastError());
        closesocket(AcceptSocket);
        WSACleanup();
        return 1;
    }
    //printf("Bytes Sent: %d\n", iResult);
}
sendFile.close();

}

这是客户端代码的sn-p:

int iResult;
int recvbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN] = "";

do {
    iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
    if ( iResult > 0){
        printf("%s",recvbuf);
        myfile.write(recvbuf, iResult);
    }
    else if ( iResult == 0 ) {
        wprintf(L"Connection closed\n");
    } else {
        wprintf(L"recv failed with error: %d\n", WSAGetLastError());
    }

} while( iResult > 0 );

myfile.close();

当尝试传输作为字典的文件时,它可能会随机中断。例如,一个运行在 S 的早期中断并在末尾附加了奇怪的字符,这并不罕见:

...
sayable
sayer
sayers
sayest
sayid
sayids
saying
sayings
╠╠╠╠╠╠╠╠recv failed with error: 10054

如何处理这些错误和奇怪的字符?

【问题讨论】:

    标签: c++ winsock2


    【解决方案1】:

    错误发生在服务器端。您收到“对等方重置连接”错误。

    这一行 - buf = new char[1024]; - 显然是有问题的,很可能导致服务器崩溃,因为它耗尽了内存。没有清理发生。首先添加适当的delete 语句,最好放在send 调用之后。如果这不能解决问题,我将使用一个小测试文件并在服务器代码中逐步执行该 while 循环。

    附:比在循环中使用 newdelete 更好的解决方案是重用现有的 buff。编译器可能会优化这个错误,但如果不是这样,你就会严重阻碍应用程序的性能。我认为您实际上应该将buf = new char[1024]; 移出循环。 buf 是一个字符指针,所以 read 将继续覆盖 buf 的内容,如果你传递它 buf。一遍又一遍地重新分配缓冲区是不好的。

    关于错误 MSDN 说:

    现有连接被远程主机强行关闭。如果远程主机上的对等应用程序突然停止,主机重新启动,主机或远程网络接口被禁用,或者远程主机使用硬关闭,这通常会导致(有关远程主机上 SO_LINGER 选项的更多信息,请参阅 setsockopt插座)。如果在一个或多个操作正在进行时,由于保活活动检测到故障而导致连接中断,也可能导致此错误。正在进行的操作因 WSAENETRESET 而失败。后续操作因 WSAECONNRESET 而失败。

    【讨论】:

    • 您好,感谢您的回复。我尝试将buf = new char[1024]; 移到循环之外,文件中的更多单词被破坏(在它们的末尾添加了随机的奇怪字符)。关于服务器内存不足 - 我们尝试发送的文件小于 1mb,并且 PC 有多个 gb 的内存。这真的是问题吗?
    • @iaacp 那可能不是问题所在。将空终止符附加到字符串怎么样?如果您的 char 数组没有换行符或空终止符,您将得到一个垃圾。我不知道 read 是否会自动执行此操作,但我没有看到任何代码来处理此问题。
    【解决方案2】:

    首先,在循环中使用 new 运算符可能不好,尤其是没有相应的 delete。虽然我不是 C++ 专家(仅限 C),但我认为值得一试。

    其次,套接字错误 10054 是“对等方重置连接”,它告诉我服务器没有在套接字上执行所谓的优雅关闭。通过优雅关闭,WinSock 将等到对方接收到所有待处理的数据,然后再发送中断连接的 FIN 消息。您的服务器很可能只是在将最终缓冲区提供给 WinSock 后立即关闭,而没有任何时间进行传输。您需要查看SO_LINGER 套接字选项——它们解释了优雅关闭与非优雅关闭。

    简单地说,你要么需要将自己的协议添加到连接中,以便客户端可以确认收到最终数据块,要么服务器端需要调用setsocketopt() 设置SO_LINGER 超时以便WinSock在通过网络发出套接字关闭之前,将等待客户端对最终数据块的 TCP/IP 确认。如果您不做这些事情中的至少 ONE,那么就会出现这个问题。

    您可能还想查看另一篇关于此的文章:

    socket error 10054

    祝你好运!

    【讨论】:

    • 如果你新建一个数组,需要使用delete[]删除它。
    • 设置一个正的 SO_LINGER 超时只会导致close() 在有待发送的数据时阻塞。不能保证它在发出 FIN 之前等待该数据被确认,也没有必要这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 2023-03-10
    • 2019-04-21
    • 1970-01-01
    相关资源
    最近更新 更多