有一天。我编写了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!
答案 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*