Ниже приведен мой код C / S и результат только для теста.
У меня с этим проблема.
ясно, что я просто дважды отправляю через клиента. но есть 3 сообщения, которые только что получает сервер. а второй кажется NULL.
#define MP_MAXLINE 4096
/* client */
int main(int ac, char *av[])
{
char buf[MP_MAXLINE];
int clifd;
struct sockaddr_in cliaddr;
if ((clifd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
perror("socket error");
memset(&cliaddr, 0, sizeof(struct sockaddr_in));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(atoi(av[2]));
inet_pton(AF_INET, av[1], &cliaddr.sin_addr);
if (connect(clifd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)))
perror("can not connect to server");
printf("enter data to send: ");
while(fgets(buf, MP_MAXLINE, stdin) != NULL){
write(clifd, buf, sizeof(buf));
printf("enter data to send: ");
}
}
/* server */
static void cli_service(int clifd){
int lclifd, rcv_cnt = 0;
char data[MP_MAXLINE];
for(;;){
if (read(clifd, data, sizeof(data)) == 0)
exit(1);
rcv_cnt++;
printf("cnt: %d, data: %s", rcv_cnt, data);
}
}
int main(int ac, char *av[])
{
...
for(;;){
clifd = accept(svrfd, (struct sockaddr *)&cliaddr, &clisocklen);
if ((clipid = fork()) < 0){
printf("fork error\n");
continue;
}
else if (clipid == 0){
close(svrfd);
cli_service(clifd);
}
close(clifd);
continue;
}
}
вывод клиента здесь:
введите данные для отправки: 1111
введите данные для отправки: 2222
введите данные для отправки:
вывод сервера здесь:
cnt: 1, данные: 1111
cnt: 2, данные: cnt: 3, данные: 2222
🤔 А знаете ли вы, что...
C стандартизирован ISO и ANSI, что обеспечивает переносимость кода между различными компиляторами и платформами.
Что происходит, так это то, что вы собираетесь обнаружить, что TCP является протоколом ориентированный на поток, что означает, что он гарантирует, что отправляемые вами байты будут доставлены правильно и по порядку, но сопоставление байтов с отдельными вызовами write()
/ read()
произвольно - то есть количество байтов, которые записываются в массив получателя при каждом вызове read()
, определенно нет гарантированно будет таким же, как количество байтов, которые были ранее переданы вызову write()
программой-отправителем.
Таким образом, похоже, что в вашем клиенте каждый вызов write()
отправляет 4096 байт, первые несколько из которых будут содержать текстовую строку с завершающим нулем, которую ввел пользователь, а остальная часть будет неинициализированными данными (т.е. это может быть что угодно потому что вы никогда не писали в эту часть массива).
Затем на вашем сервере вы вызываете read()
, который читает некоторое количество байтов, но вы не знаете, сколько байтов он считал, потому что все, что вам нужно сделать, это проверить, вернул ли read()
0 или нет. Таким образом, вы не знаете, поместила ли read () 4096 байтов данных в ваш массив data
или какое-то меньшее количество байтов; и если это было немного меньшее число, тогда ваш принимающий код теперь не синхронизирован с вашим отправляющим кодом, поскольку последующие вызовы read()
, вероятно, будут записывать неинициализированные данные в начало вашего массива data
, а следующая строка пользователя где-то в середине массива.
Это, конечно, бесполезное поведение. Что вам нужно сделать, чтобы исправить это, так это обратить пристальное внимание на значение, возвращаемое read()
; и если он больше нуля, но меньше sizeof(data)
, вам нужно как-то с этим справиться. Один из способов справиться с этим - вести подсчет того, сколько действительных байтов в настоящее время находится в вашем массиве data
, и всегда read(clifd, data+numValidBytes, sizeof(data)-numValidBytes)
до (numValidBytes == sizeof(data))
... и только затем анализировать данные и устанавливать numValidBytes
обратно в ноль. Другой, более простой вариант - вызвать recv()
вместо read()
и установить MSG_WAITALL в последнем параметре - в этом случае recv()
обещает не возвращаться, пока не прочитает все байты sizeof(data)
, и тогда вам не придется иметь дело с частично читает себя.