套接字
套接字描述符
套接字是通信端点的抽象。就比如使用文件描述符访问文件,应用程序用套接字描述符访问套接字。
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- 参数说明:
- domain (域): 确定通信的特性,包括地址格式。
套接字通信域(部分表)
域 | 描述 |
---|---|
AF_INET | IPv4 因特网域 |
AF_INET6 | IPv6 因特网域 |
- type: 确定套接字类型,进一步确定通信特征。
图:套接字类型
类型 | 描述 |
---|---|
SOCK_DGRAM | 固定长度的、无连接的、不可靠的报文传递 |
SOCK_RAM | IP 协议的数据报接口 |
SOCK_SEQPACKET | 固定长度的、有序的、可靠的、面向连接的报文传递 |
SOCK_STREAM | 有序的、可靠的、双向的、面向连接的字节流 |
- protocol: 通常为0, 表示为给定的域和套接字类型选择默认的协议。当对同一域和套接字类型支持多个协议式,可以使用 protocol 选择一个特定的协议。在 AF_INET 通信域中,套接字类型 SOCK_STREAM 的默认协议是 TCP 协议;套接字类型 SOCK_DGRAM 默认协议是 UDP。
协议 | 描述 |
---|---|
IPPROTO_IP | IPv4 网际协议 |
IPPROTO_IPV6 | IPv6 网际协议 |
IPPROTO_ICMP | 因特网控制报文协议 |
IPPROTO_RAW | 原始 IP 数据包协议 |
IPPROTO_TCP/6 | 传输控制协议 |
IPPROTO_UDP/17 | 用户数据报协议 |
寻址
1. 字节序
2. 地址格式
大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义它自己的套接字地址结构。这些结构的名字均以 sockaddr_
开头,并以对应每个协议族的唯一后缀结尾。
- 通用套接字地址结构
一个地址标识一个特定通信域的套接字端点,地址格式与这个特定的通信域相关。为了使不同的格式地址能够传入到套接字函数,地址会被强制转换成一个通用的地址结构体 sockaddr:
struct sockaddr
{
uint8_t sa_len;
unsigned short int sa_family; /* address family: AF_xxx */
unsigned char sa_data[14]; /* variable-length address */
};
对于开发人员说,通用套接字地址唯一的作用就是对指向特定于协议的套接字地址结构的指针执行类型强制转换。
- IPv4 套接字地址结构
IPv4 套接字地质结构通常也称为“网际套接字地质结构”,它以 sockaddr_in 命名,定义在<netinet/in.h>
头文件中。
路径: /usr/include/netinet/in.h
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr; /* 32-bit IPv4 address */
/* network byte ordered */
};
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
uint8_t sin_len; /* length of structure (16) */
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[8];
};
-
对套接字地址结构体的说明:
(1)长度字段 sin_len 是为了增加对 OSI 协议的支持而添加的。它是后来被更新之后的,本来第一个字段是 sin_family,它是一个无符号短整数(unsigned short)。并不是所有的内核都会有的字段,而且 POSIX 规范也不要求有这个成员。
(2)即使有长度字段,我们也无须设置和检查它,除非设计路由套接字。
(3)POSIX 规范只需要这个结构中3个字段:sin_family, sin_addr 和 sin_port。对于额外的结构字段也是可以接受的,几乎所有的实现都添加了 sin_zero 字段,用来填充这个结构。所以所有的套接字地质结构大小至少16字节。
(4)IPv4 地址和 TCP 或 UDP 端口号在套接字地址结构中总是以网络字节序来存储的。在使用这些字段时候,必须要严格按照这个要求来。
(5)sin_zero 字段未曾使用,不过在填写这种套接字地址结构时,我们总是把该字段置为0。
(6)套接字地址结构仅在给定主机上使用:虽然结构中的某些字段用在不同主机之间的通信中,但是结构本身并不在主机之间传递。- IPv6 套接字地址结构(不在详细赘述)
535 /* IPv6 packet information. */
536 struct in6_pktinfo
537 {
538 struct in6_addr ipi6_addr; /* src/dst IPv6 address */
539 unsigned int ipi6_ifindex; /* send/recv interface index */
540 };
套接字地址结构的比较
字节排序函数
由于 IP 地址和 TCP 或 UDP 端口在网络传输中均要使用网络字节序,那么如果我们的主机字节序和网络字节序不同时就要转换。
俩种字节序的转换函数:
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
均返回:网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint16_t net32bitvalue);
均返回:主机字节序的值
说明:
h 代表 host, n 代表 network, s 代表 short, l 代表 long。