Сокеты 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, возврат может происходить три раза.

  1. закройте возврат немедленно, без ожидания вообще (значения по умолчанию; рисунок 7.6)
  2. close задерживается до получения ACK нашего FIN (рисунок 7.7), или
  3. завершение работы с последующим чтением ожидает, пока мы не получим FIN однорангового узла (рисунок 7.8)

Его цифры и его комментарии указывают не на "подтверждение уровня приложения", а на комбинацию shutdown()с последующим read() ожидание нулевого кода возврата (т. е. уведомление о том, что сокет был закрыт) - единственный способ убедиться, что клиентское приложение получило данные.

Однако, если важно только, чтобы данные были успешно доставлены (и подтверждены) на компьютер партнера, тогда будет достаточно SO_LINGER.

Другие вопросы по тегам