c++ - 通过 winsock 发送压缩字符串

标签 c++ tcp winsock zlib winsock2

大家好!我在 winsock2 lib c++ 上有一个简单的 TCP 服务器和客户端。服务器只发送字符串消息。客户只是接收它们。这里一切都很好。但是当我使用 zlib 库压缩字符串时,数据已损坏,我无法在客户端正确接收它们以进行解压缩。有人能帮我吗?

服务器:

{
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Client connected\n";
    int k = rand() % strings.size();
    msg = strings[k];
    msg_size = msg.size();
    msgl_size = msg_size + msg_size*0.1 + 12;
    msgl = new unsigned char[msgl_size + 1]{0};
    if (Z_OK != compress((Bytef*)msgl, 
                         &msgl_size, 
                         reinterpret_cast<const unsigned char*>(msg.c_str()),
                         msg.size()))
    {
        std::cout << "Compression error! " << std::endl;
        exit(2);
    }
}
std::thread * thread = new std::thread([&newConnection, msgl, msgl_size, msg_size, msg]() {
    std::lock_guard<std::mutex> lock(mtx);
    send(newConnection, (char*)&msgl_size, sizeof(unsigned long), NULL);
    send(newConnection, (char*)&msg_size, sizeof(unsigned long), NULL);
    int res;
    do {
        res = send(newConnection, (char*)(msgl), sizeof(msgl_size), NULL);
    }
    while (msgl_size != res);
});

客户:

std::lock_guard<std::mutex> lock(mtxx);
unsigned long msgl_size, msg_size;
recv(Connection, (char*)&msg_size, sizeof(unsigned long), NULL);
recv(Connection, (char*)&msgl_size, sizeof(unsigned long), NULL);
unsigned char * msgl = new unsigned char[msgl_size + 1]{0};
int res;
do {
    res = recv(Connection, reinterpret_cast<char*>(msgl), msgl_size, NULL);
}
while (msgl_size != res);


char * msg = new char[msg_size + 1];
if (Z_OK == uncompress(reinterpret_cast<unsigned char*>(msg), 
                       &msg_size,
                       reinterpret_cast<unsigned char*>(msgl), 
                       msgl_size))
{
    msg[msg_size] = '\0';
    std::cout << msg << std::endl;
    std::cout << "Compress ratio: " << msgl_size / (float)msg_size << std::endl;
}
delete[] msgl;

最佳答案

客户端:

recv 只返回任何立即可用的数据或阻塞直到数据可用,这不太可能发生在大文件或慢速网络上。 recv 很可能会阻塞,直到第一个网络数据包到达为止,具体取决于底层网络,大小可能从几百字节到几万字节不等。也许这条信息适合,也许不适合。

recvflags 参数设置为 MSG_WAITALL 对于较短的消息很有用,因为您将得到您要求的字节数对于或错误。由于出错的可能性,您始终必须测试返回值。

重复:始终检查返回值。

recv 的返回值对于套接字失败为负,对于套接字关闭为 0,或者为读取的字节数。更多信息,请咨询 winsock documentation for recv .

所以...

recv(Connection, (char*)&msg_size, sizeof(unsigned long), NULL);

和 recv(Connection, (char*)&msgl_size, sizeof(unsigned long), NULL);

不检查返回值。套接字可能已经失败,或者对 recv 的调用返回的可能少于所请求的,程序的其余部分将在垃圾上运行。

这些是使用 MSG_WAITALL 的合适位置,但也有可能套接字没有问题,而您被信号中断了。不确定这是否会发生在 Windows 上,但它可以在 Linux 上发生。当心。

if (recv(Connection, (char*)&msg_size, sizeof(unsigned long), MSG_WAITALL) != sizeof(unsigned long) &&
    recv(Connection, (char*)&msgl_size, sizeof(unsigned long), NULL) != sizeof(unsigned long)(
{
    // log error 
    // exit function, loop, or whatever.
}

下一步,

do {
    res = recv(Connection, reinterpret_cast<char*>(msgl), msgl_size, NULL);
} while (msgl_size != res);

将循环直到一个 recv 在一次调用中返回正确的数量。不太可能,但如果确实如此,它一定会在第一次读取时发生,因为代码每次都会覆盖之前的读取。

假设在第一次尝试时从套接字中只读取了 1/2 的消息。由于这不是完整的消息,因此循环进入并尝试再次读取,用后半部分覆盖消息的前半部分,并可能从后续消息中获取足够的字节来满足请求的字节数。这两条消息的合并不会解密。

对于可能很大的负载,循环直到程序拥有所有负载。

char * bufp = reinterpret_cast<char*>(msgl);
int msg_remaining = msgl_size;
while (msg_remaining )
{
    res = recv(Connection, bufp, msg_remaining, NULL);
    if (res <= 0)
    {
        // log error 
        // exit function, loop, or whatever.
    }
    msg_remaining -= res; // reduce message remaining
    bufp += res; // move next insert point in msgl
}

解压可能有问题。我对此了解不够,无法回答。我建议删除它并发送易于调试的纯文本,直到您解决所有网络问题。

服务器端:

recv 一样,send 发送它能发送的内容。您可能必须循环发送以确保您没有用过大的消息填满套接字,以至于套接字无法一次吃掉。和 recv 一样,ssend 可能会失败。始终检查返回值以了解真正发生了什么。 Check the documentation for send for more information.

关于c++ - 通过 winsock 发送压缩字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52170051/

相关文章:

networking - 在进入网络时显示数据包丢失 - 可能导致什么?

winapi - WinSock getaddrinfo 从主机名获取 IP 失败,直到调用 ipconfig/flushdns

c - Windows 线程的奇怪行为

c++ - 为什么 NULL/0 是对象的非法内存位置?

c++ - tr/regex c++ 库 - 正则表达式模式的定义

c++ - std::adjacent_find(last, last) 是否未定义?

c++ - 两个编译器之间的答案差异

Python - 一个简单的套接字脚本出错

sockets - 什么是消息边界?

c++ - 为什么 Windows 不允许在模拟另一个用户时启动 WinSock