在 C++ 中从服务器读取二进制数据时清除缓冲区
Clearing buffer while reading in binary data from a server in C++
我有一个服务器发送原始二进制数据来打印用户必须遍历的 "map",但是,我在读取每行后清除缓冲区时遇到问题,因此不断打印剩余数据在较短的行的末尾。在下面的屏幕截图中,您可以在左侧看到我的输出,在右侧可以看到 should 的输出。解决这个问题的最佳方法是什么?我觉得我错过了什么,但似乎找不到解决办法。
下面是 reading/printing 的代码:
char* mapData = NULL;
string command = "command> ";
size_t dataSize = 0;
while(mapData != command.c_str()) {
unsigned char* buffer = (unsigned char*) &dataSize;
connection = read(mySocket, buffer, 8);
if(connection == -1 || connection < 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
mapData = (char*)malloc(dataSize);
buffer = (unsigned char*) mapData;
while((connection = read(mySocket, buffer, dataSize)) != -1) {
if(connection == -1 || connection < 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
if(dataSize != 1) {
cout << buffer;
}
free(buffer);
buffer = NULL;
}
}
您也应该清除缓冲区。添加:
memset(mapData, 0, dataSize);
在 malloc
.
之后
就像@eozd 指出的那样,在循环中调用malloc
和free
是个坏主意,因为您使用了return
语句。您的代码可能会泄漏内存。您应该确保在 returns 之前调用 free
。更好的是,您可以在 while loop
之外声明 buffer
,并使用 break
而不是 return
,如果出现错误 [=20],则调用 free
=]
看你的解决方案,通信协议似乎涉及先发送数据大小,然后才是实际数据。数据大小如何写入线路?您可能需要从网络字节顺序转换它。
要调试,您可以在每次读取之前打印出 dataSize
的值,以确保它是您期望的值
您忽略了 read()
的 return 值以了解缓冲区中有多少字节。
read()
returns 实际读取的字节数,可能比您请求的要少。所以你需要在循环中调用 read()
直到你读完所有你期望的字节,例如:
int readAll(int sock, void *buffer, size_t buflen)
{
unsigned char* pbuf = reinterpret_cast<unsigned char*>(buffer);
while (buflen > 0) {
int numRead = read(sock, pbuf, buflen);
if (numRead < 0) return -1;
if (numRead == 0) return 0;
pbuf += numRead;
buflen -= numRead;
}
return 1;
}
此外,在读取缓冲区后,您将其视为以 null 结尾的,但事实并非如此,这就是为什么您的输出中会出现额外的垃圾。
更重要的是,mapData != command.c_str()
将始终为真,因此您的 while
循环会无限期地迭代(直到发生套接字错误),这不是您想要的。您希望在收到 "command> "
字符串时结束循环。
mapData
最初为 NULL,而 c_str()
永远不会 returns NULL,因此循环总是至少迭代一次。
然后您分配并释放 mapData
但不将其重置为 NULL,因此它仍指向无效内存。这并不重要,因为您的 while
循环只是比较指针。 c_str()
永远不会 return 指向 mapData
曾经指向的内存的指针。
要正确结束你的循环,你需要在阅读后比较mapData
的内容,而不是比较它的内存地址.
试试这个:
char *mapData = NULL;
uint64_t dataSize = 0;
const string command = "command> ";
bool keepLooping = true;
do {
if (readAll(mySocket, &dataSize, sizeof(dataSize)) <= 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
if (dataSize == 0)
continue;
mapData = new char[dataSize];
if (readAll(mySocket, mapData, dataSize) <= 0) {
cerr << "**Error: could not read text" << endl;
delete[] mapData;
return 1;
}
cout.write(mapData, dataSize);
keepLooping = (dataSize != command.size()) || (strncmp(mapData, command.c_str(), command.size()) != 0);
delete[] mapData;
}
while (keepLooping);
或者:
string mapData;
uint64_t dataSize = 0;
const string command = "command> ";
do {
if (readAll(mySocket, &dataSize, sizeof(dataSize)) <= 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
mapData.resize(dataSize);
if (dataSize > 0) {
if (readAll(mySocket, &mapData[0], dataSize) <= 0) {
cerr << "**Error: could not read text" << endl;
return 1;
}
cout << mapData;
}
}
while (mapData != command);
我有一个服务器发送原始二进制数据来打印用户必须遍历的 "map",但是,我在读取每行后清除缓冲区时遇到问题,因此不断打印剩余数据在较短的行的末尾。在下面的屏幕截图中,您可以在左侧看到我的输出,在右侧可以看到 should 的输出。解决这个问题的最佳方法是什么?我觉得我错过了什么,但似乎找不到解决办法。
下面是 reading/printing 的代码:
char* mapData = NULL;
string command = "command> ";
size_t dataSize = 0;
while(mapData != command.c_str()) {
unsigned char* buffer = (unsigned char*) &dataSize;
connection = read(mySocket, buffer, 8);
if(connection == -1 || connection < 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
mapData = (char*)malloc(dataSize);
buffer = (unsigned char*) mapData;
while((connection = read(mySocket, buffer, dataSize)) != -1) {
if(connection == -1 || connection < 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
if(dataSize != 1) {
cout << buffer;
}
free(buffer);
buffer = NULL;
}
}
您也应该清除缓冲区。添加:
memset(mapData, 0, dataSize);
在 malloc
.
就像@eozd 指出的那样,在循环中调用malloc
和free
是个坏主意,因为您使用了return
语句。您的代码可能会泄漏内存。您应该确保在 returns 之前调用 free
。更好的是,您可以在 while loop
之外声明 buffer
,并使用 break
而不是 return
,如果出现错误 [=20],则调用 free
=]
看你的解决方案,通信协议似乎涉及先发送数据大小,然后才是实际数据。数据大小如何写入线路?您可能需要从网络字节顺序转换它。
要调试,您可以在每次读取之前打印出 dataSize
的值,以确保它是您期望的值
您忽略了 read()
的 return 值以了解缓冲区中有多少字节。
read()
returns 实际读取的字节数,可能比您请求的要少。所以你需要在循环中调用 read()
直到你读完所有你期望的字节,例如:
int readAll(int sock, void *buffer, size_t buflen)
{
unsigned char* pbuf = reinterpret_cast<unsigned char*>(buffer);
while (buflen > 0) {
int numRead = read(sock, pbuf, buflen);
if (numRead < 0) return -1;
if (numRead == 0) return 0;
pbuf += numRead;
buflen -= numRead;
}
return 1;
}
此外,在读取缓冲区后,您将其视为以 null 结尾的,但事实并非如此,这就是为什么您的输出中会出现额外的垃圾。
更重要的是,mapData != command.c_str()
将始终为真,因此您的 while
循环会无限期地迭代(直到发生套接字错误),这不是您想要的。您希望在收到 "command> "
字符串时结束循环。
mapData
最初为 NULL,而 c_str()
永远不会 returns NULL,因此循环总是至少迭代一次。
然后您分配并释放 mapData
但不将其重置为 NULL,因此它仍指向无效内存。这并不重要,因为您的 while
循环只是比较指针。 c_str()
永远不会 return 指向 mapData
曾经指向的内存的指针。
要正确结束你的循环,你需要在阅读后比较mapData
的内容,而不是比较它的内存地址.
试试这个:
char *mapData = NULL;
uint64_t dataSize = 0;
const string command = "command> ";
bool keepLooping = true;
do {
if (readAll(mySocket, &dataSize, sizeof(dataSize)) <= 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
if (dataSize == 0)
continue;
mapData = new char[dataSize];
if (readAll(mySocket, mapData, dataSize) <= 0) {
cerr << "**Error: could not read text" << endl;
delete[] mapData;
return 1;
}
cout.write(mapData, dataSize);
keepLooping = (dataSize != command.size()) || (strncmp(mapData, command.c_str(), command.size()) != 0);
delete[] mapData;
}
while (keepLooping);
或者:
string mapData;
uint64_t dataSize = 0;
const string command = "command> ";
do {
if (readAll(mySocket, &dataSize, sizeof(dataSize)) <= 0) {
cerr << "**Error: could not read text size" << endl;
return 1;
}
mapData.resize(dataSize);
if (dataSize > 0) {
if (readAll(mySocket, &mapData[0], dataSize) <= 0) {
cerr << "**Error: could not read text" << endl;
return 1;
}
cout << mapData;
}
}
while (mapData != command);