Программирование Socket C, получение сообщений переносится из предыдущих сообщений - PullRequest
0 голосов
/ 22 сентября 2018

Привет! Я пытаюсь создать простое имя пользователя / пароль для чтения и записи между моим Сервером и моим клиентским приложением.В настоящее время моя клиентская часть работает отлично.У меня проблема внутри сервера.Когда я ввожу имя пользователя на стороне Клиента, оно корректно фиксируется моим Сервером.См. Следующий код:

void authenticate_process(int cli_sockfd){

    /* Authentication Process */
    write(cli_sockfd, "USN", 3);
    char *username;
    username = recv_msg(cli_sockfd);
    printf("[DEBUG] Client username is %s.\n", username);

    write(cli_sockfd, "PSW", 3);
    char *password;
    password = recv_msg(cli_sockfd);
    printf("[DEBUG] Client Password is %s.\n", password);

}

Проблема, например, заключается в том, что пользователь вводит «Johnabscaras» в качестве имени пользователя, а затем код помещает последние «ras» в переменную пароля.Моя функция recv_msg выглядит следующим образом:

/* Reads a message from the server socket. */
char *recv_msg(int sockfd)
{
    int length;
    char *msg;
    msg = (char*) malloc (9);

    /* All messages are 9 bytes. */
    int n = read(sockfd, msg, 9);
    return msg;

}

Поскольку имя пользователя и пароль никогда не превышают 9 байт, они установлены для этого.Но я обнаружил, что при первом вводе имени пользователя, если вы введете более 9 символов, дополнительные символы будут изменены в переменной пароля.Если вы набираете менее 9 символов, код пропускается, и переменная пароля по какой-то причине сразу устанавливается на «».Может кто-нибудь объяснить и показать, как это исправить?

1 Ответ

0 голосов
/ 22 сентября 2018

Похоже, вы используете потоковый сокет (SOCK_STREAM, т.е. TCP).Потоковый сокет - это в основном (с точки зрения вашей программы) просто двунаправленный канал.

Нет понятия «сообщения», кроме того, что навязывает ваше приложение.Сокет просто отправляет и получает один длинный поток байтов.write(fd, "foo", 3); write(fd, "bar", 3); имеет тот же эффект, что и write(fd, "foobar", 6);.Он отправляет одни и те же байты в том же порядке.

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

  • Например, вы можетерешите, что каждое сообщение имеет длину ровно 100 байт, а затем всегда отправляете / получаете ровно 100 байт.
  • Или вы можете пометить конец сообщения явным ограничителем (например, '\0').Получающий код затем должен был бы зацикливаться, пока не увидит '\0'.
  • Или вы можете отправить поле длины в качестве первой части каждого сообщения;таким образом, принимающий код заранее знает, сколько байтов ему нужно выделить и прочитать.

Этот комментарий в вашем коде означает, что вы выбрали вариант 1:

/* All messages are 9 bytes. */

Но, очевидно,в отправляющем коде есть ошибка, потому что он пытается передать сообщение длиной более 9 байтов, что, конечно, затем интерпретируется как несколько сообщений.Если клиент отправляет 12 байтов, то это сообщение из 9 байтов, за которым следует другое (неполное) сообщение из 3 байтов." В настоящее время моя клиентская часть работает отлично "?Нет, это не так;по крайней мере, если ваш принимаемый код правильный.

Еще одна проблема:

printf("[DEBUG] Client username is %s.\n", username);

%s ожидает строку C, то есть последовательность символов с нулевым символом в конце.username не имеет терминатора NUL (если только клиент не решил явно отправить '\0' байт как часть сообщения).На самом деле, пользователи recv_msg не могут сказать, сколько данных было получено.Если это всегда ровно 9 байтов,

printf("%.9s", username);

будет работать, но recv_msg не гарантирует, что это так:

int n = read(sockfd, msg, 9);

Он просто игнорирует n,так что все или части msg могут быть неинициализированы, а вызывающий код не может сказать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...