para - tipos de enchufes en el mundo
¿Cómo funcionan los enchufes en C? (4)
Estoy un poco confundido acerca de la programación de socket en C.
Usted crea un socket, lo vincula a una interfaz y una dirección IP y lo hace escuchar. Encontré un par de recursos web sobre eso, y lo entendí bien. En particular, encontré un artículo de programación de red en sistemas Unix para ser muy informativo.
Lo que me confunde es el momento en que los datos llegan al socket.
¿Cómo puede saber cuándo llegan los paquetes y qué tan grande es el paquete? ¿Tiene que hacer todo el trabajo pesado usted mismo?
Mi suposición básica aquí es que los paquetes pueden ser de longitud variable, de modo que una vez que los datos binarios comienzan a aparecer en el socket, ¿cómo comienzan a construir paquetes a partir de eso?
Cuando haces una lectura en el socket, le dices cuántos bytes máximos leer, pero si no tiene tantos, te da cuantos más tenga. Depende de usted diseñar el protocolo para que sepa si tiene un paquete parcial o no. Por ejemplo, en el pasado cuando enviaba datos binarios de longitud variable, ponía un int al principio que decía cuántos bytes esperar. Haría una lectura solicitando un número de bytes mayor que el paquete más grande posible en mi protocolo, y luego compararía la primera int con todos los bytes que había recibido, y procesaría o intentaría más lecturas hasta que '' d obtenido el paquete completo, dependiendo.
Entonces, la respuesta a su pregunta depende bastante de si está usando UDP o TCP como su transporte.
Para UDP, la vida se vuelve mucho más sencilla, ya que puede llamar a recv / recvfrom / recvmsg con el tamaño de paquete que necesita (es probable que envíe paquetes de longitud fija desde la fuente de todos modos), y asume que si los datos están disponibles , está ahí en múltiplos de tamaños de paquetes de longitud. (Por ejemplo, llama a recv * con el tamaño de su paquete de envío y está configurado).
Para TCP, la vida se vuelve un poco más interesante; para el propósito de esta explicación, supondré que ya sabes cómo usar socket (), bind (), listen () y accept () - siendo esta última la forma de obtener el descriptor de archivo (FD) de su conexión recién creada.
Hay dos formas de hacer E / S para un socket - blocking, en el que se llama a read (fd, buf, N) y la lectura se queda ahí y espera hasta que haya leído N bytes en buf - o no-blocking, en el que debes verificar (usando select () o poll ()) si el FD es legible, y LUEGO haz tu lectura ().
Cuando se trata de conexiones basadas en TCP, el sistema operativo no presta atención a los tamaños de paquete, ya que se considera una secuencia continua de datos, no fragmentos separados del tamaño de un paquete.
Si su aplicación utiliza "paquetes" (estructuras de datos empaquetadas o no empaquetadas que está pasando), debe poder llamar a read () con el argumento de tamaño adecuado y leer una estructura de datos completa del socket a la vez. La única advertencia con la que tiene que lidiar es recordar ordenar por byte correctamente todos los datos que está enviando, en caso de que el sistema de origen y el de destino sean de diferente entidad de bytes. Esto se aplica tanto a UDP como a TCP.
En lo que se refiere a la programación * de socket NIX, recomiendo encarecidamente "Unix Network Programming, Vol. 1" de W. Richard Stevens (UNPv1) y "Programación avanzada en un entorno Unix" (APUE). El primero es un tomo sobre programación basada en red, independientemente del transporte, y el último es un buen libro de programación versátil, ya que se aplica a la programación * basada en NIX. Además, busque "TCP / IP Illustrated", Volúmenes 1 y 2.
La respuesta corta es que usted tiene que hacer todo el trabajo pesado usted mismo. Se le puede notificar que hay datos disponibles para leer, pero no sabrá cuántos bytes hay disponibles. En la mayoría de los protocolos IP que usan paquetes de longitud variable, habrá un encabezado con una longitud fija conocida antepuesta al paquete. Este encabezado contendrá la longitud del paquete. Lees el encabezado, obtienes la longitud del paquete y luego lees el paquete. Repite este patrón (lee el encabezado, luego lee el paquete) hasta que se complete la comunicación.
Cuando lee datos de un socket, solicita una cierta cantidad de bytes. La llamada de lectura puede bloquearse hasta que se lea el número solicitado de bytes, pero puede devolver menos bytes de los solicitados. Cuando esto sucede, simplemente vuelve a intentar leer y solicita los bytes restantes.
Aquí hay una función C típica para leer un número determinado de bytes desde un socket:
/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/* actually received from the sender. */
/* The function returns either the number of bytes read, */
/* 0 if the socket was closed by the sender, and */
/* -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
*bytesRead = 0;
while(*bytesRead < bytesToRead)
{
int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
if(ret <= 0)
{
/* either connection was closed or an error occurred */
return ret;
}
else
{
*bytesRead += ret;
}
}
return *bytesRead;
}
Los sockets funcionan a un nivel más alto que los paquetes sin procesar, es como un archivo desde el que se puede leer / escribir. Además, cuando intente leer desde un socket, el sistema operativo bloqueará (retendrá) su proceso hasta que tenga datos para cumplir con la solicitud.