Сокеты C: write(), за которыми следует close(), приводит к неполной передаче данных
Я пытаюсь написать элементарный файловый сервер, который берет имя файла от клиента и отвечает, отправляя данные через TCP клиенту. У меня есть работающее клиентское и серверное приложение по большей части, но я наблюдаю странное поведение, рассмотрим следующее
while ((num_read = read (file_fd, file_buffer, sizeof (file_buffer))) > 0)
{
if (num_read != write (conn_fd, article_buffer, num_read))
{
perror ("write");
goto out;
}
}
out:
close(file_fd); close(sub_fd);
file_fd
является файловым дескриптором файла, отправляемого по сети, conn_fd
файловый дескриптор connect()
издание TCP
разъем.
Кажется, что это работает для небольших файлов, но когда мои файлы становятся больше (мегабайт +), кажется, что некоторый непоследовательный объем данных в конце файла не удастся передать.
Я подозревал, что непосредственные операторы close() после записи могут иметь к этому какое-то отношение, поэтому я попробовал 1 секунду sleep()
до того как close()
Заявления и мой клиент успешно получил все данные.
Есть ли лучший способ справиться с этим, чем делать sleep()
на стороне сервера?
1 ответ
Успешная "запись" в сокет не означает, что данные были успешно отправлены узлу.
Если вы используете производную Unix, вы можете выполнить "сокет man 7" и изучить SO_LINGER"в качестве потенциального решения.
редактировать: благодаря комментарию EJP (спасибо), я перечитал то, что Стивенс должен сказать о предмете "Программирование в Unix Network" о гарантированной доставке всех данных одноранговому узлу. Он говорит следующее (в Томе 1 Второго издания, стр. 189):
... мы видим, что когда мы закрываем наш конец соединения, в зависимости от вызываемой функции (закрытие или выключение) и от того, установлена ли опция сокета SO_LINGER, возврат может происходить три раза.
- закройте возврат немедленно, без ожидания вообще (значения по умолчанию; рисунок 7.6)
- close задерживается до получения ACK нашего FIN (рисунок 7.7), или
- завершение работы с последующим чтением ожидает, пока мы не получим FIN однорангового узла (рисунок 7.8)
Его цифры и его комментарии указывают не на "подтверждение уровня приложения", а на комбинацию shutdown()
с последующим read()
ожидание нулевого кода возврата (т. е. уведомление о том, что сокет был закрыт) - единственный способ убедиться, что клиентское приложение получило данные.
Однако, если важно только, чтобы данные были успешно доставлены (и подтверждены) на компьютер партнера, тогда будет достаточно SO_LINGER.