2008-11-21 12 views
14

Estoy comenzando a hacer una programación de red básica con C, y encontré todo esto sobre los zócalos y todos parecen muy intrincados. Tal vez abrir sockets con C es complicado, pero me gustaría saber la forma más simple y efectiva de abrir y escribir datos en un socket en el lenguaje de programación C.Forma más sencilla de abrir y usar un zócalo en C

Gracias

Respuesta

21

Tiene razón, usar conectores en C tiene una sintaxis difícil. Los lenguajes posteriores, como Java y Python, lo convierten en un complemento en comparación. El mejor tutorial que he encontrado para hacer la programación de socket en C es Beej's Guide to Network Programming. Le recomiendo que empiece desde el principio para obtener una buena visión general, pero si solo necesita obtener un código que funcione ahora, puede pasar directamente a la sección titulada Client-Server Background.

¡Buena suerte!

+1

Esto es tonto. Los sockets * están * definidos * como una API C, y los "lenguajes posteriores" tienen que realizar todas esas llamadas C en algún nivel. Hay "bibliotecas posteriores" en C que también lo harán fácilmente, hasta hacer, por ejemplo, una solicitud de HTTP en lugar de rebuscar con sockets. Puede tener fácilmente una función cliente que tomará una dirección IP en notación de host o punto como una cadena 'char *', un número de puerto como 'int', y le devolverá una corriente' FILE * 'que denota el circuito conectado, o un puntero nulo con 'errno' establecido en algo útil. – Kaz

0

lectura y escritura de los zócalos básica no es más difícil que la lectura y escritura de archivos normales (recv sólo tiene que utilizar en lugar de leer y enviar en su lugar si escribir). Las cosas se ponen trickey cuando necesitas abrir un socket. La razón de esto es porque hay muchas maneras diferentes de comunicarse utilizando sockets (TCP, UDP, etc.).

+0

lo que dices es cierto en los sistemas operativos reales, pero desafortunadamente Windows hace que sea un poco más difícil que usar un archivo simplemente al hacer una distinción entre sockets y archivos. – rmeador

5

Usted no menciona qué plataforma que se encuentra, pero una copia de Unix Network Programming por Stevens sería una buena adición a su biblioteca. La mayoría de los sistemas operativos implementan los sockets de Berkley usando socket, bind, connect, etc.

+0

Estaba intentando recordar el nombre de ese libro para mi propia respuesta, pero actualmente está almacenado. Esta es la biblia para la programación de socket. – paxdiablo

+0

Sí, es un gran libro. Lo mantengo al alcance de la mano en el trabajo. –

+0

Todos los libros de Stevens son geniales, pero UNP es el mejor. Ambos volúmenes. – qrdl

3

A menos que escriba un daemon de red, la mayoría de las redes en C se pueden hacer a un nivel superior que utilizando directamente los sockets, utilizando las bibliotecas apropiadas. Por ejemplo, si solo desea recuperar un archivo con HTTP, use Neon o libcurl. Será más simple, será en un nivel superior y tendrá gratis SSL, IPv6, etc.

+0

Varios votos abajo y ningún comentario para explicar por qué. Esto dice mucho sobre el nivel técnico de muchos usuarios de SO ... – bortzmeyer

+8

Es más probable que sea una batalla entre las personas que están de acuerdo con usted y las personas que piensan que no está respondiendo la pregunta. –

+0

aquí tienes, ahora estás en 0. Tener razón no es fácil – droope

0

Mucho buenos consejos aquí hasta ahora. Generalmente escribo en C++, pero puede encontrar algo de uso en un libro blanco que escribí "Cómo evitar los diez errores de programación de los mejores diez zócalos": ignore los consejos para usar el kit de herramientas de ACE (ya que requiere C++) pero tome nota del zócalo errores en el documento: son fáciles de hacer y difíciles de encontrar, especialmente para un principiante. http://www.riverace.com/sockets10.htm

0

La guía definitiva a la Programación de Redes con Linux describen toma fácil y explicar muchas cosas como enhebrar servidor, el diseño del protocolo, etc ... También Sockets TCP/IP en C, segunda edición es buena.

1

POSIX 7 ejemplo de cliente del servidor TCP

Uso

Obtener dos ordenadores en una LAN.

ejecutar el servidor en un equipo con:

./server.out 

Obtener la IP del ordenador servidor con ifconfig, por ejemplo, 192.168.0.10.

En el otro equipo, ejecute:

./client.out 192.168.0.10 

líneas Ahora escriba en el cliente y el servidor se les incrementa en 1 (ROT-1 Cypher) volver.

server.c

#define _XOPEN_SOURCE 700 

#include <stdbool.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include <arpa/inet.h> 
#include <netdb.h> /* getprotobyname */ 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <unistd.h> 

int main(int argc, char **argv) { 
    char buffer[BUFSIZ]; 
    char protoname[] = "tcp"; 
    struct protoent *protoent; 
    int enable = 1; 
    int i; 
    int newline_found = 0; 
    int server_sockfd, client_sockfd; 
    socklen_t client_len; 
    ssize_t nbytes_read; 
    struct sockaddr_in client_address, server_address; 
    unsigned short server_port = 12345u; 

    if (argc > 1) { 
     server_port = strtol(argv[1], NULL, 10); 
    } 

    protoent = getprotobyname(protoname); 
    if (protoent == NULL) { 
     perror("getprotobyname"); 
     exit(EXIT_FAILURE); 
    } 

    server_sockfd = socket(
     AF_INET, 
     SOCK_STREAM, 
     protoent->p_proto 
     /* 0 */ 
    ); 
    if (server_sockfd == -1) { 
     perror("socket"); 
     exit(EXIT_FAILURE); 
    } 

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) { 
     perror("setsockopt(SO_REUSEADDR) failed"); 
     exit(EXIT_FAILURE); 
    } 

    server_address.sin_family = AF_INET; 
    server_address.sin_addr.s_addr = htonl(INADDR_ANY); 
    server_address.sin_port = htons(server_port); 
    if (bind(
      server_sockfd, 
      (struct sockaddr*)&server_address, 
      sizeof(server_address) 
     ) == -1 
    ) { 
     perror("bind"); 
     exit(EXIT_FAILURE); 
    } 

    if (listen(server_sockfd, 5) == -1) { 
     perror("listen"); 
     exit(EXIT_FAILURE); 
    } 
    fprintf(stderr, "listening on port %d\n", server_port); 

    while (1) { 
     client_len = sizeof(client_address); 
     client_sockfd = accept(
      server_sockfd, 
      (struct sockaddr*)&client_address, 
      &client_len 
     ); 
     while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) { 
      printf("received:\n"); 
      write(STDOUT_FILENO, buffer, nbytes_read); 
      if (buffer[nbytes_read - 1] == '\n') 
       newline_found; 
      for (i = 0; i < nbytes_read - 1; i++) 
       buffer[i]++; 
      write(client_sockfd, buffer, nbytes_read); 
      if (newline_found) 
       break; 
     } 
     close(client_sockfd); 
    } 
    return EXIT_SUCCESS; 
} 

client.c

#define _XOPEN_SOURCE 700 

#include <assert.h> 
#include <stdbool.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include <arpa/inet.h> 
#include <netdb.h> /* getprotobyname */ 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <unistd.h> 

int main(int argc, char **argv) { 
    char buffer[BUFSIZ]; 
    char protoname[] = "tcp"; 
    struct protoent *protoent; 
    char *server_hostname = "127.0.0.1"; 
    char *user_input = NULL; 
    in_addr_t in_addr; 
    in_addr_t server_addr; 
    int sockfd; 
    size_t getline_buffer = 0; 
    ssize_t nbytes_read, i, user_input_len; 
    struct hostent *hostent; 
    /* This is the struct used by INet addresses. */ 
    struct sockaddr_in sockaddr_in; 
    unsigned short server_port = 12345; 

    if (argc > 1) { 
     server_hostname = argv[1]; 
     if (argc > 2) { 
      server_port = strtol(argv[2], NULL, 10); 
     } 
    } 

    /* Get socket. */ 
    protoent = getprotobyname(protoname); 
    if (protoent == NULL) { 
     perror("getprotobyname"); 
     exit(EXIT_FAILURE); 
    } 
    sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto); 
    if (sockfd == -1) { 
     perror("socket"); 
     exit(EXIT_FAILURE); 
    } 

    /* Prepare sockaddr_in. */ 
    hostent = gethostbyname(server_hostname); 
    if (hostent == NULL) { 
     fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname); 
     exit(EXIT_FAILURE); 
    } 
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list))); 
    if (in_addr == (in_addr_t)-1) { 
     fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list)); 
     exit(EXIT_FAILURE); 
    } 
    sockaddr_in.sin_addr.s_addr = in_addr; 
    sockaddr_in.sin_family = AF_INET; 
    sockaddr_in.sin_port = htons(server_port); 

    /* Do the actual connection. */ 
    if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) { 
     perror("connect"); 
     return EXIT_FAILURE; 
    } 
    while (1) { 
     fprintf(stderr, "enter string (empty to quit):\n"); 
     user_input_len = getline(&user_input, &getline_buffer, stdin); 
     if (user_input_len == -1) { 
      perror("getline"); 
      exit(EXIT_FAILURE); 
     } 
     if (user_input_len == 1) { 
      close(sockfd); 
      break; 
     } 
     if (write(sockfd, user_input, user_input_len) == -1) { 
      perror("write"); 
      exit(EXIT_FAILURE); 
     } 
     while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) { 
      write(STDOUT_FILENO, buffer, nbytes_read); 
      if (buffer[nbytes_read - 1] == '\n') { 
       fflush(stdout); 
       break; 
      } 
     } 
    } 
    free(user_input); 

    exit(EXIT_SUCCESS); 
} 

On GitHub with a Makefile. Probado en Ubuntu 15.10.

La longitud del mensaje

Los read llamadas en el cliente y el servidor se ejecutan dentro de los bucles while.

Al igual que cuando se leen archivos, el sistema operativo puede dividir los mensajes arbitrariamente para agilizar las cosas, p. un paquete puede llegar mucho antes que el otro.

Por lo tanto, el protocolo debe especificar una convención de dónde se detienen los mensajes. Los métodos comunes incluyen:

  • una cabecera con un indicador de longitud (por ejemplo HTTP Content-Length)
  • una cadena única que termina mensajes. Aquí usamos \n.
  • el servidor cierra la conexión: HTTP permite que https://stackoverflow.com/a/25586633/895245. Limitado, por supuesto, ya que el siguiente mensaje requiere una reconexión.

Próximos pasos

Este ejemplo es limitada debido a que:

  • el servidor sólo puede manejar una conexión de cliente a la vez
  • comunicación se sincroniza con sencillez. Por ejemplo, en una aplicación de chat P2P, el servidor (otra persona) podría enviar mensajes en cualquier momento.

La resolución de esos problemas requiere el enhebrado y posiblemente otras llamadas como poll.

Cuestiones relacionadas