Программирование сокетов Unix на C ++, recv возвращает 0, но все еще получает данные, но иногда получает больше, чем отправлено

0

Я новичок в C++ и программировании сокетов. Я учился в руководстве Beej, поэтому мои коды почти такие же, как и руководство, но я борюсь с действительно странными ошибками.

Во-первых, мой сервер recv() возвращает 0. Согласно документу, клиент должен изящно закрыть соединение для recv(), чтобы вернуть 0. На самом деле это не так. Он возвращает 0, в то же время я все еще получаю данные от клиента. Итак, способ, которым Бэй делает, чтобы получить, не работает для меня. Может ли кто-нибудь объяснить, как это возможно?

char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0); 
buf[numbytes] = '\0';

последняя строка здесь, так как numbytes равно 0, оно отображает все полученные мной сообщения. Поэтому я должен был прокомментировать это. Теперь мой код выглядит так

char buf[MAXDATASIZE];
numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0); 
//buf[numbytes] = '\0';
printf("received: %s\n", buf); 

Теперь он работает с получением сообщений, отправленных клиентом. Тем не менее, я сделал некоторые манипуляции с строкой (добавление) на стороне клиента, а затем отправил сообщение. Теперь я посылаю строку длиной 29 на стороне клиента, но сервер получает 41 байт со странными символами. Что я отправил: получено: Войти # 1 Mary 123456 451912345 получено: Войти # 1 Mary 123456 451912345ÿ> É "ÿy @ÿ> Ád

Вот как я получаю на сервере:

while(1) { // main accept() loop

    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
    if (new_fd == -1) {
        perror("accept");
        continue;
    }
    char buf[MAXDATASIZE];
    int numbytes;
    if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1) 
    perror("recv");
    //buf[numbytes] = '\0'; // this had to be commented out
    printf("received: %s\n", buf); // prints out with weird characters

    string msgRcved = buf;

        close(new_fd); 
}

Вот как я отправляю от клиента:

// string loginCredential is loaded with "1 Mary 123456 451912345" at this point
loginCredentials.insert(0, "Login#");
const char* msgToSend = loginCredentials.c_str();
int numbytesSent;
if (numbytesSent = send(sockfd, msgToSend, strlen(msgToSend), 0) == -1)
    perror("send");

Я хотел бы знать, как мой recv получает данные, пока он возвращает 0 в первую очередь. И я хотел бы знать, что я делаю неправильно, чтобы возвращать данные с клиента/отправлять данные на сервер.

Теги:
sockets

3 ответа

3
Лучший ответ

У вас проблема с приоритетом.

Эта:

if (numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0) == -1) 

эквивалентно

if (numbytes = (recv(new_fd, buf, MAXDATASIZE-1, 0) == -1))

а также

recv(new_fd, buf, MAXDATASIZE-1, 0) == -1

0, когда recv преуспевает.

Та же проблема присутствует на отправляющей стороне.

Нет причин писать такое неудобное и подверженное ошибкам условие.
Это безопаснее:

int numbytes = recv(new_fd, buf, MAXDATASIZE-1, 0);
if (numbytes == -1) 
    perror("recv");
  • 0
    Большое спасибо @molbdnilo! Я не заметил этой проблемы приоритета. Я предполагаю, что руководство Биджа хотело сделать код коротким. Теперь я получаю правильное количество полученных и отправленных байтов. проблема решена :)) спасибо большое
2

Вы должны проверить "numbytes" на ноль, отдельно, и если вы закроете его и выйдете из цикла чтения, потому что сверстник закрыл соединение. В противном случае, и предположим, что вы также протестировали -1, вам нужно обрабатывать только байты numbytes из буфера. Не все из них. В противном случае вы можете обработать уже обработанные байты. В этом случае это может означать восстановление строки с нулевым завершением буфера, или это может означать следующее:

printf("%.*s", numbytes, buf);
  • 0
    Большое спасибо, я не совсем понял ваш ответ в начале. Ну, я все еще обрабатываю твой ответ в моем мозгу, смеется. Я бы решил проблему, если бы я разделил их, как ты сказал. Спасибо!
0

Вы печатаете любой мусор в буфере, выделенном стекем, а не то, что отправил клиент. Когда recv(2) возвращает ноль, в поставляемый буфер ничего не помещается, поэтому это, вероятно, из предыдущей итерации цикла.

Заметки:

  1. Connected TCP socket - это двунаправленный поток байтов. Это означает, что вы можете отправить несколько своих "сообщений" и получить их в одном фрагменте с другой стороны или наоборот. Прочтите из сокета в цикле до тех пор, пока у вас не будет достаточного количества данных для обработки, т.е. используйте явные разделители сообщений или предварительно отложите длину своего следующего сообщения. Это ваш протокол уровня приложения.

  2. Не смешивайте строки C и C++ так. std::string имеет метод size(), используйте его вместо выполнения strlen( msgToSend.c_str() ).

  3. Выделение любых значительных буферов в стеке, особенно тех, кто получает вход от сети, является плохой идеей.

  4. Печатание или иное прохождение дальше, непроверенный вход в сеть - это грубое нарушение безопасности, приводящее к возникновению любых проблем.

Изменить 0:

@molbdnilo ответ правильный. Я не заметил проблему приоритета в условных выражениях. Мои заметки все еще применяются.

  • 0
    Спасибо за хороший набор заметок, я пробовал отправлять разные форматы, поэтому я перепутал коды c ++ и c. Я обычно использовал std :: string для большинства из них. Кстати, я использую метод std::string.length() вместо size() , это будет проблемой при отправке сообщения? И не могли бы вы разработать свою заметку 3? Вы имеете в виду buf[MAXDATASIZE] ?
  • 0
    Во-первых, у вас мало места в стеке. Во-вторых, переполнение буфера стека намного опаснее, чем что-либо еще.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню