Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org
Сокет - коммуникационный канал между процессами . При создании сокета нужно определить 2 вещи :
   1. communication style   
   2. тип протокола 
communication style определяет структуру пакетов , возможность потери данных , число соединений , namespace . Например , TCP-protocol имеет byte-тип коммуникации и Internet namespace . Communication Styles бывают следующих типов :
   1. SOCK_STREAM
   2. SOCK_DGRAM
   3. SOCK_RAW
Сокет создается с помощью функции socket и при этом не имеет адреса , который позже можно присвоить с помощью функции bind . Для назначения адреса используется структура struct sockaddr * . В ней есть следующие члены :
    short int sa_family
    char sa_data[14]
Формат адреса может быть следующих типов :
  AF_LOCAL
  AF_UNIX
  AF_FILE
  AF_INET
  AF_INET6
  AF_UNSPEC     
Прочитать арес из этой структуры можно с помощью функции
 int getsockname (int socket, struct sockaddr *addr, socklen_t *length-ptr)
Для local namespace адресом сокета служит имя файла . К такому сокету нельзя законнектиться с другой машины . Internet Namespace имеет имя PF_INET . При этом сокет включает в себя адрес машины коннекта и порт машины коннекта .
  Базовая структура - struct sockaddr_in
 Она включает члены :
 sa_family_t sin_family
 struct in_addr sin_addr
 unsigned short int sin_port
  Каждый компьютер имееи host address вида `128.52.46.32' и 
  host name вида `mescaline.gnu.org'
 База данных сервисов хранится обычно в файле  /etc/services и имеет вид :
 systat		11/udp		users
 daytime		13/tcp
 qotd		17/tcp		quote
Для получения информации можно использовать getservbyname или getservbypor . Для потоковых сокетов протоколом по умолчанию является TCP . Для датаграмм - UDP . База данных протоколов обычно находится в /etc/protocols . Ниже привелен пример создания tcp-протокола :
 
 #include < stdio.h >
 #include < stdlib.h >
 #include < sys/socket.h >
 #include < netinet/in.h >
 int make_socket (uint16_t port)
 {
   int sock;
   struct sockaddr_in name;
   /* Create the socket. */
   sock = socket (PF_INET, SOCK_STREAM, 0);
   if (sock < 0)
    {
      perror ("socket");
      exit (EXIT_FAILURE);
    }
    /* Give the socket a name. */
    name.sin_family = AF_INET;
    name.sin_port = htons (port);
    name.sin_addr.s_addr = htonl (INADDR_ANY);
    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
    {
       perror ("bind");
       exit (EXIT_FAILURE);
    }
    return sock;
 }
 
В следующем примере показано , как заполнить структуру sockaddr_in
 
 #include < stdio.h >
 #include < stdlib.h >
 #include < sys/socket.h >
 #include < netinet/in.h >
 #include < netdb.h >
 
 void init_sockaddr (struct sockaddr_in *name,const char *hostname,uint16_t port)
   {
     struct hostent *hostinfo;
     name->sin_family = AF_INET;
     name->sin_port = htons (port);
     hostinfo = gethostbyname (hostname);
     if (hostinfo == NULL) 
     {
       fprintf (stderr, "Unknown host %s.\n", hostname);
       exit (EXIT_FAILURE);
     }
     name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
 }
 
  Следующий пример создает клиентский сокет и посылает серверу строку данных :
 
 #include < stdio.h >
 #include < errno.h >
 #include < stdlib.h >
 #include < unistd.h >
 #include < sys/types.h >
 #include < sys/socket.h >
 #include < netinet/in.h >
 #include < netdb.h >
 #define PORT            5555
 #define MESSAGE         "Yow!!! Are we having fun yet?!?"
 #define SERVERHOST      "mescaline.gnu.org"
 void write_to_server (int filedes)
 {
    int nbytes;
    nbytes = write (filedes, MESSAGE, strlen (MESSAGE) + 1);
    if (nbytes < 0)
   {
     perror ("write");
     exit (EXIT_FAILURE);
   }
 }
  int main (void)
  {
    extern void init_sockaddr (struct sockaddr_in *name,
                                 const char *hostname, uint16_t port);
    int sock;
    struct sockaddr_in servername;
    /* Create the socket. */
    sock = socket (PF_INET, SOCK_STREAM, 0);
    if (sock < 0)
     {
       perror ("socket (client)");
       exit (EXIT_FAILURE);
     }
     /* Connect to the server. */
     init_sockaddr (&servername, SERVERHOST, PORT);
     if (0 > connect (sock,(struct sockaddr *) &servername,sizeof (servername)))
     {
       perror ("connect (client)");
       exit (EXIT_FAILURE);
     }
       /* Send data to the server. */
     write_to_server (sock);
     close (sock);
     exit (EXIT_SUCCESS);
     }
 
Следующий пример создает сервер для предыдущего клиента . В отличие от клтента , он рассчитан для работы на множество потоков , для чего используется функция select .
 
 #include < stdio.h >
 #include < errno.h >
 #include < stdlib.h >
 #include < unistd.h >
 #include < sys/types.h >
 #include < sys/socket.h >
 #include < netinet/in.h >
 #include < netdb.h >
 #define PORT    5555
 #define MAXMSG  512
 
 int read_from_client (int filedes)
 {
   char buffer[MAXMSG];
   int nbytes;
   nbytes = read (filedes, buffer, MAXMSG);
   if (nbytes < 0)
     {
       /* Read error. */
       perror ("read");
       exit (EXIT_FAILURE);
     }
   else if (nbytes == 0)
   /* End-of-file. */
     return -1;
   else
   {
      /* Data read. */
      fprintf (stderr, "Server: got message: `%s'\n", buffer);
      return 0;
    }
   }
  int main (void)
 {
   extern int make_socket (uint16_t port);
   int sock;
   fd_set active_fd_set, read_fd_set;
   int i;
   struct sockaddr_in clientname;
   size_t size;
   /* Create the socket and set it up to accept connections. */
   sock = make_socket (PORT);
   if (listen (sock, 1) < 0)
    {
     perror ("listen");
     exit (EXIT_FAILURE);
    }
    /* Initialize the set of active sockets. */
     FD_ZERO (&active_fd_set);
     FD_SET (sock, &active_fd_set);
     while (1)
       {
         /* Block until input arrives on one or more active sockets. */
         read_fd_set = active_fd_set;
         if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
         {
           perror ("select");
           exit (EXIT_FAILURE);
         }
       /* Service all the sockets with input pending. */
       for (i = 0; i < FD_SETSIZE; ++i)
         if (FD_ISSET (i, &read_fd_set))
           {
           if (i == sock)
             {
                 /* Connection request on original socket. */
                 int new;
                 size = sizeof (clientname);
                 new = accept (sock,
                 (struct sockaddr *) &clientname, &size);
                 if (new < 0)
                 {
                   perror ("accept");
                   exit (EXIT_FAILURE);
 	        }
                 fprintf (stderr,"Server: connect from host %s, port %hd.\n",
 		inet_ntoa (clientname.sin_addr), ntohs (clientname.sin_port));
                  FD_SET (new, &active_fd_set);
             }
            else
            {
                /* Data arriving on an already-connected socket. */
                if (read_from_client (i) < 0)
                {
                  close (i);
                  FD_CLR (i, &active_fd_set);
                }
             }
    }
  }
 }
 
Для того , чтобы в очередь потоков вставить поток с более высоким приоритетом , ему присваивают статус out-of-band . Функция для подобной маркировки :
 intdiscard_until_mark (int socket)
 {
   while (1)
   {
     /* This is not an arbitrary limit; any size will do.  */
     char buffer[1024];
     int atmark, success;
     /* If we have reached the mark, return.  */
     success = ioctl (socket, SIOCATMARK, &atmark);
     if (success < 0)
        perror ("ioctl");
     if (result)
       return;
     /* Otherwise, read a bunch of ordinary data and discard it.
        This is guaranteed not to read past the mark
        if it starts before the mark.  */
     success = read (socket, buffer, sizeof buffer);
     if (success < 0)
       perror ("read");
   }
 }
 
Чтение out-of-band :
 
 struct buffer
 {
   char *buf;
   int size;
   struct buffer *next;
 };
   /* Read the out-of-band data from SOCKET and return it
      as a `struct buffer', which records the address of the data
      and its size.
      It may be necessary to read some ordinary data
      in order to make room for the out-of-band data.
      If so, the ordinary data are saved as a chain of buffers
      found in the `next' field of the value.  */
 
   struct buffer *
   read_oob (int socket)
   {
    struct buffer *tail = 0;
    struct buffer *list = 0;
    while (1)
    {
     /* This is an arbitrary limit.
        Does anyone know how to do this without a limit?  */
     #define BUF_SZ 1024
     char *buf = (char *) xmalloc (BUF_SZ);
     int success;
     int atmark;
     /* Try again to read the out-of-band data.  */
     success = recv (socket, buf, BUF_SZ, MSG_OOB);
     if (success >= 0)
      {
        /* We got it, so return it.  */
        struct buffer *link  = (struct buffer *) xmalloc (sizeof (struct buffer));
       link->buf = buf;
       link->size = success;
       link->next = list;
       return link;
      }
      /* If we fail, see if we are at the mark.  */
      success = ioctl (socket, SIOCATMARK, &atmark);
      if (success < 0)
            perror ("ioctl");
      if (atmark)
          {
            /* At the mark; skipping past more ordinary data cannot help.
             So just wait a while.  */
            sleep (1);
            continue;
          }
      /* Otherwise, read a bunch of ordinary data and save it.
        This is guaranteed not to read past the mark
        if it starts before the mark.  */
      success = read (socket, buf, BUF_SZ);
      if (success < 0)
        perror ("read");
       /* Save this data in the buffer list.  */
        {
           struct buffer *link = (struct buffer *) xmalloc (sizeof (struct buffer));
           link->buf = buf;
           link->size = success;
           /* Add the new link to the end of the list.  */
           if (tail)
             tail->next = link;
           else
           list = link;
           tail = link;
         }
     }
 }
 
Пример серверной UDP-датаграммы :
 
 #include < stdio.h >
 #include < errno.h >
 #include < stdlib.h >
 #include < sys/socket.h >
 #include < sys/un.h >
 #define SERVER  "/tmp/serversocket"
 #define MAXMSG  512
 int main (void)
 {
   int sock;
   char message[MAXMSG];
   struct sockaddr_un name;
   size_t size;
   int nbytes;
   /* Remove the filename first, it's ok if the call fails */
   unlink (SERVER);
   /* Make the socket, then loop endlessly. */
   sock = make_named_socket (SERVER);
   while (1)
     {
       /* Wait for a datagram. */
       size = sizeof (name);
       nbytes = recvfrom (sock, message, MAXMSG, 0,(struct sockaddr *) & name, &size);
       if (nbytes < 0)
        {
            perror ("recfrom (server)");
            exit (EXIT_FAILURE);
        }
        /* Give a diagnostic message. */
       fprintf (stderr, "Server: got message: %s\n", message);
       /* Bounce the message back to the sender. */
       nbytes = sendto (sock, message, nbytes, 0,(struct sockaddr *) & name, size);
       if (nbytes < 0)
       {
         perror ("sendto (server)");
         exit (EXIT_FAILURE);
       }
     }
   }
 
Вместо написания серверной части можно использовать для прослушивания демона inetd . Для ее настройки существует конфиг-файл /etc/inetd.conf .
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье