Использование strcmp() для чтения данных от клиента не работает должным образом

Я выполняю упражнение, в котором мне нужно настроить сеть между клиентом и сервером и заставить их «разговаривать». Мне нужно проверить конкретные ответы от клиента, а затем заставить сервер действовать соответствующим образом. Чтобы прочитать данные от клиента, я использую функцию, которая вызывает recv() в цикле до тех пор, пока либо не появится символ '\n', либо не останется байтов для чтения. Когда цикл заканчивается, последний символ в массиве устанавливается равным '\0'. Когда функция выполнена, я ожидаю получить строку с завершающим нулем, которую затем можно использовать для сравнения. Вот функция -

int read_in(int socket, char *buf, int len) {
    char *s = buf;
    int slen = len;
    int c = recv(socket, s, slen, 0);
    while((c>0) && (s[c-1]!='\n')) {
        s+=c; slen-=c;
        c = recv(socket, s, slen, 0);
    }
    if (c<0) {
        return c;
    } else if (c==0) {
        buf[0] = '\0';
    } else {
        s[c-1] = '\0';
    }
    return len-slen;
}

Теперь я жду соединения, объявляю массив с именем «buf» для хранения того, что я прочитал с помощью вышеуказанной функции, а затем сравниваю 2 строки с помощью strcmp().

int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
char buf[255];
read_in(connect_d, buf, sizeof(buf));
if (strcmp("Hello\0", buf) == 0) {
    puts("Hello");
} else {
    puts("Not Hello");
}

Я считаю, что строка «buf» содержит «Hello\0», и поэтому сравнение должно пройти успешно. Но это не так. Я не могу понять, почему. Однако, если я использую strncmp() для сравнения первых 5 байтов двух строк, сравнение проходит успешно. Но когда я сравниваю первые 6 байтов, это не удается. Мне интересно, что сохраняется в позиции 6-го байта, которая не работает.


person user1720897    schedule 25.06.2014    source источник
comment
Строковые литералы гарантированно завершаются нулем. Нет необходимости в нулевом завершающем символе.   -  person Engineer2021    schedule 25.06.2014
comment
Если вы знаете длину строки, вы должны использовать strncmp с правильной длиной (здесь 5, я думаю, для приветствия нет необходимости в \0), но поэтому вы не заботитесь о том, что там после buf [4]. здесь хорошее применение strcmp и здесь страницы strncmp.   -  person Yann    schedule 25.06.2014
comment
Почему вы думаете, что buf содержит "Hello\0"? Ошибка strcmp является довольно убедительным признаком того, что buf на самом деле не содержит того, что вы думаете.   -  person user4815162342    schedule 25.06.2014
comment
почему вы используете sizeof(ans), а не sizeof(buf)?   -  person derhoch    schedule 25.06.2014
comment
Тест s[c-1] != '\n' неверен, если сервер отправляет более одной строки одновременно, как в "hello\nworld". Чтобы убедиться, что вы не пропустили строку, вы должны использовать memchr или strchr, чтобы найти новую строку в прочитанном вами вводе.   -  person user4815162342    schedule 25.06.2014
comment
@falloutboy Это была опечатка   -  person user1720897    schedule 25.06.2014
comment
@user4815162342 user4815162342 Это то, что я набираю в командной строке telnet — Hello, за которой следует строка return. Тест может не пройти в случаях, когда клиент отправляет несколько строк, но здесь это не так.   -  person user1720897    schedule 25.06.2014
comment
возможно, клиент отправляет \r\n, поэтому ваш вывод будет Hello\r\0   -  person derhoch    schedule 25.06.2014
comment
Функция read_in() считывает Hello\r из Hello\r\n, а затем заменяет последний символ в Hello\r на '\0', делая его Hello\0. По крайней мере, теоретически это то, что делает код.   -  person user1720897    schedule 25.06.2014
comment
цикл заканчивается, когда s[c-1] == '\n', а затем устанавливает s[c-1] = '\0', поэтому, если раньше было \r\n, в конце будет \r\0.   -  person derhoch    schedule 25.06.2014
comment
Ваша логика, к сожалению, ошибочна: тот факт, что вы вводите Hello в командной строке telnet, не гарантирует, что Hello окажется в buf. У вас может быть ошибка в read_in() или где-то еще в программе, которая неправильно заполняет buf, или telnet (как указывали другие) telnet может отправлять дополнительный символ возврата каретки по сети. С другой стороны, strcmp(buf, "Hello"), возвращающее ненулевое значение, в значительной степени является гарантией того, что buf в этот момент не начинается с Hello\0.   -  person user4815162342    schedule 25.06.2014
comment
Telnet определенно отправляет \r\n, по крайней мере, если он соответствует соответствующему RFC.   -  person alk    schedule 25.06.2014
comment
Вы не упомянули, настроен ли сокет на блокировку или неблокировку. Это существенно меняет код. Вам было бы намного лучше, хотя это и медленнее, в цикле, который завершается по тайм-ауту или '\n' с использованием функции select() и считывает только один символ за раз. Если сокет не блокируется и возвращается 0 байт, это не ошибка.   -  person user3629249    schedule 25.06.2014
comment
относительно этой строки: if (strcmp(Hello\0, buf) == 0) {, strcmp останавливается на первом '\0', поэтому сравнение никогда не совпадет. должно быть if (strcmp(Hello, buf) == 0) { и даже в этом случае оно будет совпадать только в том случае, если заглавные буквы совпадают.   -  person user3629249    schedule 25.06.2014
comment
относительно строки: char buf[255]; это должно быть: char buf[255] = {0}; Это будут строки: } else if (c==0) { buf[0] = '\0'; } еще { s[c-1] = '\0'; можно устранить.   -  person user3629249    schedule 25.06.2014
comment
относительно этой строки: read_in(connect_d, buf, sizeof(buf)); функция read_in() возвращает индикацию ошибки ввода-вывода, либо 0, либо количество прочитанных байтов. Код возврата не проверяется, поэтому никаких сбоев не обнаружено. Кроме того, если установлен флаг сокета «неблокирующий», то функция read_in завершает работу без фактического чтения ввода. (еще одна причина использовать select() с тайм-аутом и читать только один символ за раз.)   -  person user3629249    schedule 25.06.2014
comment
относительно строк: if (strcmp(Hello\0, buf) == 0) { puts(Hello); если вы напечатаете f() buf, перед 'if' вы увидите, что на самом деле находится в buf. и замените «puts (Hello)» на «puts (buf)», чтобы отобразить фактическое прочитанное значение.   -  person user3629249    schedule 25.06.2014


Ответы (2)


Есть некоторые возможности:

  1. В read_in() вы добавляете NUL к buf, если последним прочитанным символом является '\n', но если вы прочитали 0 байтов при последнем вызове recv(), вы делаете buf[0] = '\0';. Вы должны изменить это на s[0] = '\0';, чтобы сохранить то, что вы читали ранее (честно говоря, если бы это произошло в вашем случае, strncmp("Hello", buf, 5) тоже не сработало бы, но я все равно хотел указать на эту проблему)
  2. если клиент отправляет более одной строки, вы можете получить что-то. как "Hello\nWorld\n" в одном вызове read_in(), и вы бы установили buf в "Hello\nWorld"
  3. это виндовый клиент? в этом случае вы могли бы получить "Hello\r\n", поэтому вы установили c[s-2] на '\0' (будьте осторожны, оба символа могут быть получены разными вызовами recv())
  4. возможно, клиент отправляет не "Hello\n", а "Hello \n" или что-то в этом роде

Просто добавьте printf() (например, printf( "buf[5] = %x\n", buf[5] & 0xff );), чтобы увидеть, что именно вы получили

Изменить

теперь, когда мы знаем, что сервер получает "Hallo\r\n", я предлагаю вам добавить это в конец read_in() (в случае c >= 0):

 int buflen = strlen( buf );
 if( buflen > 0 && buf[buflen-1] == '\r' )
      buf[buflen-1] = '\0';
person Ingo Leonhardt    schedule 25.06.2014
comment
Клиент Telnet находится на компьютере с Linux. - person user1720897; 25.06.2014
comment
тогда вам действительно следует добавить printf(), чтобы увидеть, что telnet решил отправить (кстати: помимо того, что вы сказали в своем последнем комментарии к вопросу, ваш код не устранит '\r', только '\n') - person Ingo Leonhardt; 25.06.2014
comment
printf("buf[5] = %x\n", buf[5] & 0xff); возвращает ans[5] = d - person user1720897; 25.06.2014
comment
Здесь мы -- 0x0d == '\r', поэтому вам нужно исключить '\r' и '\n' - person Ingo Leonhardt; 25.06.2014
comment
strlen может завершиться ошибкой, если buf не завершается нулем. - person Engineer2021; 25.06.2014
comment
@staticx buf всегда завершается NUL, если чтение было успешным. я уточнил свое предложение - person Ingo Leonhardt; 26.06.2014

Скорее всего, сервер отправил вам всего пять байтов «H», «e», «l», «l», «o», а не нулевой байт. strcmp ожидает строки, оканчивающиеся нулевым байтом (а компилятор C добавляет нулевой байт к строковому литералу, так что ваше "Hello\0" ​​совершенно бессмысленно).

Вы хотите проверить, что количество возвращенных байтов равно 5, а memcmp ("Hello", buf, 5) возвращает 0. Вам нужно проверить количество возвращенных байтов отдельно, иначе ответ типа "Привет, мой друг" будет интерпретируется как «Здравствуйте».

person gnasher729    schedule 25.06.2014
comment
ОП явно уже знает об этом - функция read_in действительно пытается завершить NUL полученную строку. - person nobody; 25.06.2014