协议栈与套接字
协议栈是一种网络控制软件,它主要根据套接字中记录的通信信息来工作。
在Window上使用netstat
命令可以查看套接字的信息,使用-ano
可以命令选择项,a
表示显示所有套接字(包括正在通信和尚未开始通信等状态下的),n
表示显示IP地址和端口号,o
表示使用该套接字的程序PID。
在应用程序与服务器之间进行连接通信的过程,实质上进行了套接字的创建连接,通过套接字携带的目的端口、IP地址等进行匹配连接。创建套接字的过程非常简单,只要调用Socket库中的socket程序组件就可以了,实际上,创建完套接字后,该套接字传递到协议栈中后,协议栈会返回一个描述符,应用程序会将描述符存在内存中,以后委托协议栈使用哪个套接字来连接或收发数据,就可以直接出示描述符来避免多个套接字冲突。
那么连接过程究竟是如何的呢?
数据连接过程
协议栈中存在重要的TCP和IP模块,当套接字创建成功之后,客户端的协议栈生成一个SYN
为1的TCP包,并发送给服务器,这个TCP包的头部还包含了客户端向服务器发送数据时使用的初始序号,以及服务器向客户端发送数据时需要用到的窗口大小。
协议栈将收到应用程序发过来的数据包加工后(加上协议头部),并不会马上发送出去,而是会根据一个叫MTU
(Maximum Transmission Unit,最大传输单元)的参数进行判断,MTU的大小是协议栈协议头部和数据长度的总和,所以MTU-协议栈协议头部=`MSS
(Maximum Segment Size,最大分段大小),当协议栈收到的数据接近或超过MSS时,就应该发送出去了。如果应用程序发送包的频率过小,很可能导致协议栈一直处于等待状态,为了解决这一问题,协议栈内部会有一个计时器规定等待发送的时间。
IP模块会根据TCP传递的目的服务器IP地址、发送IP写入IP头部,而不会管IP是否正确。
实际上,一般计算机的网卡IP才是计算机的IP,每一个IP都可以将数据包传递到相应子网的以太网中,通过route print
可以查看本地路由表判断该由哪块网卡发送。
生成IP头部之后,接下来IP模块还要将MAC头部追加到IP头部之前,MAC头部是MAC头部是以太网使用的头部,MAC地址是路由器的以太网(cwq简单看成局域网)地址,如00-80-C8-2D-82-EA(也可以使用’.’代替’-‘)MAC头部包含了14个字节(接收方的MAC地址,发送方的MAC地址,以太类型,48+48+16bit),你可以通过ARP(Address Resolution Protocol,地址解析协议)查询目标服务器(网卡)的MAC地址,如果对方与自己在同一个子网中,就可以通过广播响应得到,为了减少空间消耗,每查询过的MAC地址生成的ARP包,都会被缓存起来,你可以通过arp -a
查询缓存条目。
接下来轮到网卡了,不过应该由网卡驱动程序来控制网卡(网卡的ROM中存在着世界上唯一的MAC地址,这是在生产的时候厂商就已经写入的),将IP数字信号转换为电信号(或光信号)发送出去,网卡中包含有MAC模块,网卡将发送包复制到缓冲区后,MAC模块会在缓冲区内将包取出,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的FCS(帧校验序列)
报头是一串像10101010...
这样1和0交替出现的比特序列,长度为56比特,它的作用是确定包的读取时机,主要是将1和0看成纵坐标画出的上下波动图代表电信号的高低电平的波动,如果有连续的1出现,那么此处就是读取时刻了。在这里有一个转换细节,就是连续的1出现和0出现,在电平波动上结果是一样的,那么就无法判断出这个读取时刻了,为了解决这一问题,引入了一个时钟信号,它是一段规律的电平波动,如在间隔时间,如
,然后通过和数据信号进行一一对应(作用完将信号传递给接收方,接收方根据信号提取出时钟信号,然后计算出数据信号),就可实现连续的1和0的判断,末尾的FCS校验序列是用来检查在包传输过程中因噪声导致的波形紊乱、数据错误,由32bit组成,通过一组公式从包头到尾部计算出来的,接收方可以通过相同的公式计算出此序列,比较开始的FCS序列是否出现不同来判断包数据是否出现错误,差异化会导致包被丢弃
接下来就是交由网线发送了,发送信号的操作分为联众,一种使用集线器的半双工模式,一种使用交换机的全双工模式。半双工模式会先检查网线中是否存在其他正在传输的信号,有则待其传输完毕。
然后,PHY(MAU)
模块会将信号转换为可在网线上传输的信号格式,并通过网线发送出去。
当这个包到达服务器后,服务器会发出一个包含ACK
号(表示确认已收到包)、初始序号和窗口大小的SYN
为1的包。
当这个包到达客户端后,客户端会返回一个包含确认收到包的ACK
号的TCP包。
其中,为了防止网络攻击,通常会以一个随机数计算出的一个初始序号来代表第一个包的编号,并且通信双方在数据传递之前就已经知道这个初始序号。而ACK
号是通过初始序号来计算得出的(表示为已经收到的第ACK字节之前的所有字节)。在得到对方确认之前,发送过的包都会保存在发送缓冲区中,以便TCP重传。
由于服务器的距离不是固定的,所以在返回ACK号所需时间也是不固定的。为了提高效率,TCP采用了动态调整等待时间(ACK号返回所需的时间)的方法。而为了能实现更高效地利用等待时间,客户端会在发送一个包之后,不等待ACK号的返回,而是直接发送后续的一系列包,我们称之为滑动窗口方式。
在服务器应用程序接收数据之前,会把接收到的包暂时先存储在自己的缓存区中,为了不过载,服务器会在返回ACK的时候顺带把自己缓冲区还能接收的数据空间大小返回。