Что я могу сделать, чтобы не было TCP Zero Window / TCP Window Full on сторона получателя?

У меня есть небольшое приложение, которое отправляет файлы по сети агенту, расположенному в ОС Windows.

Когда это приложение работает в Windows, все работает нормально, связь в порядке, и все файлы успешно скопированы.

Но, когда это приложение работает в Linux (RedHat 5.3, получатель все еще Windows) - я вижу в сообщениях трассировки сети Wireshark TCP Zero Window и TCP Window Full, которые появляются каждые 1-2 секунды. Затем агент закрывает соединение через несколько минут.

Код Windows - Linux почти такой же, и довольно простой. Единственной нетривиальной операцией является setsockopt с SO_SNDBUF и значением 0xFFFF. Удаление этого кода не помогло.

Может кто-нибудь помочь мне с этой проблемой?

РЕДАКТИРОВАТЬ: добавив код отправки - похоже, он правильно обрабатывает частичные записи:

int totalSent=0;
while(totalSent != dataLen)
{
    int bytesSent 
        = ::send(_socket,(char *)(data+totalSent), dataLen-totalSent, 0);

    if (bytesSent ==0) {
        return totalSent;
    }
    else if(bytesSent == SOCKET_ERROR){
#ifdef __WIN32
        int errcode = WSAGetLastError();
        if( errcode==WSAEWOULDBLOCK ){
#else
            if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
#endif
            }
            else{
                if( !totalSent ) {
                    totalSent = SOCKET_ERROR;
                }
                break;
            }
        }
        else{
            totalSent+=bytesSent;
        }
    }
}

Заранее спасибо.

8
задан Robert S. Barnes 8 August 2010 в 12:10
поделиться

3 ответа

Распространенная ошибка при разработке с использованием сокетов TCP связана с неверным предположением о поведении read () / write ().

Когда вы выполняете операцию чтения / записи, вы должны проверить возвращаемое значение, они могут не иметь чтения / записи запрошенных байтов, вам обычно нужен цикл, чтобы отслеживать и гарантировать, что все данные были переданы.

1
ответ дан 5 December 2019 в 12:54
поделиться

Не видя вашего кода, я должен угадать.

Причина, по которой вы получаете нулевое окно в TCP, заключается в том, что в буфере recv получателя нет места.

Это может произойти несколькими способами. Одна из распространенных причин этой проблемы - когда вы отправляете сообщения по локальной сети или другому относительно быстрому сетевому соединению, и один компьютер работает значительно быстрее, чем другой. В качестве крайнего примера предположим, что у вас есть компьютер с частотой 3 ГГц, который как можно быстрее отправляет сообщения через Gigabit Ethernet на другой компьютер, на котором работает процессор с частотой 1 ГГц. Поскольку отправитель может отправлять намного быстрее, чем может прочитать получатель, буфер recv получателя будет заполнен, в результате чего стек TCP объявит отправителю нулевое окно.

Теперь это может вызвать проблемы как на отправляющей, так и на принимающей сторонах, если они обе не готовы с этим справиться. На стороне отправки это может привести к заполнению буфера отправки и вызовам на отправку для блокировки или отказа, если вы используете неблокирующий ввод-вывод. На принимающей стороне вы можете тратить так много времени на ввод-вывод, что у приложения не будет возможности обработать какие-либо данные, и это будет выглядеть заблокированным.

Редактировать

Судя по некоторым из ваших ответов и кода, ваше приложение однопоточное и вы по какой-то причине пытаетесь выполнить неблокирующую отправку.Я предполагаю, что вы устанавливаете сокет в режим неблокирования в какой-то другой части кода.

В общем, я бы сказал, что это плохая идея. В идеале, если вы беспокоитесь о том, что ваше приложение зависает на send (2) , вам следует установить длительный тайм-аут для сокета с помощью setsockopt и использовать отдельный поток для фактической отправки.

См. socket (7) :

SO_RCVTIMEO и SO_SNDTIMEO Укажите тайм-ауты приема или отправки до сообщения об ошибке. В параметр - это время структуры. Если функциональные блоки ввода или вывода для этот период времени, и данные были отправлено или получено, возвращаемое значение эта функция будет количеством данные переданы; если не было данных передан и тайм-аут был достигнуто, то -1 возвращается с ошибкой установите EAGAIN или EWOULDBLOCK так же, как если сокет был указан как неблокирующий. Если для тайм-аута установлено значение ноль (по умолчанию), затем операция никогда не будет тайм-аута.

Ваш основной поток может помещать каждый дескриптор файла в очередь , используя, скажем, мьютекс повышения для доступа к очереди, а затем запускать потоки от 1 до N для фактической отправки, используя блокирующий ввод-вывод с таймаутами отправки.

Ваша функция отправки должна выглядеть примерно так (при условии, что вы устанавливаете тайм-аут):

// blocking send, timeout is handled by caller reading errno on short send
int doSend(int s, const void *buf, size_t dataLen) {    
    int totalSent=0;

    while(totalSent != dataLen)
    {
        int bytesSent 
            = send(s,((char *)data)+totalSent, dataLen-totalSent, MSG_NOSIGNAL);

        if( bytesSent < 0 && errno != EINTR )
            break;

        totalSent += bytesSent;
    }
    return totalSent;
}

Флаг MSG_NOSIGNAL гарантирует, что ваше приложение не будет убито из-за записи в закрытый сокет или сбросить одноранговым узлом. Иногда операции ввода-вывода прерываются сигналами, и проверка EINTR позволяет перезапустить отправку send .

Обычно следует вызывать doSend в цикле с фрагментами данных размером TCP_MAXSEG .

На принимающей стороне вы можете написать аналогичную блокирующую функцию recv, используя тайм-аут в отдельном потоке.

12
ответ дан 5 December 2019 в 12:54
поделиться

Наиболее вероятная проблема заключается в том, что в вашем коде есть ошибка, которая не позволяет правильно обрабатывать частичное чтение или частичную запись. Известно, что TCP между Linux и Windows работает.

0
ответ дан 5 December 2019 в 12:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: