network_communication

socket 编程

大端序和小端序

PC 上是小端序(低位在低地址,高位在高地址,易于计算机处理)

网络传输是大端序(低位在高地址,高位在低地址,符合人类的阅读习惯)

发送端总是把要发送的数据转化为大端字节序数据后再发送

socket 地址

通用socket 地址 sockaddr

1
2
3
4
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};

专用socket地址

UNIX本地协议族使用如下专用socket地址结构体:

1
2
3
4
struct sockaddr_un {
sa_family_t sin_family; /* 地址族: AF_UNIX */
char sun_path[108]; /* 文件路径名 */
};

TCP/IP协议族有socket_in和sockaddr_in6两个专业socket地址结构体,它们分别用于IPv4和IPv6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct sockadddr_in{
sa_family_t sun_family; /* 地址族: AF_INET */
u_int16_t sin_port; /* 端口号,要用网络字节序表示 */
struct in_addr sin_addr /* IPv4地址结构体 */
};
struct in_addr {
u_int32_t s_addr; /* IPv4地址,要用网络字节序号表示 */
};
struct sockaddr_in6 {
sa_family_t sin6_family; /* 地址族: AF_INET6 */
u_int16_t sin6_port; /* 端口号,要用网络字节序表示 */
u_int32_t sin6_flowinfo; /* 流信息,应设置为0 */
struct in6_addr sin6_addr; /* IPv6地址结构体 */
u_int32_t sin6_scope_id; /* scope ID, 尚处于实验阶段 */
}
struct in6_addr {
unsigned char sa_addr[16]; /* IPv6地址,要用网络字节序表示 */
}

所有专用socket地址(以及sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket类型sockaddr(强制类型转换),因为socket编程接口使用的地址参数的类型都是sockaddr。

socket api 调用流程

服务器端:
创建 socket: 指定网络层协议(ipv4 or ipv6)和传输层协议(tcp or udp)->

绑定 socket: 与具体的服务端地址绑定(ip and port) ->

监听 socket: 创建一个监听队列存放待处理的客户端连接 ->

接收连接: 从监听队列中取出就绪的连接

客户端:
创建 socket ->

发起连接: 向指定地址的服务端发起连接请求

参考: https://swordandtea.github.io/2020/11/06/hight_performance_linux_coding/Linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80API/

IO 多路复用

一个socket 就是一个文件,每个socket 都对应一个 socket fd,IO多路复用是指将多个 socket fd 同时进行监听,从而获取多个事件状态。具体来讲,我们有三个IO多路复用的系统调用可以使用: #### select 将所有已连接的socket fd 放在一个文件描述符集合中,然后调用select 将文件描述符集合拷贝到内核中,内核中对集合进行轮训以检查是否有事件发生,当有事件产生后则将对应的文件描述符标记为可读或可写,然后再将集合从内核拷贝到用户态,然后用户再遍历集合以查找有有事件产生的文件描述符

所以通过select 实现IO多路复用需要对文件描述符集合进行两次拷贝和两次遍历。且select 使用固定长度BitMap 表示文件描述符集合,因此其所支持的复用路数是有限制的,默认最大值是1024

poll

poll 和 select 的整体流程一样,只是在文件描述符集合的实现上由 BitMap 修改为了链表,因此其所支持的文件描述符个数不再受限制


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!