Я вижу несколько проблем в вашем коде.
вы не Переустановка readset
переменной каждый раз, когда вы называете select()
. select()
изменяет переменную. Для случая с одним гнездом это не так уж плохо, но вы должны привыкнуть каждый раз перезапускать переменную.
Вы не проверяете ошибки, связанные с кодом recv()
. Вы предполагаете, что любой не-изящный-разъединение - это успех, но это не всегда так.
в конце readN()
перед возвращением true
, вы выводя параметр std::cout
buffer
, однако buffer
будет указывать на END данных, а не НАЧАЛА, так как она была выдвинута чтения петля. Вероятно, это происходит из-за вашей путаницы в отношении «пустого буфера». readN()
сам не должен даже выводить данные вообще, так как вы делаете это после завершения readN()
, иначе вы получите избыточные выходные сообщения.
если readN()
возвращает истину, вы передаете окончательный 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()
не попросит вас назвать:
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. –