基本概念
字节序
- 主机字节序
-
不同类型的机器处理数据时,会按特定的字节排列顺序读取存储器
-
不是由操作系统来决定的,是由硬件平台来决定的
-
Big-Endian,数据高位地址存放在低位地址空间
以0x0002为例
-
Little-Endian,数据高位地址存放在高位地址空间
-
- 网络字节序
- 是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关
- 网络字节顺序采用Big-Endian排序方式,总是从低位地址开始传输
- 发送数据包时,程序将主机字节序转换为网络字节序
ILP/LP/LLP约定
I:integer,L:long integer,P:pointer
linux/unix:LP64 ; windows:LLP64
数据结构对齐
-
结构体对其规则
- 结构体变量的⾸地址能够被其最宽基本类型成员的⼤⼩所整除;
- 结构体每个成员相对结构体⾸地址的偏移量(offset)都是成员⼤⼩的整
数倍,如有需要编译器会在成员之间加上填充字节; - 结构体的总⼤⼩为结构体最宽基本类型成员⼤⼩的整数倍,如有需要编译器会在最末⼀个成员之后加上填充字节
-
结构体内存对齐规则(存在结构体嵌套)
- 如果结构体中嵌套结构体,那么嵌套结构体相对结构体首地址的的偏移量必须是嵌套结构体最大基本类型成员大小的整数倍,嵌套结构体的规则满足一所述;
- 结构体总大小为**结构体最大基本类型成员(包括嵌套结构体中的最大基本类型成员)**大小的整数倍;
-
对齐系数
- 通过预编译命令
#pragma pack(n)
,n=1,2,4,8,16来改变这一系数,其中的n就是指定的“对齐系数” - 当n大于结构体最大基本类型成员的大小,n无效,按结构体最大基本类型成员的大小对齐
- 通过预编译命令
原因
- 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的。
- 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
Socket套接字与循环服务器
接口协议
特点:
- TCP/IP协议存在于OS中,网络服务通过OS提供
- TCP/IP要尽量避免让接口使用某一个厂商的OS中特有的特征(而其他厂商没有)
- TCP/IP和应用程序之间的接口应该不精确指明
- 不规定接口的细节
- 只建议需要的功能集
- 允许系统设计者选择有关API的具体实现细节
不精确指明的协议接口
- 优点:提供灵活性和容错能力
- 便于各种OS实现TCP/IP
- 接口可以是过程的,也可以是消息的
- 缺点:不同的OS中的接口细节不同
- 移植性差
- 程序员需要重新学习接口知识
功能
- 分配用于通信的本地资源
- 指定本地和远程通信端点
- (客户端)启动连接
- (客户端)发送数据报
- (服务器端)等待连接到来
- 发送或者接收数据
- 判断数据何时达
- 产生紧急数据
- 处理到来的紧急数据
- 从容终止连接
- 处理来自远程端点的连接终止
- 异常终止通信
- 处理错误条件或者连接异常终止
- 连接结束后释放本地资源
POSIX标准
Portable Operating System Interface of UNIX:可移植操作系统接口
源代码级别的软件可移植性:为一个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自另一个厂商)上编译执行
系统调用
操作系统内核提供一系列具备预定功能的多内核函数,通过称为系统调用(System Call)的接口呈现给用户
为安全考量,诸如I/O操作等特权指令被限制在内核态模式方可执行,系统调用使得应用程序从操作系统获得服务
套接字
协议操作接口,而非协议本身
- Socket是一个主机本地应用程序所创建的, 为操作系统所控制的接口, Client/server模式的通信接口
- 应用进程通过这个接口,使用传输层提供的服务, 跨网络发送(/接收)消息到(/从)其他应用进程。
类型
主被动
创建方式相同,使用方式不同
- 主动:等待传入连接的套接字,如服务器套接字
- 主动:发起连接的套接字,如客户端套接字
指明端到端地址:创建时不指定,使用时指明 - TCP/IP需要指明协议端口号和IP地址
- TCP/IP协议族:PF_INET
- TCP/IP地址族:AF_INET
通信服务类型
- SOCKET_DGRAM: 双向不可靠数据报,对应UDP
- SOCKET_STREAM:双向可靠数据流,对应TCP
- SOCKET_RAW:低于传输层的低级协议或物理网络提供的套接字类型,可以访问内部网络接口
l拓展: inux文件组织结构
地址结构
- IPv4地址结构:
sockaddr_in
&in_addr
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
typedef unsigned short sa_family_t;
struct in_addr{
in_addr_t s_addr;
};
struct in_addr {
in_addr_t s_addr; /* 32-bit IPv4 addre33 */
/* network byte ordered */
};
struct sockaddr_in {
uint8_t sin_ len; /* length of structure (16) */
sa_family_t sin_family;/* AF_ INET */
in_port_t sin_port;/* 16-bit TCP or UDP port number */
/* network byte ordered*/
struct in_addr sin_addr; /* 32-bit IPv4 addre33 */
/* network byte ordered */
char sin_zero[8]; /* unused */
};
- IPv6:
sockaddr_in6
&in6_addr
struct in6_addr {
uint8_t s6_addr[16] ;/* 128-bit IPv6 addres3 */
/* network byte ordered */
};
#define SIN6 LEN /* required for compile-time tests */
struct sockaddr_in6 {
uint8 t sin6_len;/* length of this struct (28) */
sa_family_t sin6_family;/* AF INET6 */
in_port_t sin6_port;/* transport layer port# */
/* network byte ordered */
uint32_t sin6_flowinfo; /* flow information, undefined */
struct in6_addr sin6_addr;/* IPv6 addre3s */
/* network byte ordered */
uint32_t sin6_scope_id; /*set of interfaces for a scope */
};
地址结构转换函数
inet_aton
: ASCII to 32位网络字节序二进制
inet_addr
: 返回32位二进制的网络
inet_ntoa
: 返回指向点分十进制数串的指针
以上三个函数仅针对IPv4
inet_pton
、inet_ntop
与上述作用类似,但是可以检错无效的点分十进制
步骤
服务器端
- 创建套接字
- 绑定套接字
- 设置套接字为监听模式,进入被动接受连接状态
- 接受请求,建立连接(accept会产生新的socket处理业务)
- 读写数据
- 终止连接
客户端
- 创建套接字
- 与远程服务器建立连接
- 读写数据
- 终止连接
TCP循环服务器
UDP循环服务器