2010-08-08 25 views
8

Tengo una pequeña aplicación que envía archivos a través de la red a un agente ubicado en un sistema operativo Windows.¿Qué puedo hacer para evitar TCP Zero Window/TCP Window Full en el lado del receptor?

Cuando esta aplicación se ejecuta en Windows, todo funciona bien, la comunicación es correcta y todos los archivos se copian correctamente.

Pero, cuando esta aplicación se ejecuta en Linux (RedHat 5.3, el receptor sigue siendo Windows) - Veo en la red de Wireshark que los mensajes de TCP Zero Window y TCP Window Full aparecen cada 1-2 segundos. El agente luego cierra la conexión después de algunos minutos.

El código de Windows - Linux es casi el mismo, y bastante simple. La única operación no trivial es setsockopt con SO_SNDBUF y valor de 0xFFFF. Eliminar este código no ayudó.

¿Alguien puede ayudarme con este problema?

EDIT: añadiendo el código de envío - se ve que trata las escrituras correctamente parciales:

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; 
     } 
    } 
} 

Gracias de antemano.

+0

Más información? ¿Se está transfiriendo el archivo con éxito, solo a un ritmo más lento o la transferencia está fallando? Si está fallando, ¿dónde está fallando? ¿Hay algo que se está transmitiendo o está fallando a la mitad? –

+0

@Robert, gracias. La transferencia falla. Si transfiero una carpeta contiene, por ejemplo, 2 GB de archivos de 3 KB a 50 KB, a veces transfiere ~ 0.5 GB, a veces ~ 1.3 GB de datos y luego falla. – rkellerm

+0

¿Qué mensajes de error está recibiendo y qué lado está cerrando la conexión? ¿Está utilizando E/S bloqueante o no bloqueante? ¿Tienes un hilo dedicado haciendo E/S? Cuantos más detalles, mejor, y si podría publicar fragmentos de código que serían los mejores. –

Respuesta

0

Intenté desactivar el algoritmo de Nagle (con TCP_NODELAY), y de alguna manera, me ayudó. La velocidad de transferencia es mucho mayor, el tamaño de la ventana TCP no se completa o se restablece. Lo extraño es que cuando clasifiqué el tamaño de la ventana no tuvo ningún impacto.

Gracias.

+0

Eso es realmente extraño. Normalmente, deshabilitar Nagle solo es útil para aplicaciones en tiempo real en las que desea tener una latencia muy baja a expensas de perder mucho ancho de banda. Deshabilitarlo para la transferencia masiva de archivos parece contradictorio. ¿Has probado y visto objetivamente que inhabilitar a Nagle es lo que hace la diferencia? ¿Tal vez algún otro cambio que hayas hecho sea responsable? –

+0

@Robert S. Barnes: Eso es realmente extraño, estoy de acuerdo. Pero este es el único cambio que se hizo, y ayudó. Además, el lado del receptor ya ha desactivado Nagle. Sé que puede referirse a un problema fundamental subyacente que se esconde en algún lugar, esperando para saltar y morder en otro momento. Pero como solución alternativa es lo suficientemente bueno. – rkellerm

0

El problema más probable es que tenga un error en su código donde no maneje lecturas parciales o escrituras parciales correctamente. Se sabe que TCP entre Linux y Windows funciona.

1

Un error común al desarrollar con sockets TCP es una suposición incorrecta sobre el comportamiento de lectura()/write().

Cuando realiza una operación de lectura/escritura, debe verificar el valor de retorno, puede que no haya leído/escrito el pedido de bytes, generalmente necesita un bucle para realizar un seguimiento y asegurarse de que se transfirieron todos los datos.

12

No veo tu código, tendré que adivinar.

La razón por la que obtiene una ventana Cero en TCP es porque no hay espacio en el búfer recv del receptor.

Existen varias formas de que esto ocurra. Una causa común de este problema es cuando está enviando a través de una LAN u otra conexión de red relativamente rápida y una computadora es significativamente más rápida que la otra computadora. Como un ejemplo extremo, digamos que tiene una computadora 3Ghz que envía lo más rápido posible a través de un Gigabit Ethernet a otra máquina que tiene una CPU de 1Ghz. Como el emisor puede enviar mucho más rápido de lo que el receptor puede leer, el búfer de recepción del receptor se llenará y la pila de TCP anunciará una ventana Cero al remitente.

Ahora esto puede causar problemas tanto en el lado de envío como en el lado de recepción si no están preparados para hacer frente a esto. En el lado de envío, esto puede ocasionar que el buffer de envío se llene y que las llamadas se envíen a bloquearse o fallar si está usando E/S sin bloqueo. En cuanto a la recepción, podría estar pasando tanto tiempo en E/S que la aplicación no tiene la oportunidad de procesar ninguno de sus datos y dar la apariencia de estar encerrado.

Editar

De algunas de sus respuestas y código Parece que su aplicación es de un solo subproceso y que está tratando de hacer no bloqueante envía por alguna razón. Supongo que está configurando el socket como no bloqueante en alguna otra parte del código.

En general, diría que esta no es una buena idea. Idealmente, si le preocupa que su aplicación se cuelgue en un send(2), debe establecer un tiempo de espera prolongado en el socket usando setsockopt y usar un hilo separado para el envío real.

Ver socket(7):

SO_RCVTIMEO y SO_SNDTIMEO Especifique la recepción o el envío de los tiempos de espera hasta que informar de un error. El parámetro es una struct timeval. Si una función de entrada o salida bloquea durante este período de tiempo, y los datos han sido enviados o recibidos, el valor de retorno de esa función será la cantidad de datos transferidos; si no hay datos ha sido transferido y el tiempo de espera ha sido alcanzado entonces se devuelve -1 con errno conjunto EAGAIN o EWOULDBLOCK tan si el socket se especificó para ser sin bloqueo. Si el tiempo de espera se establece en cero (valor predeterminado), la operación nunca expirará.

Su hilo principal puede empujar cada descriptor de archivo en un queue usando decir un mutex impulso para el acceso a la cola, a continuación, iniciar 1 - N hilos para hacer el envío real mediante el bloqueo de E/S con mando tiempos de espera.

Su función de envío debe ser algo como esto (suponiendo que se está configurando un tiempo de espera):

// 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; 
} 

La bandera MSG_NOSIGNAL asegura que su aplicación no es matado por escrito a una toma de corriente que se ha cerrado o se restablece por el par. A veces, las operaciones de E/S se ven interrumpidas por señales, y la comprobación de EINTR le permite reiniciar el send.

En general, debe llamar al doSend en un bucle con trozos de datos que son de tamaño TCP_MAXSEG.

En el lado de recepción puede escribir una función de bloqueo de recv similar utilizando un tiempo de espera en una secuencia separada.

+0

Gracias por esta publicación. Es muy informativo especialmente el 'MSG_NOSIGNAL' que creo que es mi problema en una de mis aplicaciones. – kuchi

Cuestiones relacionadas