网络编程——socket定义和地址格式
目录
- socket 是什么?
- 套接字地址格式
1. socket 是什么?
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b18dea4edca2347e8ce5c691e689f666.png)
- 网络编程中, socket 翻译为套接字或套接口,指可以通过插口接入的方式,快速完成网络连接和数据收发。
- 上图表示网络编程中,客户端和服务器工作的核心逻辑。
- 服务器端,在客户端发起连接请求之前,服务器端必须初始化好。
- 初始化 socket。
- 执行 bind 函数,将服务能力绑定在一个可知的地址和端口上。
- 然后执行 listen 操作,将原先的 socket 转化为服务端的 socket。
- 服务端最后阻塞在 accept 上等待客户端请求。
- 当服务器端已经准备就绪,客户端需要先初始化 socket,再执行 connect 向服务器端的地址和端口发起连接请求,这里的地址和端口必须是客户端预先知道的。connect请求就是TCP 三次握手(Three-way Handshake)。
- 三次握手完成后,客户端和服务器端建立连接,就进入了数据传输过程。
- 客户端进程向操作系统内核发起 write 字节流写操作,内核协议栈将字节流通过网络设备传输到服务器端,服务器端从内核得到信息,将字节流从内核读入到进程中,并开始业务逻辑的处理,完成之后,服务器端再将得到的结果以同样的方式写给客户端。
- 所以一旦连接建立,数据的传输就不再是单向的,而是双向的。
- 当客户端完成和服务器端的交互后,比如执行一次 Telnet 操作,或者一次 HTTP 请求,需要和服务器端断开连接时,就会执行 close 函数。
- 操作系统内核此时会通过原先的连接链路向服务器端发送一个 FIN 包,服务器收到之后执行被动关闭,这时候整个链路处于半关闭状态。
- 此后,服务器端也会执行 close 函数,整个链路才会真正关闭。
- 半关闭的状态下,发起 close 请求的一方在没有收到对方 FIN 包之前都认为连接是正常的。
- 而在全关闭的状态下,双方都感知连接已经关闭。
- 以上所有的操作,都是通过 socket 来完成的。无论是客户端的 connect,还是服务端的 accept,或者 read/write 操作等,socket 是用来建立连接,传输数据的唯一途径。
2. 套接字地址格式
- 在使用套接字时,首先要解决通信双方寻址的问题,需要套接字的地址建立连接,套接字的地址格式如下。
1. 通用套接字地址格式
- 套接字的通用地址结构:
typedef unsigned short int sa_family_t;
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];
};
- 第一个字段是地址族,表示使用什么样的方式对地址进行解释和保存。地址族在 glibc 里的定义非常多,常用的有以下几种:
- AF_LOCAL:表示的是本地地址,对应的是 Unix 套接字,一般用于本地 socket 通信,也可以写成 AF_UNIX、AF_FILE。
- AF_INET:因特网使用的 IPv4 地址。
- AF_INET6:因特网使用的 IPv6 地址。
- AF_ 表示的含义是 Address Family, PF_表示Protocol Family协议族,比如 PF_INET、PF_INET6 等。用 AF_xxx 的值来初始化 socket 地址,用 PF_xxx 的值来初始化 socket。在 <sys/socket.h> 头文件中可以看到,这两个值是一一对应的。
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
- sockaddr 是一个通用的地址结构,适用于多种地址族。
2. IPv4 套接字格式地址
- 常用的 IPv4 地址族的结构:
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in
{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
- 和 sockaddr 一样,都有一个 16-bit 的 sin_family 字段,对于 IPv4 来说值为 AF_INET。
- 端口号,端口号最多是 16-bit,即最大支持 2 的 16 次方(65535)。
- 保留端口是约定俗成的,已经被对应服务使用的端口,比如 ftp 的 21 端口,ssh 的 22 端口,http 的 80 端口等。
- 一般大于 5000 的端口可以作为自己应用程序的端口使用。
- 下面是 glibc 定义的保留端口。
enum
{
IPPORT_ECHO = 7,
IPPORT_DISCARD = 9,
IPPORT_SYSTAT = 11,
IPPORT_DAYTIME = 13,
IPPORT_NETSTAT = 15,
IPPORT_FTP = 21,
IPPORT_TELNET = 23,
IPPORT_SMTP = 25,
IPPORT_TIMESERVER = 37,
IPPORT_NAMESERVER = 42,
IPPORT_WHOIS = 43,
IPPORT_MTP = 57,
IPPORT_TFTP = 69,
IPPORT_RJE = 77,
IPPORT_FINGER = 79,
IPPORT_TTYLINK = 87,
IPPORT_SUPDUP = 95,
IPPORT_EXECSERVER = 512,
IPPORT_LOGINSERVER = 513,
IPPORT_CMDSERVER = 514,
IPPORT_EFSSERVER = 520,
IPPORT_BIFFUDP = 512,
IPPORT_WHOSERVER = 513,
IPPORT_ROUTESERVER = 520,
IPPORT_RESERVED = 1024,
IPPORT_USERRESERVED = 5000
- IPv4 地址是一个 32-bit 的字段,最多支持的地址数是 2 的 32 次方,大约是 42 亿。
3. IPv6 套接字地址格式
- IPv6 的地址结构:
struct sockaddr_in6
{
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
- 整个结构体长度是 28 个字节,其中流控信息和域 IP 一个在 glibc 的官网上没出现,另一个是当前未使用的字段。地址族是 AF_INET6,端口同 IPv4 地址一样,关键的地址从 32 位升级到 128 位,完全解决了寻址数字不够的问题。
- IPv4 和 IPv6 的地址格式都是因特网套接字的格式,还有一种本地套接字格式,用来作为本地进程间的通信, 即 AF_LOCAL。
struct sockaddr_un {
unsigned short sun_family;
char sun_path[108];
};
4. 几种套接字地址格式比较
- 几种地址的比较见下图,IPv4 和 IPv6 套接字地址结构的长度是固定的,而本地地址结构的长度是可变的。
![image.png](https://img-blog.csdnimg.cn/img_convert/df5ce4d0087a13947b4ebae498e42584.png#averageHue=#f6f6f6&clientId=u9c495fc1-dbe4-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=637&id=u7d3e6575)