大家好!我在 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
很可能会阻塞,直到第一个网络数据包到达为止,具体取决于底层网络,大小可能从几百字节到几万字节不等。也许这条信息适合,也许不适合。
将 recv
的 flags
参数设置为 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/