Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

 W. R. Stevens  : Глава 3

Введение в сокеты

Сокет можно передавать в 2-х направлениях : от процесса к ядру и от ядра к процессу. Текстовое представление адреса сокета в двоичное выполняют функции inet_addr и inet_ntoa, эти 2 функции работают в IPv4. Еще 2 функции - inet_pton и inet_ntop - работают как в IPv4 , так и в IPv6.

Большинство функций сокетов используют в качестве аргумента указатель на структуру адреса сокета. Каждый набор протоколов определяет свою собственную структуру адреса сокета. Имена этих структур начинаются с sockaddr_ и заканчиваются уникальным суффиксом.

Структура адреса сокета IPv4 - sockaddr_in :


 struct in_addr {
 	in_addr_t s_addr;
 				};
 struct sockaddr_in {
 	uint8_t  		sin_len;
 	sa_family_t 	sin_family;
 	in_port_t 		sin_port;
 	struct in_addr  sin_addr;
 	char 			sin_zero[8];
 					};
 

 Например  , в 10-й SUSE это прописано в /include/linux/in.h :
 struct in_addr {
         __u32   s_addr;
 };
 struct sockaddr_in {
   sa_family_t           sin_family;     /* Address family               */
   unsigned short int    sin_port;       /* Port number                  */
   struct in_addr        sin_addr;       /* Internet address             */
 
   /* Pad to size of `struct sockaddr'. */
   unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
                         sizeof(unsigned short int) - sizeof(struct in_addr)];
 };
 Поле sin_len здесь просто не поддерживается. Если даже оно и присутствует , его не обязательно инициализировать.
 
 Минимальный размер сокета для любой реализации - 16 байт.
 in_addr_t   - 4 байта
 in_port_t   - 2 байта
 sa_family_t - 1 или 2 байта
 
И адрес , и порт в этой структуре хранятся в том порядке байт , который принят в интернете .

Есть 4 функции , передающие сокет от процесса к ядру :


 bind
 connect
 sendto
 sendmsg
 
Есть 5 функций , передающих сокет обратно :

 accept
 recvfrom
 recvmsg
 getpeername
 getcockname
 
Во всех функциях сокет всегда передается по ссылке. Тип передаваемого указателя формируется с помощью структуры адреса сокета :

 struct sockaddr {
 	 uint8_t         sa_len; 
         sa_family_t     sa_family;      /* address family, AF_xxx       */
         char            sa_data[14];    /* 14 bytes of protocol address */
 };
  
  Пример использования :
 
 	struct sockaddr_in serv;
 	bind(fd , (struct sockaddr * ) &serv , sizeof(ser))
 
 
 

 Яковлев С: В 10-й SUSE это прописано в /include/linux/socket.h :
 struct sockaddr {
         sa_family_t     sa_family;      /* address family, AF_xxx       */
         char            sa_data[14];    /* 14 bytes of protocol address */
 };
 
Обьявление функции выглядит так :

 int bind(int , struct soxkaddr *, socklen_t);
 
Реализация функции выглядит так :

 struct sockaddr_in serv;
 int bind(sockfd , (struct soxkaddr *) &serv, sizeof(serv));
 
Структура сокета IPv6 :

 struct in6_addr
 {
 	uint8t  s6_addr[16];
 };
 
 #define SIN6_LEN
 
 struct sockaddr_in6 {
 		uint8t 			sin6_len;
 		sa_family_t 		sin6_family;
 		in_port_t		sin6_port;
 		uint32_t		sin6_flowinfo;
 		struct in6_addr 	sin6_addr;
 };
 

 Яковлев С: В 10-й SUSE это прописано в /include/linux/in6.h :
 struct in6_addr
 {
         union
         {
                 __u8            u6_addr8[16];
                 __u16           u6_addr16[8];
                 __u32           u6_addr32[4];
         } in6_u;
 #define s6_addr                 in6_u.u6_addr8
 #define s6_addr16               in6_u.u6_addr16
 #define s6_addr32               in6_u.u6_addr32
 };
 
 struct sockaddr_in6 {
         unsigned short int      sin6_family;    /* AF_INET6 */
         __u16                   sin6_port;      /* Transport layer port # */
         __u32                   sin6_flowinfo;  /* IPv6 flow information */
         struct in6_addr         sin6_addr;      /* IPv6 address */
         __u32                   sin6_scope_id;  /* scope id (new in RFC2553) */
 };
 
IPv6 относится к AF_INET6 , IPv4 - AF_INET.
Сокет IPv6 имеет фиксированную длину в 24 байта , т.е. он в полтора раза больше IPv4.

В функцию , кроме того , что сам сокет передается по ссылке , также передается длина сокета. Имеется 2 варианта способа передачи второго параметра - длины сокета :

1. Передача от процесса к ядру :


  	bind
 	connect
 	sendto
 
Здесь способ передачи 2-го параметра - по значению :

 	struct sockaddr_in serv;
 	connect(sockfd,(SA *) &serv,sizeof(serv));
 
2. Передача от ядра к процессу :

 	accept
 	recvfrom
 	getsockname
 	getpeername
 
Здесь 2-й аргумент передается по ссылке , в не по значению - это говорит о том , что размер может быть изменен ядром :

 	struct sockaddr_un cli;
 	socklen_t len;
 	len = sizeof(cli);
 	getpeername(unixfd,(SA *) &cli , &len);
 
Есть сокеты , которые имеют переменную , а не фиксированную длину.

Определение порядка байтов

2 байта в памяти можно положить 2-мя способами :
1. little-endian - первым идет младший байт
2. big-endian - первым идет старший байт
Способ упорядочивания байтов в системе - host byte order - определяется в следующей программе :

 // intro/byteorder.c
 
 #include	"unp.h"
 
 int main(int argc, char **argv)
 {
 	union {
 	  short  s;
       char   c[sizeof(short)];
     } un;
 
 	un.s = 0x0102;
 	printf("%s: ", CPU_VENDOR_OS);
 	if (sizeof(short) == 2) {
 		if (un.c[0] == 1 && un.c[1] == 2)
 			printf("big-endian\n");
 		else if (un.c[0] == 2 && un.c[1] == 1)
 			printf("little-endian\n");
 		else
 			printf("unknown\n");
 	} else
 		printf("sizeof(short) = %d\n", sizeof(short));
 
 	exit(0);
 }
 
2-байтное значение 0x0102 помещается в переменную типа short и проверяем значения 2-х байтов этой переменной - c[0] и c[1].
Пример можно взять тут

В сетевых протоколах используется обратный порядок байтов - network byte order. И задача программиста - привести порядок байтов узла в сетевой , а потом обратно. Для этого используются 2 функции , возврающие значение , записанное в сетевом порядке байтов :


 	uint16_t htons(uint16_t host16bitvalue);
 	uint32_t htonl(uint32_t host32bitvalue);
 
2 функции , возврающие значение , записанное в порядке байтов узла :

 	uint16_t ntohs(uint16_t host16bitvalue);
 	uint32_t ntohl(uint32_t host32bitvalue);
 
По определению байт - это 8 бит . Но 8 бит - это также октет .

Функции управления байтами

Существуют 2 группы функций для работы с многобайтовыми полями.

1. Первая группа функция взята из стандарта BSD.


 	void bzero(void *dest,size_t nbytes);
 	void bcopy(const void *src,void *dest,size_t nbytes);
 	int bcmp(const void *ptr1,const void *ptr2,size_t nbytes);
 
bzero - обнуляет заданное число байтов в указанной облачти памяти. bcopy - копирует заданное число байтов из источника в приемник. bcmp - сравнивает 2 байтовых последовательности.

2. Вторая группа функций взята из стандарта ANSI C.


 	void *memset(void *dest,int c , size_t len);
 	void *memcpy(void *dest,int c , size_t len);
 	int  memcmp(const void *ptr1,const void *ptr2,size_t nbytes);
 
Есть некоторые различия в работе этих функций и 1-й группы.

Функции преобразования адресов

Существуют 2 группы таких функций :

1. Функции , работающие только с IPv4 :


 	inet_aton
 	inet_ntoa
 	inet_addr
 
Они преобразуют адрес из точечно-десятичной (10.10.11.12) в 4-х байтную двоичную или наоборот. inet_addr считается устаревшей.

2. Функции , работающие и с IPv4 , и с IPv6 :


 	inet_pton
 	inet_ntop
 
Оставьте свой комментарий !

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

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