Socket 介绍

IPV4 套接字地址结构

<netstat/in.h>
struct in_addr {
  in_addr_t   s_addr;           /*u32-bit IPv4 address;network byte ordered*/
};

struct sockaddr_in {
  uint8_t         sin_len;      /* length of structure (16) */
  sa_family_t     sin_family;   /* u8-bit(support length) or u16-bit(not support)*/
  in_port_t       sin_port;     /* u16-bit TCP or UDP port number;network byte ordered*/
  struct in_addr  sin_addr;     /* in_addr structure*/
  char            sin_zero[8];  /* unused */
};

sin_len字段简化了处理不同长度套接字地址结构的情况。用于内核处理不同协议的套接字。除了路由套接字之外,我们不使用这个字段。
有四个函数将套接字地址从进程传给内核bind,connect,sendto,sendmsg,这些函数通过参数显式设置sin_len参数;另外五个函数将套接字地址从内核传给进程,accept,recvform,recvmsg,getpeername,getsockname,sin_len在返回进程之前就被设置。
POSIX只需要sin_family,sin_addr,sin_port。大多数实现都增加了sin_zero成员,因此所有的socket address结构都至少为16字节。IPV4地址及端口号都存储为网络字节序,一般使用时都要与主机字节序相互转换。
32位IPV4地址有两种访问方式:sin_addr,将IPV4地址视为in_addr结构;sin_addr.s_addr将IPV4地址视为in_addr_t(uint32_t),当传参数时,应当正确使用IPV4地址。早期实现中,in_addr结构是多个结构的union,允许访问32位中的每个字节。用于A,B,C类型网络获取地址。随着子网出现以及无类型寻址,union结构被废弃。sin_zero字段不使用,总是设为0。实际上在填充sockaddr_in之前,将整个结构填为0。

套接字地址总是引用传参。但是套接字函数会处理多种协议。因此提出了通用的套接字地址结构。

<sys/socket.h>
struct sockaddr {
  uint8_t      sa_len;
  sa_family_t  sa_family;    /* address family: AF_xxx value */
  char         sa_data[14];  /* protocol-specific address */
};

套接字函数都使用通用套接字地址作为参数。一般情况下这样使用:

int bind(int, struct sockaddr *, socklen_t); /*prototype*/

struct sockaddr_in  serv;      /* IPv4 socket address structure */
/* fill in serv{} */
bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));

IPV6套接字地址结构 (todo)

使用了新的通用结构,支持所有地址类型

struct sockaddr_storage {
  uint8_t      ss_len;       /* length of this struct (implementation dependent) */
  sa_family_t  ss_family;    /* address family: AF_xxx value */
  /* implementation-dependent elements to provide:
   * a) alignment sufficient to fulfill the alignment requirements of
   *    all socket address types that the system supports.
   * b) enough storage to hold any type of socket address that the
   *    system supports.
   */
};

sockaddr_storage支持对齐需求。除了ss_family和ss_len,其他字段都是不透明的,需要根据ss_family进行转换。

套接字地址结构比较

a
IPV4和IPV6结构是定长的,Unix domain和datalink结构是变长的。因此当我们向套接字函数传递套接字指针时,需要传递长度。从4.3BSD开始,所有套接字地址都增加了长度字段,可以将长度包含着结构体之中而不用向函数传递参数。

Value-Result参数

前面提到,套接字地址结构通过引用传递。长度参数的传递方式取决于传递方向。并且,
bind(), connect(), sendto()从进程将结构指针以及长度值传给内核,因此内核知道从哪拷贝多少数据。
accept(), recvfrom(), getsockname(),getpeername()从内核传给进程,结构及长度都使用指针传递。当函数调用时,进程告诉内核结构的长度;当返回时,内核告诉进程实际存储的数据长度。另外两个函数recvmsg, sendmsg将长度作为结构成员而不是参数。

字节序

小端:低位在低地址;大端:高位在低地址。网络字节序使用大端。
a

#include <netinet/in.h>
 
uint16_t htons(uint16_t ) ; 
uint32_t htonl(uint32_t ) ;/*Both return: value in network byte order*/

uint16_t ntohs(uint16_t ) ; 
uint32_t ntohl(uint32_t ) ; /*Both return: value in host byte order*/
/*do not care about the actual byte order*/

字符串与地址转换函数

#include <arpa/inet.h> 
int inet_aton(const char *strptr, struct in_addr *addrptr); /*Returns: 1 if string was valid, 0 on error*/
in_addr_t inet_addr(const char *strptr);/*Returns: 32-bit binary network byte ordered IPv4 address; INADDR_NONE if error,deprecated*/
char *inet_ntoa(struct in_addr inaddr);/*Returns: pointer to dotted-decimal string*/

这三个函数转换IPV4地址的字符串与32位网络字节序值。inet_aton将字符串转换为32位网络字节;inet_addr发生错误时将返回INADDR_NONE(0xffffffff),因此无法处理255.255.255.255,此函数已经废弃;inet_ntoa返回322位网络字节对应的字符串,返回值在静态内存中,此函数是不可重入的。

#include <arpa/inet.h>
 
int inet_pton(int family, const char *strptr, void *addrptr); /*Returns: 1 if OK, 0 if input not a valid presentation format, -1 on error*/

const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len); /*Returns: pointer to result if OK, NULL on error*/

这两个函数可以处理IPV4或IPV6地址。p代表presentation,n代表numeric。family是AF_INET,AF_INET6之一。‌inet_ntop的len参数指定缓冲区的长度,使用两个宏来指定大小:

#define INET_ADDRSTRLEN       16       /* for IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN      46       /* for IPv6 hex string */
原文地址:https://www.cnblogs.com/zyfgs2012/p/4074528.html