x3fang_c 2024-02-03 17:31 采纳率: 8.7%
浏览 5

关于 recv 不返回的问题

将 winsock 封装成一个类但 Recv 函数中出现了问题
下面是问题代码:

std::string recvBuf;
char buffer[10240];
int flags = 1;
int forn = 12;
if (isBlocking)
{
    flags = 0;
    forn = 1;
}
recvBuf.clear();
ioctlsocket(s1, FIONBIO, (unsigned long *)&flags);
for (int i = 1; i <= forn; i++)
{
    while (recv(s1, buffer, 1, 0) > 0)
    {
          recvBuf += buffer;
    }
    Sleep(1);
}
flags = 0;
ioctlsocket(s1, FIONBIO, (unsigned long *)&flags);

问题是它运行后不返回
完整代码:


#include <winsock2.h>
#include <windows.h>
#include <string>
#include <cstring>
#define default_sock 0
#pragma comment(lib, "WS2_32.lib")

class socks
{
private:
    int is_server;
    int flag;
    std::string ip;
    int port;
    SOCKET s1;
    sockaddr_in addr;

public:
    socks()
    {
        port = 80;
        is_server = 0;
        flag = 0;
    };
    ~socks() { WSACleanup(); };
    socks(std::string addrr /*ip 地址*/, int port = 80 /*端口号(默认 80 端口)*/)
    {
        init(port, addrr);
        this->port = port;
        this->ip = addrr;
        is_server = 0;
        flag = 0;
    }
    /*
    -----
    功能:初始化套接字
    ```C++
    int port:端口号
    string addrr:ip 地址
    return:bool:是否成功(true:成功 false:失败)
    ```
    */
    bool init(int port, std::string addrr)
    {
        WSADATA data;
        WORD w = MAKEWORD(2, 1);
        if (::WSAStartup(w, &data) != 0)
            return false;
        s1 = ::socket(AF_INET, SOCK_STREAM, 0);
        if (s1 == INVALID_SOCKET)
            return false;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.S_un.S_addr = inet_addr(addrr.c_str());
        flag = 1;
        this->port = port;
        this->ip = addrr;
        return true;
    }
    /*
    -----
    功能:绑定地址并监听(服务器)
    ```C++
    int listens:同时监听几个连接
    return:bool:是否成功(true:成功 false:失败)
    ```
    */
    bool Bind(int listens)
    {
        if (flag == 1 && is_server == 0)
        {
            if (bind(s1, (SOCKADDR *)&addr, sizeof(SOCKADDR)) != 0)
                return false;
            if (listen(s1, listens) != 0)
                return false;
            flag = 2;
            is_server = 2;
            return true;
        }
        return false;
    }
    /*
    -----
    功能:接受连接(服务器)
    ```C++
    bool isBlocking:是否堵塞并等待连接(true:表示堵塞 false:表示不堵塞)
    return SOCKET:返回套接字(NULL:表示无连接或错误)
    ```
    */
    SOCKET Accept(bool isBlocking = true)
    {
        if (flag == 2 && is_server == 2)
        {
            int flag = 0;
            if (!isBlocking)
                flag = 1;
            ioctlsocket(s1, FIONBIO, (unsigned long *)&flag);
            sockaddr_in accepts;
            int accept_len = sizeof(accepts);
            SOCKET status = accept(s1, (struct sockaddr *)&accepts, &accept_len);
            flag = 0;
            ioctlsocket(s1, FIONBIO, (unsigned long *)&flag);
            if (status == INVALID_SOCKET)
            {
                return NULL;
            }
            return status;
        }
        return NULL;
    }
    /*
    -----
    功能:连接服务器(客户端)
    ```C++
    return bool:是否连接成功(true:表示连接成功 false:表示连接失败)
    ```
    */
    bool Connect()
    {
        if (is_server == 0 && flag == 1)
        {
            int status = connect(s1, (sockaddr *)&addr, sizeof(sockaddr));
            if (status == INVALID_SOCKET)
            {
                return false;
            }
            flag = 2;
            return true;
        }
        return false;
    }
    /*
    -----
    功能:发送消息
    ```C++
    string sendBuf:发送的消息
    SOCKET SendSock:发送消息的套接字
    return:bool:是否成功(true:成功 false:失败)
    ```
    */
    bool Send(std::string sendBuf, SOCKET SendSock = default_sock)
    {
        if (SendSock == default_sock && flag == 2 && is_server == 0)
        {
            send(s1, sendBuf.c_str(), sendBuf.length(), 0);
            return true;
        }
        else if (SendSock != default_sock && flag == 2)
        {
            send(SendSock, sendBuf.c_str(), sendBuf.length(), 0);
            return true;
        }
        else
            return false;
    }

    /*
    -----
    功能:接受消息
    ```C++
    SOCKET RecvSock:接受消息的套接字
    bool isBlocking:是否堵塞接收(true:表示堵塞接收 false:表示非堵塞接收)
    return:string 接收到的消息(返回空字符串表示失败)
    ```
    */
    std::string Recv(SOCKET RecvSock = default_sock, bool isBlocking = false)
    {
        if (RecvSock == default_sock && flag == 2 && is_server == 0)
        {
            std::string recvBuf;
            char buffer[10240];
            int flags = 1;
            int forn = 12;
            if (isBlocking)
            {
                flags = 0;
                forn = 1;
            }
            recvBuf.clear();
            ioctlsocket(s1, FIONBIO, (unsigned long *)&flags);
            for (int i = 1; i <= forn; i++)
            {
                while (recv(s1, buffer, 1, 0) > 0)
                {
                    recvBuf += buffer;
                }
                Sleep(1);
            }

            flags = 0;
            ioctlsocket(s1, FIONBIO, (unsigned long *)&flags);
            return recvBuf;
        }
        else if (RecvSock != default_sock && flag == 2)
        {
            std::string recvBuf;
            char buffer[1024];
            int close_recv = 0;
            int forn = 12;
            int flags = 1;
            if (isBlocking)
            {
                flags = 0;
                forn = 1;
            }
            recvBuf.clear();
            ioctlsocket(RecvSock, FIONBIO, (unsigned long *)&flags);
            for (int i = 1; i <= forn; i++)
            {
                while (recv(RecvSock, buffer, 1, 0) > 0)
                {
                    recvBuf += buffer;
                }
                Sleep(1);
            }

            flags = 0;
            ioctlsocket(RecvSock, FIONBIO, (unsigned long *)&flags);
            return recvBuf;
        }
        else
        {
            return "";
        }
    }
    /*
    -----
    功能:释放套接字
    ```C++
    return:void:无返回
    ```
    */
    void Close()
    {
        closesocket(s1);
        flag = 0;
    }
    /*
    -----
    功能:设置端口
    ```C++
    int port:要设置的端口
    return:bool:是否成功(true:成功 false:失败)
    ```
    */
    bool SetPort(int port)
    {
        if (flag == 0)
        {
            addr.sin_port = htons(port);
            this->port = port;
            return true;
        }
        else
            return false;
    }
    /*
    -----
    功能:设置ip地址
    ```C++
    string ip:要设置的 ip 地址
    return:bool:是否成功(true:成功 false:失败)
    ```
    */
    bool SetIp(std::string ip)
    {
        if (flag == 0)
        {
            addr.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
            this->ip = ip;
            return true;
        }
        else
            return false;
    }
    /*
    -----
    功能:获取端口
    ```C++
    return:int:端口号
    ```
    */
    int getPort() { return this->port; }
    /*
    -----
    功能:获取 ip
    ```C++
    return:string:ip
    ```
    */
    std::string getIp() { return this->ip; }
    /*
    -----
    功能:获取错误号
    ```C++
    return:int:错误号
    ```
    */
    int getError() { return WSAGetLastError(); }
};
  • 写回答

2条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-02-03 17:32
    关注

    引自免费微信小程序:皆我百晓生

    针对您提到的 Recv 函数在运行后不返回的问题,可以从以下几个方面进行分析:

    1. 非阻塞 I/O 模式与循环条件: 在您的代码中,当 isBlockingfalse 时,程序会将套接字设置为非阻塞模式,并尝试接收数据。然而,在非阻塞模式下,recv 函数可能因为没有足够的数据可读而立即返回 0-1(错误)。因此,循环条件 while (recv(s1, buffer, 1, 0) > 0) 可能导致死循环,如果网络状况不佳或者对方没有发送任何数据。

    2. 错误处理: 需要在 recv 后添加适当的错误检查,以确保正确处理不同返回值的情况。例如,当 recv 返回 -1 时,应调用 WSAGetLastError() 获取错误代码,并根据错误代码判断是否应该继续等待或结束接收。

    3. 缓冲区处理: 目前,代码按字节逐个累积消息,这可能导致效率低下,而且对于网络通信而言不是常见的做法。通常我们会一次性读取一定大小的数据块,直到收到完整的消息为止。在这种情况下,您需要对消息长度进行约定或解析,确保知道何时读取完毕。

    4. 改进 Recv 函数: 修改 Recv 函数,使其能够更合理地处理阻塞和非阻塞模式下的接收操作,同时考虑消息边界问题。以下是一个改进后的示例代码段:

    std::string Recv(SOCKET RecvSock, bool isBlocking)
    {
        std::string recvBuf;
        char buffer[10240];
        int flags = isBlocking ? 0 : 1;
        int bytesReceived = 0;
    
        ioctlsocket(RecvSock, FIONBIO, (unsigned long *)&flags);
    
        do
        {
            int len = recv(RecvSock, buffer + bytesReceived, sizeof(buffer) - bytesReceived, 0);
            if (len == SOCKET_ERROR)
            {
                // 处理错误,比如检查WSAGetLastError()
                break;
            }
            else if (len == 0)
            {
                // 连接关闭
                break;
            }
            else
            {
                bytesReceived += len;
            }
    
            // 如果有协议规定的消息结束标志,可以在这里检测并退出循环
            // 或者达到预期接收长度也停止接收
    
        } while (!isBlocking && bytesReceived < sizeof(buffer)); // 对于非阻塞模式,可以选择循环一定次数或到满缓冲区
    
        ioctlsocket(RecvSock, FIONBIO, (unsigned long *)&flags); // 恢复原来的阻塞模式
    
        if (bytesReceived > 0)
        {
            recvBuf.assign(buffer, bytesReceived);
        }
    
        return recvBuf;
    }
    

    请根据实际情况调整上述代码中的错误处理部分和消息边界处理逻辑。另外,由于网络编程的复杂性,实际应用中可能还需要处理更多细节,如超时、断线重连等。

    评论

报告相同问题?

问题事件

  • 创建了问题 2月3日

悬赏问题

  • ¥15 气象网格数据与卫星轨道数据如何匹配
  • ¥100 java ee ssm项目 悬赏,感兴趣直接联系我
  • ¥15 微软账户问题不小心注销了好像
  • ¥15 x264库中预测模式字IPM、运动向量差MVD、量化后的DCT系数的位置
  • ¥15 curl 命令调用正常,程序调用报 java.net.ConnectException: connection refused
  • ¥20 关于web前端如何播放二次加密m3u8视频的问题
  • ¥15 使用百度地图api 位置函数报错?
  • ¥15 metamask如何添加TRON自定义网络
  • ¥66 关于川崎机器人调速问题
  • ¥15 winFrom界面无法打开