gpt4 book ai didi

c++ - 成功接收后清空缓冲区

转载 作者:可可西里 更新时间:2023-11-01 02:40:42 28 4
gpt4 key购买 nike

我正在用 C++ 在 Windows 上编写一个服务器,我在使用 recv() 时遇到了一个奇怪的行为。

我写了这个函数:

bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int left, res;
FD_ZERO(&readset);
FD_SET(s, &readset);
left = size;
std::cout << "-----called readN to read " << size << " byte" << std::endl;
while (left > 0) {
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, left, 0);
if (res == 0) {//connection closed by client
return false;
}

left -= res;
std::cout << "\treceived " << res << " left " << left << std::endl;
if (left != 0) {
buffer += res;
}

}
else if (res == 0) { //timer expired
return false;
}
else { //socket error
return false;
}
}
std::cout << "\t" << buffer << std::endl;
return true;
}

我这样调用它:

std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout <<"\t"<< buffer.get()<< std::endl;
}

问题是即使recv() 返回一个正数,缓冲区仍然是空的。我错过了什么?

最佳答案

我发现您的代码中存在一些问题。

  1. 您没有重置 readset每次调用 select() 时都会发生变化. select()修改变量。对于单插槽情况,这还算不错,但您应该养成每次都重置变量的习惯。

  2. 您没有检查 recv() 返回的错误.您假设任何非正常断开都是成功的,但这并不总是正确的。

  3. readN() 的末尾返回前true , 你正在输出 buffer参数 std::cout , 然而 buffer将指向数据的END,而不是BEGINNING,因为它是由读取循环推进的。这可能是您对“空缓冲区”感到困惑的来源。 readN()本身甚至根本不应该输出数据,因为你在 readN() 之后这样做退出,否则你最终会得到冗余的输出消息。

  4. 如果 readN()返回 true,您将传递最后一个 bufferstd::cout使用 operator<<期望空终止 char字符串,但不能保证您的缓冲区以 null 结尾。

尝试更像这样的东西:

bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;

res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res == WSAEWOULDBLOCK) {
continue; //call select() again
}
return false; //socket error
}

if (res == 0) {
return false; //connection closed by client
}

buffer += res;
size -= res;

std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}

/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/

else {
return false; //timer expired or socket error
}
}

return true;
}

std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout << "\t";
std::cout.write(buffer.get(), size_);
std::cout << std::endl;
}

话虽如此,我建议使用 readN() 的替代实现方式,取决于您使用的是阻塞套接字还是非阻塞套接字。

如果阻塞,使用setsockopt(SO_RCVTIMEO)而不是 select() .如果recv()因超时而失败,WSAGetLastError()会报WSAETIMEDOUT :

sck = socket(...);

DWORD timeout = MAXWAIT * 1000;
setsockopt(sck, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

bool readN(SOCKET s, int size, char* buffer){
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
/*
res = WSAGetLastError();
if (res == WSAETIMEDOUT) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
return false; //timer expired or socket error
}

if (res == 0) {
return false; //connection closed by client
}

buffer += res;
size -= res;

std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}

return true;
}

如果是非阻塞的,不要调用select()除非recv()要求你调用它:

bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res != WSAEWOULDBLOCK) {
return false; //socket error
}

FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;

res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
continue; //call recv() again
}

/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/

return false; //timer expired or socket error
}

if (res == 0) {
return false; //connection closed by client
}

buffer += res;
size -= res;

std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}

return true;
}

关于c++ - 成功接收后清空缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38474190/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com