unix网络编程卷1_套接字联网API
UNIX网络编程卷1:套接字联网API
2. 传输层:TCP、UDP、SCTP
TCP状态转换图,全书最重要的图之一
![image-20230707161910922](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230707161910922.png)
UNIX网络编程第二章
![image-20230707162837287](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230707162837287.png)
3. 套接字编程简介
以下是UNIX网络编程卷一上的图,查看Linux 这几个地址的定义,有些许差别,Linux的定义如下:
1 |
|
![image-20230710135021051](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230710135021051.png)
4. 基本TCP套接字编程
内核为每一个监听套接字维护两个队列:
![image-20230711105902889](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230711105902889.png)
![image-20230711105700774](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230711105700774.png)
![image-20230711110426805](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230711110426805.png)
相关知识: 半连接攻击
6. IO复用:select和poll函数
5种IO模型(这部分小林的图解系列图更好)
一个输入操作包括2个不同的阶段:
- 等待数据准备好
- 从内核向进程复制数据
![image-20230712101328832](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712101328832.png)
一个非阻塞描述符循环调用recvfrom时,我们称之为轮询。该操作大量消耗CPU时间。
![image-20230712101520449](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712101520449.png)
IO多路复用同样是阻塞式的,不过它阻塞在IO复用系统调用上,如select、poll以及epoll,而不是阻塞在真正的IO系统调用上。注意,数据从内核复制到用户空间的那段时间,应用也是阻塞的。
![image-20230712101818511](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712101818511.png)
信号驱动式IO模型暂未发现可用场景,就不放图了。
以上所有的IO模型,均为同步IO。
POSIX异步IO框架aio系列已经过时了,在Linux平台,可以使用从Linux内核5.1开始的io_uring异步框架。
注意,IO多路复用是在收到数据时触发读事件,通知上层应用,此时需要将数据复制到用户空间。而异步IO是收到数据,并且将数据从内核复制到了用户空间后,再通知应用处理数据。
![image-20230712102345606](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712102345606.png)
五种IO模型的比较:
![image-20230712103326030](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712103326030.png)
shutdown函数:
终止网络连接的一般方法是调用close函数,不过close有两个限制,如下:
- close将描述符的引用计数减1,仅在该计数变为0时才关闭套接字。
- close终止读和写两个方向的数据传输。
而shutdown函数,既可以不管引用计数就激发TCP的正常连接终止序列,也可以处理如下这种场景:
我们需要告知对端我们已经完成了数据发送,即使对端仍有数据要发送给我们。
理解为什么一定要四次挥手。
![image-20230712105346134](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230712105346134.png)
SHUT_RD
:关闭读。套接字接收缓冲区的现有数据将被丢弃。SHUT_WR
:关闭写,TCP称为半关闭。套接字发送缓冲区的现有数据将被发送。SHUT_RDWR
:读写关闭
陈硕的muduo网络库中,即使用了TCP的半关闭功能。
1 |
|
8. 基本UDP套接字编程
![image-20230713110933870](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230713110933870.png)
![image-20230713114245758](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230713114245758.png)
11. 名字与地址转换
![image-20230714094837417](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714094837417.png)
1 |
|
同时支持IPv4与IPv6的转换函数:
头文件:<netdb.h>
![image-20230714100425698](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714100425698.png)
![image-20230714100811868](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714100811868.png)
创建一个TCP套接字并连接到一个服务器:
![image-20230714101711314](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714101711314.png)
![image-20230714102858926](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714102858926.png)
12. IPv4与IPv6的互操作性
![image-20230714103525135](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714103525135.png)
拓展: 当数据流路径是 IPv6通道 –> IPv4通道 –>IPv6(通道)时,将采用隧道技术。
![image-20230714104353262](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714104353262.png)
![image-20230714104908196](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714104908196.png)
总结: IPv6 对 IPv6,IPv4 对 IPv4, IPv4 对 IPv6 采用映射。
![image-20230714105120073](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230714105120073.png)
关于黄色标注处:若客户选择AAAA记录从而发送IPv6数据,则不能工作。若选择A记录, 则A记录实际作为一个IPv4映射的IPv6地址返回给客户,使得客户可以发送IPv4数据。
14. 高级IO函数
![image-20230717114657535](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230717114657535.png)
这两个函数是最重要的函数之一,因为最通用。具体的使用,可以查看vector AP底层网络通信部分的代码。
![image-20230718101508940](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230718101508940.png)
![image-20230717130305636](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230717130305636.png)
15. unix域套接字
unix域提供两类套接字:字节流套接字(类似于TCP)和数据报套接字(类似于UDP)。
使用unix域套接字的三个理由:
- 使用socket API,但不经过TCP/IP协议栈,快
- 可以在两个进程之间传递描述符
- 可以传递凭证,是服务器验证客户身份的可靠手段
unix域中用于标识客户与服务器的协议地址是普通文件系统中的路径名(绝对路径)。
![image-20230718093249058](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230718093249058.png)
socketpair函数可以用于网络服务器中的唤醒工作,除了该方式外,还可以使用管道或者eventfd方案。
指定SOCK_STREAM
的socketpari称为流管道,是全双工通信方式。
注意:因为unix域套接字的地址是一个绝对路径,因此在bind地址之前,一定要确认路径文件是否存在,若存在,一定要先通过unlink
清除路径文件。
![image-20230718094548141](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230718094548141.png)
16. 非阻塞式IO
Pass
20. 广播
![image-20230719101442074](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230719101442074.png)
![image-20230719101234474](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230719101234474.png)
21. 多播
单播地址标识单个IP接口,广播地址标识某子网的所有IP接口,多播地址标识一组IP接口。
广播一般局限于局域网内使用,多播既可以用于局域网,也可以跨广域网使用。
单播使用MAC地址,广播的MAC地址为 FF-FF-FF-FF-FF-FF
,多播的MAC地址为:01-00-5e-0 + 低序23位
,这三者是不同的。
一个进程接收某个多播数据包的先决条件是:该进程加入相应的多播组并绑定相应的端口。
![image-20230720092320779](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720092320779.png)
![image-20230720094101668](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720094101668.png)
IPv4首部的TTL字段兼用作多播范围字段。
- 0:接口局部,不能由接口输出
- 1:链路局部,不可由路由器转发
- 其他不再赘述,看图
![image-20230720094607440](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720094607440.png)
关于不完备过滤与完备过滤:目前多播地址是224.0.0.1
,若有一个多播地址225.0.1.1
发送数据。由于多播地址组ID的高5位在到以太网地址的映射中被忽略,该主机的接口也将接收目的以太网地址为01:00:5e:00:01:01
的帧。因此在数据链路层无法过滤掉,所以说数据链路层是不完备过滤。这种情况下,该帧承载的分组将在IP层完备过滤,IP层会比较该地址与本机接收应用进程已经加入的所有多播地址,根据结果是接收还是丢弃。因此该分组将被丢弃。
![image-20230720100557840](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720100557840.png)
多播套接字选项:采用getsockopt
或setsockopt
,相关选项如下:
![image-20230720105313825](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720105313825.png)
![image-20230720105446586](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720105446586.png)
如果发送UDP多播之前,没有指定影响发送的多播套接字选项,那么UDP的外出接口将有内核选择,TTL或跳限将为1,并有一个副本自环回来。
附录A
IP层提供无连接不可靠的数据报递送服务。
IP层最重要的功能之一是路由。
IPv4首部最大长度为60字节,固定部分20字节,选项部分,最长40字节。
协议字段中,1代表ICMPv4、2代表IGMPv4、6代表TCP、17代表UDP。
![image-20230720112341494](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720112341494.png)
IPv6首部最大长度为40字节,没有选项。
下一个首部:6代表TCP、17代表UDP、58代表ICMPv6等
跳限类似于IPv4中的TTL
![image-20230720113140264](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720113140264.png)
![image-20230720113922109](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230720113922109.png)
![image-20230721165536460](/2023/07/21/unix%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E5%8D%B71-%E5%A5%97%E6%8E%A5%E5%AD%97%E8%81%94%E7%BD%91API/image-20230721165536460.png)