C++ 如何使用Winsock2处理短读?

C++ 如何使用Winsock2处理短读?,c++,winsock2,C++,Winsock2,我在使用Windows的Winsock2通过网络接收数据时遇到问题。我正在尝试使用一个简单的客户端和服务器系统来实现一个文件传输程序。在我们当前的代码中,最后一个数据包不会附加到文件中,因为它不是缓冲区的大小。因此,文件传输不完全,抛出错误并中断。它并不总是最后一包,有时是更早的 以下是服务器代码的一个片段: int iResult; ifstream sendFile(path, ifstream::binary); char* buf; if (sendFile.is_open()) {

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

以下是服务器代码的一个片段:

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();
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();
}

下面是客户机代码的一个片段:

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();
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();
当试图传输字典文件时,它可能会在随机时间中断。例如,一次跑步在年代初中断,并在结尾添加了奇怪的字符,这并不罕见:

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

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

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

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

另外,与在循环中使用
new
delete
相比,更好的解决方案是重用现有的buff。编译器可能会优化此错误,但如果不优化,则会严重影响应用程序的性能。我认为你实际上应该移动
buf=newchar[1024]循环外部
buf
是一个字符指针,因此如果您传递
buf
,则
read
将继续覆盖
buf
的内容。反复重新分配缓冲区是不好的

关于错误,MSDN表示:


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

首先,在循环中使用new操作符可能不太好,尤其是没有相应的delete。我不是C++专家,虽然(只有C)但是我认为值得检查。 其次,套接字错误10054是“由对等方重置连接”,它告诉我服务器没有在套接字上执行所谓的正常关闭。通过优雅的关闭,WinSock将在发送中断连接的FIN消息之前等待,直到另一方接收到所有挂起的数据。很可能您的服务器在将最终缓冲区提供给WinSock后立即关闭,而没有任何时间进行传输。您需要研究一下
SO_LINGER
套接字选项——它们解释了优雅关闭与非优雅关闭的区别

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

这里还有另一篇关于这方面的文章,您可能想看看:


祝你好运

如果要新建数组,需要使用delete[]将其删除。设置正的延迟超时只会导致
close()
在有数据待发送时阻塞。不能保证它会在发出FIN之前等待确认数据,也没有必要这样做。您好,谢谢您的回复。我尝试过移动
buf=newchar[1024]在循环之外,文件中的更多单词被打断(随机添加的奇怪字符)。至于内存不足的服务器,我们试图发送的文件小于1mb,而PC有多gb的ram。这真的是个问题吗?@iaacp那不可能是个问题。在字符串后面附加一个空终止符怎么样?如果char数组没有换行符或空终止符,则会得到一个垃圾。我不知道read是否会自动执行此操作,但我没有看到任何代码可以处理此操作。