POSIX API与网络协议栈

本文介绍linux中与tcp网络通信相关的POSIX API,在每次调用的时候,网络协议栈会进行的操作与记录。

POSIX API

Posix API,提供了统一的接口,使程序能得以在不同的系统上运行。简单来说不同的操作系统进行同一个活动,比如打开文件,所调用的函数是不同的。系统1调用的是open1函数,系统2调用的是open2函数,在Posix API中统一规定把这个功能的函数包装成open,那么当应用程序在不同的系统上调用open函数的时候就都能打开文件了。

应用程序可能会有自己的打开文件函数,比如popen,fopen等等,都是在直接或间接的调用open。

网络协议栈

网络协议栈是内核的一部分,是一系列网络协议的集合。负责网络通信和网络数据传输。

tcp网络通信相关的POSIX API

socket

socket通俗的翻译是插座,插座分为插头和座,socket函数实际上是实现了网络通信中建立连接所需要的“插头”和“座”。在网络通信中,“插头”对应着socketfd,“座”对应着tcp控制块。

socketfd存在于用户空间中,Tcp协议是网络协议栈的一部分,tcp控制块又是tcp协议的一部分。

Tcp控制块包含了以下内容:源IP地址和源端口、目标IP地址和目标端口、连接状态、序列号和确认号、窗口大小、拥塞控制参数、超时和重传信息、接收和发送缓冲区、计时器、选项和标志。

bind

给socetkfd的tcp控制块的目的IP地址和目的端口赋值。有些客户端不绑定一个特定的端口,那么系统就会随机分配。

listen与connect

Listen函数的第二个参数是在设置syn_queue与accept_queue的总长度。listen函数主要在做两件事:

a.修改tcp控制块的连接状态,发生连接前后分别是LISTEN状态和ESTABLISH状态。

b.建立syn_queue(同步队列)和accept_queue(接受队列)。syn_queue用于存储未完成的三次握手的连接请求控制块,accept_queue用于存储刚刚完成了3次握手的连接请求控制块,等待移交给accept函数进行处理。

三次握手介绍

客户端连接到服务器需要首先进行三次握手,这三次握手是在底层进行的。服务器在listen状态,客户端调用connect之后,就在进行三次握手了,整个过程对于用户是黑箱的。作为高水平程序员,我们还是需要来了解下底层到底是怎么样的运作机制。

首先了解tcp头,需要重点了解的是控制位(Flags),包括了8块内容:CWR、ECE、URG、ACK、PSH、RST、SYN、FIN,这些内容位要么填1,要么填0,用于控制和标识TCP连接的状态和行为。比如客户端发起了第一次握手,会往服务器发送一个tcp包,这个包会包含包头和没有数据的包体(包括后面的两次握手也是),这时候SYN被设置为1,说明是第一次握手。

三次握手的流程如图所示,以下的一根蓝线代表一次握手:

第一次握手,发送一个tcp包,这个包包头的SYN位置被设为1,seqnum被随机生成,比如是12345,由客户端发送给服务器。请求进入到了服务器的syn队列中,随即建立相应的请求控制块。

第二次握手,服务器返回一个包,这个包包头的SYN和ACK被设为1,客户端发送的12345的seqnum,收到12345的acknum。服务器也随机生成了一个seqnum,比如34531。

第三次握手,客户端发送了一个tcp包,于是服务器发送的是34531的seqnum,收到的34532的acknum。收到多大的acknum,表示seqnum小于这个acknum的tcp包都已经全部被对端接收过了。相应的请求控制块从syn队列移动到accept队列中。

通过seqnum和acknum的数值排序,可以实现tcp通信的不重复、不乱序、不缺失。

3个问题

  1. 连接的生命周期是什么时候开始的?从服务器第一次接收到tcp包开始。
  2. 第三次握手结束,accept队列是如何从syn队列中找到对应的请求控制块的?根据源ip和源port查找。
  3. Syn泛洪是什么?是一种网络攻击手段,简单来说就是黑客不断地通过第一次握手并且不回应第二次握手,把syn队列塞满,导致服务器无法建立新的连接。目前已经通过堡垒机、防火墙等手段拦截了该手段。

accept

Accept函数做了两件事:

a.     根据取出的连接请求控制块,建立新的fd,也就是通常新的clientfd。

b.     为这个新的fd建立新的tcp控制块。

send与write

tcp通信的这两个函数其实只做一件事,就是把数据从应用层appliacation,拷贝进入内核kenerl的发送缓冲区中。至于内核什么时候把数据发送到对端,就不一定了,有可能是先凑满一定的字符数再一起发送,有可能是看到一定时间内都没有数据再拷贝进入就发送,不同的系统会有不同的操作。我们可以通过ifconfig命令查看mtu,也就是单个数据包中可以传输的最大字节数。

recv和read

tcp通信的这两个函数其实也只做了一件事,就是把数据从内核的接收缓冲区拷贝到应用层。

close

该函数是负责断开客户端与服务器之间的连接。底层的流程通常称为“四次挥手”。不同于发起连接只能由客户端发起,断开连接可以由任意一方启动,只分为主动方和被动方。

Close函数做了两部分工作:a.回收fd。b.发送一个控制位FIN为1的包(第一次挥手),也叫fin包或final包,这个包的包体没有任何内容,于是对端的recv会返回0。

当对端接收到FIN包之后,通常会返回一个ack包,再调用close函数返回一个fin包(第二、三次挥手)。本端收到fin包之后再返回一个ack包(第四次挥手)。

下图是close主动方与被动方的挥手关系与状态变化:

红圈对应着主动方与被动方的状态变化。

  • 46
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值