为什么std :: string使WSAGetLastError返回零?

时间:2018-02-12 15:21:58

标签: c++ sockets c++11 winapi winsock

有一天。我编写了Socket包装器类,比如用于C ++的Python内置套接字库(Visual Studio 2015)

所有功能都运行良好。但是,我发现我写的一些功能有问题。

使用std::string之类的.size()方法后,.clear()WSAGetLastError()始终返回零。

std::string str = "Hello, world";
size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);

// Why always return zero after using std::string's methods?
int err = ::WSAGetLastError();

因此,我无法知道套接字状态,套接字已死,或套接字仍在等待接收数据。

所以,我使用了一种方法,在WSAGetLastError()的方法之前存储std::string返回值,然后使用WSASetLastError()恢复它。

std::string str = "Hello, world";

// Before using std::string's methods
int err = ::WSAGetLastError();

size_t recvsz = ::send(socket, str.c_str(), str.size(), 0);

// Restore error value after std::string's methods
::WSASetLastError(err);

我使用的方式是否正确?或为什么std::string使最后一个错误回归零?

编辑:哇...我错了.. :(。我用:: send not :: recv。抱歉让你感到困惑

SOCKET socket;
char buf[100] = {0x00,};
std::string data, temp_buf;
ssize_t recvsz = ::recv(socket, buf, 99, 0);

// When err has proper error code before std::string methods
// It just for a test
// int err = ::WSAGetLastError();

temp_buf = buf;
data += buf;

int err = WSAGetLastError(); // Always return zero!

2 个答案:

答案 0 :(得分:4)

因为您通过写入您不拥有的只读内存而犯了严重的错误。简而言之,你摧毁了计算机的内存,现在你的程序无法预测。

使用兼容的C ++ 11编译器,您可以:

ssize_t recvsz = ::recv(socket, &str[0], str.size(), 0);

那是因为&str[0]是指向实际字符串缓冲区的指针(在非const上下文中),并且因为从C ++ 11开始,保证数据存储 连续。从技术上讲,在此之前,并没有那么多(虽然在实践中它基本上总是如此)。

否则,您将不得不分配一个自己的char[]缓冲区来接收数据。你可以给它自动存储持续时间,几乎没有成本,除非你在一个资源紧张的嵌入式系统上(但你不会使用string)。

顺便提一下,请注意返回类型:size_t是无符号的。 recv返回已签名的 ssize_t ,可能为否定,表示错误情况。

const size_t BUF_SIZE = 256;
char buf[BUF_SIZE];

ssize_t recvsz = ::recv(socket, &buf[0], BUF_SIZE, 0);
if (recvsz < 0) {
   // ... handling
}

// Now, optionally:
std::string str(buf, recvsz);

现在,解决了所有问题,答案是the "last error" is permitted to be changed even by a successful operation。所以,是的,如果你想保留它,你必须保存然后重新设置一个先前的“最后一个错误”值。这似乎具有可疑的价值 - 为什么你不处理它生成的错误?在导致错误后继续进行似乎是一种糟糕的做生意的方式。

答案 1 :(得分:1)

您不应该以这种方式写入addPrinters(printers);的内部缓冲区。它由对象管理,您必须使用append()assign()等函数对其进行更新,以便字符串可以跟踪其当前内容和长度。

此外,std::string返回c_str()指针,您无法写入此类内存。

如果您确定,您将始终写入正确的字节数(因此写入的大小数据和内部存储的数据的实际长度将匹配),您可以使用operator[]来获取对字符串中的特定字符(可能是您的第一个字符)并使用其地址作为目标缓冲区。

正如@AndyG在评论中注意到的那样,C ++ 17引入了std::basic_string::data(),它还返回指向实际缓冲区的指针。

您的代码应采用以下形式之一:

const char*