UNIX网络编程卷1:套接字联网API(第3版)

PDF 百度可找,也可联系我

TCP/IP

概述

1
2
3
4
5
6
7
要通信,需知协议;

Client发请求,Server响应;

复杂通信支持异步回调;

Server:Client => 1:n;

在这里插入图片描述

1
2
3
4
5
TCP/IP 使用 TCP通信,TCP使用IP通信;

协议栈一端向下=封装,一端向上=解析;

Client、Server 用户进程,TCP协议、IP协议是内核;

在这里插入图片描述

1
2
3
4
5
6
7
8
9
IP -> IPv4,IPv6(升级版v4);

局域网LAN,广域网WAN;

路由器是广域网的架构设备;(我之前记得听说,路由器 WAN口速度慢,LAN口速度快,)

*最大广域网:因特网 Internet;

私人广域网可接入因特网,也可以不接入因特网;

在这里插入图片描述

时间获取程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 时间程序(客户端创建):
TCP创建Socket,AF_INET TCP/IP协议字节流,
连接Server,
for读取,
关闭;

TCP无边界字节流协议;

read 一次读不完,要在while中循环读,0表示对端关闭,负值表示发生错误;

exit unix终止进程运行,关闭该进程所有fd,TCP被关闭;

# 时间程序(服务器创建):
Socket创建,
Bind绑定端口、INADDR_ANY接受任意地址连接,
Listen监听(由内核接受Client连接),
accept Server进程睡眠,等待Client连接由内核接受,TCP三次握手建立连接,握手完毕accept返回已连接的新的fd,
Close 发送 FIN终止连接;

处理多Client,每个Client fork创建一个子进程,或线程代替fork;
Server 守护进程运行;

在这里插入图片描述

包裹函数、errno

1
2
3
4
5
包裹函数:首字母大写(带有check的函数);

unix 函数有错误:errno 全局变量赋值正值错误码,函数返回-1;
E开头全大写,表示错误码;
0不表示任何错误;

在这里插入图片描述

OSI模型

1
2
3
4
国际标准化组织ISO的计算机通信开放系统互联OSI模型,是七层模型;TCP、UDP留有间隙,可绕过传输层,也可以直接访问数据链路层的帧;

上三层:应用层细节,用户进程;
下四层:通信细节,内核;

在这里插入图片描述

传输层:TCP、UDP 和 SCTP

概述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 概述
udp简单、不可靠、数据报协议;
tcp复杂、可靠、字节流协议;
sctp 可靠传输协议,还有增强功能,还可绕过传输层;

IPv4 32位(Internet Protocol Version 4),
IPv6 128位(Internet Protocol Version 6),
TCP(传输控制协议 Transmission Control Protocol),
UDP(用户数据报协议 User Datagram Protocol),
SCTP(流控制传输协议 Stream Control Transmission Protocol),
(ICMP,IGMP,ARP,RARP,ICMPv6,BPF,DLPI)

# UDP
UDP简单传输协议,UDP Socket 写入一个消息,消息封装为UDP数据报,再被封装为 IP数据报发送目的地;
不可靠性;
有长度;
无连接,不是长连接;
Server 发完一个断开,发另外一个Client;
也可以 Server Client 1:n;

# TCP
TCP建立连接,传输,断开连接;
可靠性:对端确认,不确认次数重传,超时机制;
RTT往返时间;
字节关联序列号保证顺序,对端接收再排序,接收再去重;UDP可能会乱序,重复,因为没有序列号;
TCP流量控制,滑动窗口,写变大,读变小,已满要等待读取才能发;

# SCTP
UDP无流量控制,一端发的快,一端接收慢是可以的;
TCP全双工,同时发送和接收,也可以转为单工连接;
UDP是可以全双工的;
SCTP双端关联,一个连接两个IP通信;
可靠性,排序,流控,全双工;面向消息的,消息有序,消息有长度;
双端可有多个流,每个流独立有序传输,一个流鸡掰不影响其他流;
多宿特性,单端多IP,一个网络坏了,切换其他网络;

TCP连接建立与终止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 三握手
三次握手:①客户端SYN J ②服务端SYN K,ACK J+1 ③客户端ACK K+1 (你可能听见,能/你可能听见,能);
电话系统:
socket电话可用,
bind告诉别人我的号码,
listen打开手机铃声。
connect打电话,
accept别人打我电话时,也就是建立完连接,返回标识(ip,port,号码);
=> 可能会走 DNS电话簿;

MSS 分节大小/窗口大小(长胖管道选项);
高带宽,长延迟网络称为长胖管道;
TCP建连接3分节,断连接4分节;

# 四挥手
①客户端FIN M ②服务端ACK M+1 ③服务端FIN N ④客户端ACK N+1(挂了昂,好,我也挂了,好);
2到3之前可能存在数据传递,称为半关闭;
发送 FIN 是因为调用了 close函数;
当 Unix 进程关闭时,所有描述符都关闭,即会调用TCP连接 close方法发出 FIN;

# 11种状态
11种状态(还是看的太慢了,细看了,)

确认与应答一起发叫捎带,应答慢就会先确认,再应答;
TCP细节转移至UDP应用进程中做,UDP少数据,避免TCP创建连接与关闭连接开销;

在这里插入图片描述

TIME_WAIT状态

1
2
3
4
5
6
主动关闭端会有TIME_WAIT,成为 2MSL(最大2分节生命期);
MSL 30s-2min,TIME_WAIT 1min-4min;
重复分组问题;
TIME_WAIT存在的两个理由:
1、可靠实现TCP全双工连接的终止
2、允许老的重复分节在网络中消逝(这一页我好晕啊,为了以后来理解,放出下面这张图)

在这里插入图片描述

SCTP关联的建立和终止

1
2
3
4
5
6
7
8
9
10
11
12
# 四握手
SCTP四握手:①INIT ②INIT ACK Cookie ③Cookie Echo ④Cookie ACK;
四握手可避免DDoS;
TCP 32位初始序号,SCTP 不定长字段基于加密防攻击;

# 三挥手
SCTP三挥手:①SHUTDOWN ②SHUTDOWN ACK ③SHUTDOWN COMPLETE;
无需TIME_WAIT,因为后续每次请求都带INIT和INIT ACK标记;

(不用去记这些状态,先跳过)

SCTP 块,多个块可组合;

在这里插入图片描述

端口号、套接字对

1
2
3
4
5
6
7
8
9
10
11
# 端口号
①0-1023 IANA控制
②1024-49151
③49151-65535 临时端口

# 套接字对
TCP四元组:src_ip,scr_port,dest_ip,dest_port;
SCTP四元组:src_ip[],scr_port,dest_ip[],dest_port;
UDP无连接;
套接字就是描述端点两个值(ip+port);
bind函数需要ip+port;

TCP端口号、并发服务器

1
2
# 并发服务器
Server accept 每个Client连接都fork一个子进程,子进程拥有 client_ip:random_port,server_ip:port;

在这里插入图片描述

MTU/MSS/缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# MTU
MTU => 最大传输单元(maximum transmission unit);
以太网 MTU 1500字节;
A->B,B->A 可以不同;IP数据报超出MTU要分片;
IPv4 本身分片,IPv4 路由分片;
IPv6 本身分片,IPv6 路由不分片;
IPv4 首部有分片字段,IPv6无;
DF位 IPv4设置后超出MTU会报错,IPv6隐含DF位,超出MTU会报错;

# MSS
MSS => 最大分节大小(maximum segment size)
TCP 有 MSS,用于告诉对端每个分节能发送最大的 TCP 数据量;

# TCP 缓冲区
TCP 每个套接字都有一个发送缓冲区,应用程序调用write,内核会把所有数据从应用缓冲区复制到套接字发送缓冲区;
发送缓冲区容不下应用进程缓冲区的所有数据(或应用进程缓冲区大于发送缓冲区,或发送缓冲区已有其他数据),该应用进程将睡眠,假设套接字是阻塞的(默认),内核不会从write返回,直到应用进程缓冲区的所有数据全部复制到套接字的发送缓冲区;
write返回只代表应用进程缓冲区可用,并不代表对端TCP已收到数据;
对端TCP确认收到数据,对端ACK送达后,本端TCP才从套接字发送缓冲区丢弃已确认的数据;
TCP必须为已发送的数据保留一份副本,直到对端确认为止;
TCP发MSS大小的块,分片;
分片传给数据链路输出队列,队列满了,会返回IP错误,再返回TCP错误,TCP会重传,应用进程无感知;

# UDP 缓冲区
UDP发送缓冲区就是数据报大小,发送超出这个大小会报错;
UDP不可靠,因此不保留副本,无需发送缓冲区;
UDP会发送数据报或分片片段放入数据链路输出队列;
UDP write返回是数据报或片段放入数据链路输出队列,队列满了会报错,依次返回给应用进程,不像TCP会重传,有的UDP实现则会忽略这个错误,应用进程看不到错误;

# SCTP 缓冲区
SCTP同TCP,是可靠协议,有套接字发送缓冲区,write 在上面情况也会阻塞,直到内核把应用进程缓冲区所有数据复制到套接字的发送缓冲区时返回,复制完成只是发送缓冲区可用,不代表对端已收到;
对端SACK确认,本端发送缓冲区删除副本;

# 总结
TCP复杂、可靠、面向连接的协议;
UDP简单、不可靠、无连接的协议;
SCTP组合两个协议的一些特性,并提供TCP所不具备的额外特性;
三个协议各有好处,不同场景有不同场景的替代理由;

在这里插入图片描述

TCP 函数

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 传递套接字地址结构
进程 -> 内核:bind,connect,sendto;
内核 -> 进程:accept,recvfrom,getsockname,getpeername;

b开头 byte,mem开头 memory;

# 值-结果参数
写套接字结构时,结构长度也传引用,称为值-结果参数;
sock_开头函数与协议无关,适用IPv4,IPv6;
TCP字节流,没有记录标记,read返回少;

# bind
bind绑定端口;
若未bind,客户端connect 或服务端listen自动绑定端口,服务端一般自己设置port,为了让别人连;
进程绑定本机ip到Socket,Client为了发送时当源IP,Server仅接收目的地为这个IP的连接;

# listen
listen在 socket,bind之后,accept之前调用;

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# accept
未完成队列(接收客户端syn,等待三次握手完成);
已完成队列(完成客户端三次握手,尾插);
accept从已完成队列头取,若无则阻塞;

accept 第一个参数监听套接字,返回值为已连接套接字;

# fork
fork创建新进程,父进程返回子进程id,子进程返回0,因此根据返回值知道当前是子进程还是父进程;
子进程只有唯一父进程,父进程fork调用之前的资源可以给子进程共享;
例如:父进程执行关闭Socket,子进程进行读写Socket;exec是子进程自身替换成新的程序;

listenfd,connfd 在fork前引用数各为1,fork后引用各为2,引用为0才彻底关闭;

# close
close是把 sockfd状态改为已关闭;
引用数为0时调用close 才会发送 FIN 关闭 Socket,如果想立即关闭可使用 shutdown;
如果父进程不调用 Close,子进程即使Close,连接依然存在,因为引用数为1,父进程最终耗尽可以描述符,因为一个进程可用描述符是有限的;

# 并发服务器
for循环处理

在这里插入图片描述

TCP回射服务器

在这里插入图片描述
在这里插入图片描述

1
2
3
4
5
6
7
ps 命令返回的 STAT 列:
S 进程等待资源而睡眠;
Linux进程阻塞accept或connect输出wait_for_connect
阻塞Socket IO时输出tcp_data_wait
阻塞终端 IO时输出read_chan;
Z 僵尸进程;
子进程结束,给父进程发信号,父进程不处理,子进程就是僵尸进程。

信号

1
2
3
4
信号是软件中断,可以有内核发给进程,或进程发给另一个进程(或自身);信号是int值,处理函数没有返回值;有两个信号不可被捕获SIGKILL,SIGSTOP;

信号设置只能做一次,在fork第一个子进程前做;
wait 等待子进程终止并返回,waitpid 指定进程;