在C++winsock中通过TCP发送大数据(如文件)

Sending large data (like a file) over TCP in C++ winsock

本文关键字:数据 文件 C++winsock TCP      更新时间:2023-10-16

我正试图使用TCP在C++中创建一个程序进行文件传输,但我遇到了一个很难解决的问题:

这是服务器中的接收线程,它在循环中执行recv()并处理从该客户端接收的数据包。

function()是一个日志函数,基本上,数据包中的第一个字节应该是消息ID。100代表开始文件传输101代表传输数据块102代表完成转移

200和201是我认为有帮助的回答。显然,如果在客户端上,每次发送数据块时我都会等待服务器的响应,那么文件传输的工作原理就像魔术一样,但由于计算机之间的ping,它的工作速度非常慢。

我想做的是让客户端将数据发送到服务器,而不必在每次发送数据时等待服务器响应。

void RecvThread(Object ^lpv)
{
//  function("Hello! %s", ManagedStringToChar( (String^)lpv ) );
//  function("Socket ID : %d!", (System::UInt32)lpv);
    char recvbuf[4096*10];
    int nrecvret;
    SOCKET clientsocket = (SOCKET)lpv;
    do {
        nrecvret = recv(clientsocket, recvbuf, 4096*10, 0);
        if (nrecvret > 0) {
//          function("Bytes received: %dn", nrecvret);
            BYTE msgID = recvbuf[0];
            switch (msgID)
            {
                case 100:
                {
                    nfilecount++;
                    DWORD dwFileSize = *(DWORD*)(recvbuf + 1);
                    function("File size: %d.n", dwFileSize);
                    fileList[nfilecount].nSize = dwFileSize;
                    fileList[nfilecount].pData = (unsigned char*)malloc(dwFileSize);
                    break;
                }
                case 101:
                {
                    BYTE *pData = (BYTE*)recvbuf;
                    pData++;
                    unsigned int nSection = *(DWORD*)(pData);
                    pData += 4;
                    unsigned int nLength = *(DWORD*)(pData);
                    pData += 4;
                    function("Recieved msg id item data! Section %d, length %dn", nSection , nLength);
                    for (int i = 0; i < nLength; i++)
                        fileList[nfilecount].pData[nSection+i] = pData[i];
                    if (nSection + nLength != fileList[nfilecount].nSize)
                    {
                        BYTE* pSendPacketOrg = (BYTE*)malloc(0x5);
                        BYTE* pSend = pSendPacketOrg;
                        pSend[0] = 200;
                        pSend++;
                        *(DWORD*)pSend = nSection + nLength;
                        function("%d : %d. Requesting %d...", nSection, nLength, nSection + nLength);
                        send(clientsocket, (char*)pSendPacketOrg, 5, 0);
                    }
                    if (nSection + nLength == fileList[nfilecount].nSize)
                    {
                        function("Recieved all sections!");
                        BYTE pSend = 201;
                        send(clientsocket, (char*)&pSend, 1, 0);
                    }
                    break;
                }
                case 102:
                {
                    function("finished recieving file!n");
                    FILE* file = fopen("FileRecieved.dat", "wb");
                    if (file)
                    {
                        int nWritten = 0;
                        while (nWritten < fileList[nfilecount].nSize)
                        {
                            int nToWrite = 1024 * 1024;
                            int Remaining = fileList[nfilecount].nSize - nWritten;
                            if (Remaining <= nToWrite)
                                nToWrite = Remaining;
                            int nBytesWritten = fwrite(fileList[nfilecount].pData + nWritten, 1, nToWrite, file);
                            nWritten += nBytesWritten;
                        }
                    }
                    fclose(file);
                    break;
                }
                case 't':
                {
                    // Echo the buffer back to the sender
                    function("Recieved handshake!n");
                    int nsendret = send(clientsocket, recvbuf, nrecvret, 0);
                    if (nsendret == SOCKET_ERROR) {
                        function("send failed with error: %dn", WSAGetLastError());
                        //              closesocket(clientsocket);
                        //              WSACleanup();
                        break;
                    }
//                  function("Bytes sent: %dn", nsendret);
                    break;
                }
                default:
                {
                    function("Unknown msgID: %d, size: %dn", msgID, nrecvret);
                    function("Last error: %dn", WSAGetLastError());
                    break;
                }   
            }

        }
        else if (nrecvret < 0)
        {
            if (errno == EAGAIN)
            {
                function("recv() timed out.n");
            }
            else
            {
                function("recv() failed due to errno = %dn", errno);
//              exit(1);
            }
        }
        else if (nrecvret == 0)
            function("Connection closing...n");
        else if (WSAGetLastError() == 10053)
        {
            function("An established connection was aborted by the software in your host computer, possibly due to a data transmission time-out or protocol error.n");
            break;
        }
        else  {
            function("recv failed with error: %dn", WSAGetLastError());
            break;
        }
    } while (nrecvret > 0);
    // shutdown the connection since we're done
    function("Shutting down and closing socket %d...n", clientsocket);
    nrecvret = shutdown(clientsocket, SD_SEND);
    if (nrecvret == SOCKET_ERROR) {
        function("shutdown failed with error: %dn", WSAGetLastError());
        closesocket(clientsocket);
//      WSACleanup();
        return;
    }
}

这部分代码来自客户端。客户端连接,打招呼,并将我分配的数据(目前仅用于测试)发送到服务器。我添加了一些注释来帮助理解代码。

int __cdecl main(int argc, char **argv)
{
    unsigned char* pData; // "file" data
    unsigned int nSize = 1023928; // "file" size, 
    pData = (unsigned char*)malloc(nSize); // for now these are allocated instead of reading a file
    for (int i = 0; i < nSize; i++)
        pData[i] = 0xD2; // the data for the file would be 0xD2 throughout the whole file
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
        hints;
    char *sendbuf = "this is a test";
    char *szServerIP = "109.xx.xxx.xx"; // server IP, censored
    char recvbuf[DEFAULT_BUFLEN]; // default buflen is 512
    int iResult;
    int recvbuflen = DEFAULT_BUFLEN;
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %dn", iResult);
        return 1;
    }
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    // Resolve the server address and port
    iResult = getaddrinfo(szServerIP, "6005", &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %dn", iResult);
        WSACleanup();
        return 1;
    }
    int count = 0;
    while (1)
    {
        count++;
        printf("[%d] Connecting... n", count);
        // Create a SOCKET for connecting to server
        ConnectSocket = socket(result->ai_family, result->ai_socktype,
            result->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ldn", WSAGetLastError());
            WSACleanup();
            return 1;
        }
        // Connect to server.
        iResult = connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }
    freeaddrinfo(result);
    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!n");
        WSACleanup();
        return 1;
    }
    printf("Connected to the server on port 6005! Now sending data...");
    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); // says hi
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %dn", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    printf("Bytes Sent: %ldn", iResult);
    // Wait for the server to say hi back
    do {
        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0)
        {
            printf("Bytes received: %dn", iResult);
            break;
        }
        else if (iResult == 0)
            printf("Connection closedn");
        else
            printf("recv failed with error: %dn", WSAGetLastError());
    } while (iResult > 0);
    Sleep(2000); // wait 2 seconds and then send our data

    BYTE *pSizePacketOrg = (BYTE*)malloc(5);// packet 100 sends data size
    BYTE *pSizePacket = pSizePacketOrg;//
    pSizePacket[0] = 100;//
    pSizePacket++;//
    *(DWORD*)pSizePacket = 1023928;//
    printf("Sending 100n");
    iResult = send(ConnectSocket, (char*)pSizePacketOrg, 5, 0);
    if (iResult == SOCKET_ERROR) {
        printf("send 100 with error: %dn", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    free(pSizePacketOrg);
    unsigned int nWritten = 0;
    printf("Sending 101n"); // packet 101 sends the data, in chunks
    while (nWritten < nSize)
    {
        unsigned int nToWrite = 1024;
        printf("nToWrite %dn", nToWrite);
        unsigned int Remaining = nSize - nWritten;
        printf("nRemaining %dn", nToWrite);
        if (Remaining <= nToWrite)
        {
            nToWrite = Remaining;
            printf("Remaining smaller or equal to write. Writing %d.n", nToWrite);
        }
        BYTE *pSendPacketOrg = (BYTE*)malloc(nToWrite + 1 + 4 + 4);
        BYTE* pSendPacket = pSendPacketOrg;
        pSendPacket[0] = 101;
        pSendPacket++;
        *(DWORD*)pSendPacket = nWritten;
        pSendPacket += 4;
        *(DWORD*)pSendPacket = nToWrite;
        pSendPacket += 4;
        memcpy(pSendPacket, (pData + nWritten), nToWrite);
    //  Sleep(100);
//      Sleep(20);
        int nBytesWritten = send(ConnectSocket, (char*)pSendPacketOrg, nToWrite + 9, 0);
        if (nBytesWritten == SOCKET_ERROR) {
            printf("send 101 : %d:%d with error: %dn", nWritten, nToWrite, WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }
        free(pSendPacketOrg);
        printf("written %d bytes.n", nBytesWritten);
        nWritten += nBytesWritten-9; // -9 because of the information before the data
        if (nWritten == nSize) // if finished sending everything, wait for a response from the server
        {
            int nResponse = recv(ConnectSocket, recvbuf, recvbuflen, 0);
            if (nResponse == 1)
            {
                if (((BYTE*)recvbuf)[0] == 201) // server notified finished recieving
                {
                    printf("Server finished recieving! send end file...");
                    break;
                }
            }
            //int nResponse = recv(ConnectSocket, recvbuf, recvbuflen, 0);
            //if (nResponse == 5)
            //{
            //  if ( ((BYTE*)recvbuf)[0] == 200)
            //  {
            //      printf("Server is requesting %d!", *(DWORD*)(recvbuf + 1));
            //  }
            //}
        }
//      Sleep(50);
    }
    printf("Sending 102n"); // confirming the end of the transmission
    BYTE pEndPacket = 102;
    iResult = send(ConnectSocket, (char*)(&pEndPacket), 1, 0);
    if (iResult == SOCKET_ERROR) {
        printf("send 102 with error: %dn", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    Sleep(1000);
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("send failed with error: %dn", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    Sleep(10000);
    // shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %dn", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    // cleanup
    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}

在我的日志文件中,当我从计算机本地运行客户端和服务器时,它几乎完美地工作:

[LOG] Recieved handshake!
[LOG] File size: 1023928.
[LOG] Recieved msg id item data! Section 0, length 36864
[LOG] 0 : 36864. Requesting 36864...[LOG] Recieved msg id item data! Section 36864, length 36864
[LOG] 36864 : 36864. Requesting 73728...[LOG] Recieved msg id item data! Section 73728, length 36864
[LOG] 73728 : 36864. Requesting 110592...[LOG] Recieved msg id item data! Section 110592, length 36864
[LOG] 110592 : 36864. Requesting 147456...[LOG] Recieved msg id item data! Section 147456, length 36864
[LOG] 147456 : 36864. Requesting 184320...[LOG] Recieved msg id item data! Section 184320, length 36864
[LOG] 184320 : 36864. Requesting 221184...[LOG] Recieved msg id item data! Section 221184, length 36864
[LOG] 221184 : 36864. Requesting 258048...[LOG] Recieved msg id item data! Section 258048, length 36864
[LOG] 258048 : 36864. Requesting 294912...[LOG] Recieved msg id item data! Section 294912, length 36864
[LOG] 294912 : 36864. Requesting 331776...[LOG] Recieved msg id item data! Section 331776, length 36864
[LOG] 331776 : 36864. Requesting 368640...[LOG] Recieved msg id item data! Section 368640, length 36864
[LOG] 368640 : 36864. Requesting 405504...[LOG] Recieved msg id item data! Section 405504, length 36864
[LOG] 405504 : 36864. Requesting 442368...[LOG] Recieved msg id item data! Section 442368, length 36864
[LOG] 442368 : 36864. Requesting 479232...[LOG] Recieved msg id item data! Section 479232, length 36864
[LOG] 479232 : 36864. Requesting 516096...[LOG] Recieved msg id item data! Section 516096, length 36864
[LOG] 516096 : 36864. Requesting 552960...[LOG] Recieved msg id item data! Section 552960, length 36864
[LOG] 552960 : 36864. Requesting 589824...[LOG] Recieved msg id item data! Section 589824, length 36864
[LOG] 589824 : 36864. Requesting 626688...[LOG] Recieved msg id item data! Section 626688, length 36864
[LOG] 626688 : 36864. Requesting 663552...[LOG] Recieved msg id item data! Section 663552, length 36864
[LOG] 663552 : 36864. Requesting 700416...[LOG] Recieved msg id item data! Section 700416, length 36864
[LOG] 700416 : 36864. Requesting 737280...[LOG] Recieved msg id item data! Section 737280, length 36864
[LOG] 737280 : 36864. Requesting 774144...[LOG] Recieved msg id item data! Section 774144, length 36864
[LOG] 774144 : 36864. Requesting 811008...[LOG] Recieved msg id item data! Section 811008, length 36864
[LOG] 811008 : 36864. Requesting 847872...[LOG] Recieved msg id item data! Section 847872, length 36864
[LOG] 847872 : 36864. Requesting 884736...[LOG] Recieved msg id item data! Section 884736, length 36864
[LOG] 884736 : 36864. Requesting 921600...[LOG] Recieved msg id item data! Section 921600, length 36864
[LOG] 921600 : 36864. Requesting 958464...[LOG] Recieved msg id item data! Section 958464, length 36864
[LOG] 958464 : 36864. Requesting 995328...[LOG] Recieved msg id item data! Section 995328, length 28600
[LOG] Recieved all sections![LOG] finished recieving file!

然而。。。当我在远程PC上运行客户端时,服务器日志如下所示:

[LOG] Recieved handshake!
[LOG] File size: 1023928.
[LOG] Recieved msg id item data! Section 0, length 36864
[LOG] 0 : 36864. Requesting 36864...[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35972
[LOG] Last error: 183
[LOG] Recieved msg id item data! Section 147456, length 36864
[LOG] 147456 : 36864. Requesting 184320...[LOG] Unknown msgID: 210, size: 40960
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1967
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 6800
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 593
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35980
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 0, size: 37214
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36582
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 38080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36506
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36410
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 38080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 4080
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 32640
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35123
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 37899
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36234
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 1360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 35360
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 36720
[LOG] Last error: 183
[LOG] Unknown msgID: 210, size: 28515
[LOG] Last error: 183
[LOG] Connection closing...
[LOG] Shutting down and closing socket 680...

我收到客户端没有发送的数据包,大小都不一样,我假设它们是文件数据,因为数据包中的第一个字节是210(dec表示0xD2,我将其设置为"文件"数据)但是,为什么收到的包裹是这样的?

我被告知应该有一些延迟,可能与阻塞或非阻塞功能有关,我不是网络专家。。。

请记住,每次客户端发送数据包时等待服务器响应是我希望避免的目标,因为我希望确保最大传输速度,而计算机之间的ping会降低数据传输的速率。我应该在代码中添加一个套接字选项吗?我做错什么了吗?

提前谢谢,强尼!

实际上,您不必像TCP为您所做的那样手动执行ACK。

如果出于某种原因必须手动进行ACK,那么您需要做的是窗口化,即允许一定数量的未确认数据包最大限度地利用网络容量。(但确定窗口大小是一项艰巨的任务,TCP仍然可以完成)