首页 > 解决方案 > Winsock 通过 recv() 接收随机字母

问题描述

我正在尝试进行 Winsock 聊天。我想发送位于 2 个“标签”之间的数据包。有点像“^^^TAG^^^数据包数据^^^TAG^^^”

问题是,我正在使用的客户端应用程序,包括我自己编写的客户端应用程序,要么发送消息错误,要么我的服务器应用程序接收数据错误

这就是我的意思:

使用 Hercules TCP 客户端

使用我自己的客户端

我知道为什么要拆分,这就是我的标签想法的目的,但是如果您阅读我发送的内容和收到的内容,您会看到添加和替换的字母。在某个时刻,我什至收到了我发送的单词,然后是“=============================================================随机的 unicode 字符,但我无法再次截图。

由于我从互联网上下载的大多数 TCP 客户端都不起作用,我认为问题在于我如何接收数据包,而不是我和其他程序如何发送它们

我的代码:

这是我的代码的重写简单版本

struct client_info
{
    SOCKET sock;
    const char* ip;
    int port;
};

struct server_info
{
    SOCKET sock;
    const char* ip;
    int port;
    std::vector<client_info> clients;
    int client_count;

    HANDLE connection_handler;
    HANDLE recv_handler;
};

struct param_info
{
    void* server_info_pointer;
};

class my_server
{
public:
    my_server(const char* ip, int port)
    {
        this->m_info.ip = ip;
        this->m_info.port = port;

        this->start();
        this->client_handler();
        this->recv_packet();
    }
    ~my_server(void)
    {

    }
private:
    server_info m_info;

    bool start(void)
    {
        WSADATA lpWsaData = decltype(lpWsaData){};

        WSAStartup(MAKEWORD(2, 2), &lpWsaData);
        this->m_info.sock = socket(AF_INET, SOCK_STREAM, 0);

        sockaddr_in lpAddr = decltype(lpAddr){};
        lpAddr.sin_family = AF_INET;
        lpAddr.sin_addr.S_un.S_addr = inet_addr(this->m_info.ip);
        lpAddr.sin_port = htons(this->m_info.port);

        char chOption = 1;
        setsockopt(this->m_info.sock, SOL_SOCKET, SO_REUSEADDR, &chOption, sizeof(chOption));
        setsockopt(this->m_info.sock, IPPROTO_TCP, TCP_NODELAY, &chOption, sizeof(chOption));

        if (!bind(this->m_info.sock, reinterpret_cast<sockaddr*>(&lpAddr), sizeof(lpAddr)))
        {
            return true;
        }

        closesocket(this->m_info.sock);
        WSACleanup();
        return false;
    }

    bool client_handler(void)
    {
        param_info pi = param_info{};
        pi.server_info_pointer = &this->m_info;

        if (this->m_info.connection_handler = CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>
            (this->client_handler_internal), &pi, 0, nullptr))
        {
            return true;
        }

        return false;
    }

    static void client_handler_internal(void* param)
    {
        auto pi = reinterpret_cast<param_info*>(param);

        if (!listen(reinterpret_cast<server_info*>(pi->server_info_pointer)->sock, SOMAXCONN))
        {
            client_info ci = client_info{};

            sockaddr_in lpAddr;
            int dAddrSize = sizeof(lpAddr);

            while (ci.sock = accept(reinterpret_cast<server_info*>(pi->server_info_pointer)->sock, reinterpret_cast<sockaddr*>(&lpAddr), &dAddrSize))
            {
                ci.ip = inet_ntoa(lpAddr.sin_addr);
                ci.port = htons(lpAddr.sin_port);

                printf("%s:%d joined!\n", ci.ip, ci.port);

                reinterpret_cast<server_info*>(pi->server_info_pointer)->clients.push_back(ci);

                memset(&ci, 0, sizeof(ci));
                Sleep(100);
            }
        }

        return;
    }

    auto __forceinline recv_packet(void) -> bool
    {
        param_info pi = param_info{};
        pi.server_info_pointer = &this->m_info;

        if (this->m_info.recv_handler = CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>
            (this->recv_packet_internal), &pi, 0, nullptr))
        {
            return true;
        }

        return false;
    }

    static void recv_packet_internal(void* param)
    {
        auto pi = reinterpret_cast<param_info*>(param);

        for (;;)
        {
            for (int i = 0; i < reinterpret_cast<server_info*>(pi->server_info_pointer)->clients.size(); ++i)
            {
                char * lpBuffer = new char[64];
                if (0 < recv(reinterpret_cast<server_info*>(pi->server_info_pointer)->clients.at(i).sock, lpBuffer, sizeof(lpBuffer), 0))
                {
                    std::string lpNewBuffer = lpBuffer;
                    printf("%s\n", lpNewBuffer.c_str());
                }

                memset(lpBuffer, 0, sizeof(lpBuffer));
            }

            Sleep(50);
        }

        return;
    }
};

标签: c++winsock

解决方案


if (0 < recv(reinterpret_cast<server_info*>(pi->server_info_pointer)->clients.at(i).sock, lpBuffer, sizeof(lpBuffer), 0))

您忽略 的返回值recv,因此您的代码不知道它收到了多少字节。另外,请参阅下文,了解此处为何sizeof(lpBuffer)出错。

memset(lpBuffer, 0, sizeof(lpBuffer));

因为lpBuffer是 a char *,所以这会将字节归零sizeof(char *),这是不对的。仅sizeof在需要类型的大小时使用。另外,为什么要将已经使用并且永远不会再次使用的缓冲区归零?

std::string lpNewBuffer = lpBuffer;

您应该使用recv此处的返回值来知道应该有多少字节lpNewBuffer

如果它们不是字符串,请不要将它们视为字符串。存储的返回值,recv以便您知道收到了多少字节。


推荐阅读