C ++ Win套接字睡眠错误?

Posted

技术标签:

【中文标题】C ++ Win套接字睡眠错误?【英文标题】:C++ Win socket sleep bug? 【发布时间】:2022-01-07 09:45:22 【问题描述】:

我在使用 TCP 向下载的服务器发送文件时遇到了一点问题。我花了几个小时找出问题所在,但仍然找不到它不起作用的原因。

主要问题是当我尝试发送文件时。程序获得了我的文件的字节数也读取了文件并循环通过条件,但它有时只发送文件。当我通过调试器运行它时,程序总是发送所有文件。当我使用 sleep() 1 秒时,问题也消失了。

#include <iostream>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <string>
#pragma comment(lib, "ws2_32.lib")
#include <windows.h>
int main()

    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) 
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    
    SOCKET SendSocket;
    SendSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (SendSocket < 0) 
        perror("Error at socket: ");
        return 1;
    

    struct sockaddr_in myAddr;
    memset(&myAddr, '\0', sizeof myAddr);
    myAddr.sin_family = AF_INET; //IP4
    myAddr.sin_port = htons(2000);

    inet_pton(AF_INET, "127.0.0.1", &(myAddr.sin_addr)); 
    memset(myAddr.sin_zero, '\0', sizeof myAddr.sin_zero);
    if (connect(SendSocket, (struct sockaddr*)&myAddr, sizeof(myAddr)) < 0)
    
        perror("Error at connect: ");
        return 1;
    

    char buffer[512];

    const char fileName[] = "test.txt";
    FILE* fd;
    if (errno_t a = fopen_s(&fd, fileName, "rb") != 0) 
        perror("File not opened:");
        return 1;
    

    size_t bytes_read = 0;
    size_t test = 0;
    while (!feof(fd)) 
        do 
            if ((bytes_read = fread(buffer, 1, sizeof(buffer), fd)) < 0)
            
                std::cout << "while error";
                break;
            
            else
            
                //Sleep(1000);
                if ((test = send(SendSocket, buffer, bytes_read, 0)) < 0) 
                    perror("Error send");
                    return 1;
                

                //std::cout << "while send" << std::endl;
            
       
         while (test != bytes_read);
    
    fclose(fd);
    WSACleanup(); //clinap
    system("PAUSE");

【问题讨论】:

可能与您当前的问题无关,但很重要:Why is “while ( !feof (file) )” always wrong? 【参考方案1】:

您的代码已损坏。 send 返回发送的字节数,可以小于bytes_read。但是循环while (test != bytes_read) 会丢弃缓冲区内容并用新的fread 填充它。

您可以将其更改为在嵌套循环中调用 send,直到发送整个缓冲区,但这对于 TCP 成帧来说效率低下 - 为了获得最佳性能,需要在同一循环中组合 freadsend .

还有关于帧 - 您的缓冲区太小 (512),建议发送大小为 1500(典型的最大 MTU)。

另请注意:您的 fread 错误处理已损坏。在 Win32 中fread 返回值是无符号的,所以它永远不会是&lt; 0

应该是这样的(未经测试):

char buffer[8192];
int bytes_in_buffer = 0;
for (;;) 
    if (bytes_in_buffer < sizeof(buffer) / 2) 
        int bytes_read = (int)fread(buffer + bytes_in_buffer, 1, sizeof(buffer) - bytes_in_buffer, fd);
        if (bytes_read == 0) 
            if (ferror(fd)) 
                perror("fread");
                return 1;
            
            // at EOF now but continue
        
        bytes_in_buffer += bytes_read;
    
    if (bytes_in_buffer == 0) 
        break; // all data is sent - done
    
    int bytes_sent = send(SendSocket, buffer, bytes_in_buffer, 0);
    if (bytes_sent < 0) 
        perror("send");
        return 1;
    
    bytes_in_buffer -= bytes_sent;
    if (bytes_in_buffer > 0 && bytes_sent > 0) 
        memmove(buffer, buffer + bytes_sent, bytes_in_buffer);
    

这里可能的改进是使用环形缓冲区以减少复制。

【讨论】:

【参考方案2】:

您需要更好地检查send的返回值

https://linux.die.net/man/2/send

返回值 成功后,这些调用会返回发送的字符数。

有时,会发送少于完整缓冲区的内容。您不能只丢弃其余的,您需要重新发送它们。

你还需要处理错误情况:

EAGAIN 或 EWOULDBLOCK 套接字标记为非阻塞,并且 请求的操作会阻塞。 POSIX.1-2001 允许任一错误 在这种情况下返回,并且不需要这些常量 具有相同的值,因此可移植应用程序应检查两者 可能性。

这些最有可能受到Sleep 的影响。如果您睡得很多,您将不会收到这些错误,因为消息会在您睡觉时被刷新。当您不睡觉时,获得这些意味着您需要稍等片刻(Sleep(0)),然后再试一次,直到获得正返回值。不要因为你得到它们一次就出错。

【讨论】:

以上是关于C ++ Win套接字睡眠错误?的主要内容,如果未能解决你的问题,请参考以下文章

从套接字流读取时是不是需要线程睡眠?

阻塞和非阻塞同步和异步

Winsock - 10038 错误 - Win2K3 服务器 - 令人费解的行为

TCP-IP 套接字 C:尝试连接到服务器套接字时出现错误地址错误

分段错误(核心转储)错误 C linux 套接字

非阻塞式I/O