Не видя вашего кода, мне придется угадать.
Причина, по которой вы получаете нулевое окно в TCP, заключается в том, что в буфере recv получателя нет места.
Есть несколько способов, которыми это может произойти.Одна из распространенных причин этой проблемы - отправка по локальной сети или другому относительно быстрому сетевому соединению, когда один компьютер работает значительно быстрее, чем другой.В качестве крайнего примера, скажем, у вас есть компьютер с частотой 3 ГГц, который отправляет как можно быстрее по Gigabit Ethernet на другую машину с процессором 1 ГГц.Так как отправитель может отправить намного быстрее, чем получатель может прочитать, то буфер recv получателя заполнится, и стек TCP объявит отправителю нулевое окно.
Теперь это может вызвать проблемы как при отправкеи получающие стороны, если они оба не готовы иметь дело с этим.На отправляющей стороне это может привести к заполнению буфера отправки и вызовам для отправки, чтобы заблокировать или завершить сбоем, если вы используете неблокирующий ввод / вывод.На принимающей стороне вы могли бы тратить так много времени на ввод-вывод, что приложение не имеет шансов обработать какие-либо свои данные и создать видимость блокировки.
Редактировать
Из некоторых ваших ответов и кода кажется, что ваше приложение однопоточное, и по какой-то причине вы пытаетесь выполнять неблокирующие посылки.Я предполагаю, что вы устанавливаете сокет как неблокирующий в какой-то другой части кода.
Вообще, я бы сказал, что это не очень хорошая идея.В идеале, если вас беспокоит зависание вашего приложения на send(2)
, вы должны установить длительный тайм-аут для сокета, используя setsockopt
и использовать отдельный поток для фактической отправки..
См. socket (7) :
SO_RCVTIMEO и SO_SNDTIMEO Укажите таймауты получения или отправки до сообщения об ошибке.Параметр представляет собой структуру timeval.Если функциональные блоки ввода или вывода на этот период времени и данные были отправлены или получены, возвращаемое значение этой функции будет количеством переданных данных;если данные не были переданы и тайм-аут был достигнут, то возвращается -1 с errno, установленным в EAGAIN или EWOULDBLOCK, как если бы сокет был указан как неблокирующий.Если тайм-аут установлен на ноль (по умолчанию), то операция никогда не будет тайм-аут.
Ваш основной поток может вставить каждый дескриптор файла в queue
, используя, скажем, повышениемьютекс для доступа к очереди, затем запустите 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
size.
На приемной стороне вы можете написать аналогичную блокирующую функцию recv, используя таймаут в отдельном потоке.