我看到你的代碼中的一些問題。
你是不是每次調用select()
時間重置readset
變量。 select()
修改變量。對於單插座的情況,這並不算太壞,但你應該養成每次重置變量的習慣。
您不檢查由recv()
返回的錯誤。你認爲任何非優雅斷開都是成功的,但這並非總是如此。
在readN()
結束返回true
之前,你輸出buffer
參數std::cout
,但buffer
將在年底的數據,而不是的 BEGINNING被人指指點點,因爲它是由閱讀先進循環。這可能是您對「空緩衝區」的混淆來自何處。 readN()
本身甚至不應該輸出數據,因爲您在readN()
退出後會這樣做,否則最終會輸出冗餘輸出消息。
如果readN()
返回true,您使用的是operator<<
期望一個空值終止字符串char
,但你的緩衝區不能保證是空值終止的傳遞最終buffer
到std::cout
。
嘗試一些更喜歡這個:
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;
}
定義'緩衝區仍然是空的'。你對這個斷言有什麼證據?而你忽略了'recv()'返回-1的可能性。 – EJP
我建議你調試代碼。特別是在recv調用中斷,然後檢查緩衝區,真正比請求其他人進行精神調試更合理。 –
我調試了程序,recv返回的值是正值,但緩衝區內容是'\ 0',但仍然是正確的,我沒有處理-1的情況。 –