Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...5170 
 Trees...940 
 Максвелл 3...871 
 Go Web ...823 
 William Gropp...803 
 Ethreal 3...787 
 Gary V.Vaughan-> Libtool...773 
 Ethreal 4...771 
 Rodriguez 6...766 
 Ext4 FS...755 
 Clickhouse...754 
 Steve Pate 1...754 
 Ethreal 1...742 
 Secure Programming for Li...732 
 C++ Patterns 3...716 
 Ulrich Drepper...698 
 Assembler...695 
 DevFS...662 
 Стивенс 9...650 
 MySQL & PosgreSQL...632 
 
  01.01.2024 : 3621733 посещений 

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 .
Оставьте свой комментарий !

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

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