我正在使用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()
返回正数,缓冲区仍为空。我错过了什么?
答案 0 :(得分:2)
我在你的代码中看到了一些问题。
每次致电readset
时,您都没有重置select()
变量。 select()
修改变量。对于单插槽情况,这不是太糟糕,但你应该养成每次重置变量的习惯。
您没有检查recv()
返回的错误。您认为任何非优雅断开都是成功的,但并非总是如此。
readN()
之前,在true
结尾处,您正在将buffer
参数输出到std::cout
,但是buffer
将指向数据的 END ,而不是 BEGINNING ,因为它是由读取循环推进的。这可能是你对空白缓冲区&#34;是来自。 readN()
本身甚至不应该输出数据,因为您在readN()
退出后执行此操作,否则最终会出现冗余输出消息。
如果readN()
返回true,则使用期望空终止buffer
字符串的std::cout
将最终operator<<
传递给char
,但是你的缓冲区不能保证以空值终止。
尝试更像这样的东西:
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()
要求您致电:{/ p>
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;
}
答案 1 :(得分:0)
在readN()
的末尾有
std::cout << "\t" << buffer << std::endl;
问题是缓冲区现在指向buffer + size
的{{1}}的原始值。该值已被
buffer
这应输出缓冲区
buffer += res;
使用以下std::cout << "\t" << (buffer - size) << std::endl;
对readN()
进行试验后,如果套接字不是无效句柄(ncat发送的文本/二进制数据),则main()
似乎有效。如果套接字是无效句柄,则函数会快速返回。
readN()