UNIX网络编程:1,传输层简介

1.0tcp/udp简介

1.1 tcp

tcp是一个面向连接的协议,为用户进程提供可靠的全双工字节流,tcp套接字是一种流套接字(stream socket),tcp关心确认,超时,重传之类的细节,tcp可以用到ipv4也可以用到ipv6

tcp不同于udp的是,

  • tcp首先为服务器和客户之间提供连接,然后再跨连接交换数据,最后终止连接,tcp也提供可靠性,可靠性体现在,当一段向另一端发送数据时,他要求对端返回一个确认,如果没有收到,tcp将自动重传数据并且等待更长时间,在数次重传失败后,tcp才放弃
    tcp还含有用于动态估算客户和服务器之间的往返时间(rtt)的算法,以便它知道等待一个确认需要多少时间。
  • tcp还通过其中每个字节关联一个序列号对所发送的数据进行排序,假设我们有一个应用写2048字节到tcp套接字,导致tcp套接字发送2个分节,第一个分节序列号1~ 1024,第二个分节序列号为2 ~2048 (分节时tcp往下传输给ip的数据单元),如果这些分节按照非顺序到达,接收端的tcp将会为他们重新排序,再把结果数据单元传给应用,如果对端tcp套接字收到重复数据,它可以根据序列号判定数据是重复的,从而丢弃重复数据,

udp不提供可靠性,udp本身不提供确认,序列号,rtt估算,超时和重传等机制,如果一个udp数据报在网络中被复制(有可能堵塞,等其他原因让客户端重传),2个副本都有可能到达接收端的主机,如果udp发送2个被分割的数据报到对端,有可能被排成网络序列,顺序颠倒发送到对端,所以我们使用udp一定要应对这个情况

  • tcp还提供流量控制的功能,这个流量控制的意思就是tcp接收端总是能告知tcp发送端我本地的接收缓存大小,也就是自己还能接收多少字节的数据,这个叫做通告窗口,这样,tcp发送端根据通告窗口发送数据就不会让接收端缓冲区溢出,当缓冲区接收发送端发送的数据后,通告窗口就会变小(因为缓冲区被填上数据),当应用从缓冲区提取数据到应用后,通告窗口变大(缓冲区数据被提取),如果tcp某个套接字的通告窗口满了,发送端只能等待,应用读取数据,使缓冲区空闲后再发送数据进来
  • tcp还是全双工的,代表给定的连接上可以同时发送和传输数据(udp也是可以的)

1.2 udp

udp是一个无连接协议,udp套接字是一种数据报套接字,udp数据报不能保证最终到达他们的目的地,于tcp一样,udp即可使用ipv4也可使用ipv6.

首先我们一个应用程序往UDP套接字写入一个消息,然后这个消息被封装到一个UDP的报文头部(里面有源端口目标端口,用户包长度,checksum),然后这个包再往下被封装ip头部(源ip,目标ip),再往下被封装以太网帧头部(源mac 目标mac),再被传输到对端的主机
UDP不保证UDP报文传输会到达最终目的地,不保证各个数据报的先后顺序跨网络后保持不变,也不保证每个数据只到达一次
如果一个UDP数据包到达了对端主机,但是校验发生错误,或者传输过程中丢了,他就无法传递给对端udp套接字,也不会重传,
我们也说udp是无连接的,因为UDP客户端与服务器之间不必存在任何长期关系,换句话说,一个UDP客户端可以创建一个套接字并且发送一个数据报给一个给定的服务器,然后立即用同一个套接字发送另一个数据报给另一个服务器,同样,一个UDP服务器可以用 同一个UDP套接字从若干个不同UDP客户端接收数据报,每个客户端一个数据报

2.0 TCP三次握手

在这里插入图片描述
1.我们的服务器通过socket创建套接字后,再用bind绑定端口,再用listen监听套接字(这个不是三次握手里面的,他只是准备程序,使客户端连接我们服务端收得到)
2,客户端首先通过connect函数发起主动连接,通常客户端会发送一个SYN(同步)分节,他会告诉服务端他自己的初始序列号,通常syn不懈怠数据,ip报文也就包含一个ip首部和tcp首部,有可能包含tcp option(注意connect的时候是被阻塞的,直到对方服务端accept函数抽取连接后返回确认值到本套接字才会接触堵塞)
3,这一步的意义是服务器确认客户端发送的ACK(使用accept()函数接收客户端发送的连接,接收完成后还会发送一个syn向客户端说自己已经接收好了,在客户端回复收到之前accept都是堵塞的),服务端怎么确认了?首先把客户端发送的SYN+1后的数当成ACK发送回客户端,并且携带一个自己定义的SYN初始序列号,发送给客户端
4,客户端收到服务端发送的ACK核对是否是自己之前发的加一,并且接收客户端自己的SYN,然后说明服务端套接字已经收到自己的连接,然后解除connect的堵塞状态,然后将服务端发送的SYN+1当成ACK返回给服务端,说明自己已经收到,可以进行数据通信
5,最后服务端调用read函数读取数据,读取过程堵塞。

2.1 tcp选项

首先我们每一个syn都有多个tcp选项

2.2 TCP四次挥手

在这里插入图片描述
这里的过程也非常的简单
1,首先客户端调用close()函数主动的关闭连接,调用的时候会发送一个FIN给服务端,表示客户端的数据发送完毕,我要断开链接了
2,服务端收到这个FIN后知道客户端没有数据要发送勒,他就发送一个EOF到发送到应用程序的队列中,等到数据传送完成后再传输EOF给应用程序表示再无任何数据可以接收,并且发送一个ack给服务端,代表自己已经接收这个ack的值就是接收FIN的值+1
3,服务端的应用程序收到EOF后同样调用close()关闭自己的套接字,关闭后也会向客户端发送一个FIN
4,客户端收到FIN后也会发送一个ACK给服务端,来确认自己已经收到,ACK的值也是FIN+1

此处有一个TCP的状态转换图如下
在这里插入图片描述
上半部分是TCP三路握手过程的状态变迁,下半部分是TCP四次挥手过程的状态变迁。

1.CLOSED:起始点,在超时或者连接关闭时候进入此状态,这并不是一个真正的状态,而是这个状态图的假想起点和终点。

2.LISTEN:服务器端等待连接的状态。服务器经过 socket,bind,listen 函数之后进入此状态,开始监听客户端发过来的连接请求。此称为应用程序被动打开(等到客户端连接请求)。

3.SYN_SENT:第一次握手发生阶段,客户端发起连接。客户端调用 connect,发送 SYN 给服务器端,然后进入 SYN_SENT 状态,等待服务器端确认(三次握手中的第二个报文)。如果服务器端不能连接,则直接进入CLOSED状态。

4.SYN_RCVD:第二次握手发生阶段,跟 3 对应,这里是服务器端接收到了客户端的 SYN,此时服务器由 LISTEN 进入 SYN_RCVD状态,同时服务器端回应一个 ACK,然后再发送一个 SYN 即 SYN+ACK 给客户端。状态图中还描绘了这样一种情况,当客户端在发送 SYN 的同时也收到服务器端的 SYN请求,即两个同时发起连接请求,那么客户端就会从 SYN_SENT 转换到 SYN_REVD 状态。

5.ESTABLISHED:第三次握手发生阶段,客户端接收到服务器端的 ACK 包(ACK,SYN)之后,也会发送一个 ACK 确认包,客户端进入 ESTABLISHED 状态,表明客户端这边已经准备好,但TCP 需要两端都准备好才可以进行数据传输。服务器端收到客户端的 ACK 之后会从 SYN_RCVD 状态转移到 ESTABLISHED 状态,表明服务器端也准备好进行数据传输了。这样客户端和服务器端都是 ESTABLISHED 状态,就可以进行后面的数据传输了。所以 ESTABLISHED 也可以说是一个数据传送状态。

上面就是 TCP 三次握手过程的状态变迁。结合第一张三次握手过程图,从报文的角度看状态变迁:SYN_SENT 状态表示已经客户端已经发送了 SYN 报文,SYN_RCVD 状态表示服务器端已经接收到了 SYN 报文。


下面看看TCP四次挥手过程的状态变迁。结合第一张四次挥手过程图来理解。

1.FIN_WAIT_1:第一次挥手。主动关闭的一方(执行主动关闭的一方既可以是客户端,也可以是服务器端,这里以客户端执行主动关闭为例),终止连接时,发送 FIN 给对方,然后等待对方返回 ACK 。调用 close() 第一次挥手就进入此状态。

2.CLOSE_WAIT:接收到FIN 之后,被动关闭的一方进入此状态。具体动作是接收到 FIN,同时发送 ACK。之所以叫 CLOSE_WAIT 可以理解为被动关闭的一方此时正在等待上层应用程序发出关闭连接指令。前面已经说过,TCP关闭是全双工过程,这里客户端执行了主动关闭,被动方服务器端接收到FIN 后也需要调用 close 关闭,这个 CLOSE_WAIT 就是处于这个状态,等待发送 FIN,发送了FIN 则进入 LAST_ACK 状态。

3.FIN_WAIT_2:主动端(这里是客户端)先执行主动关闭发送FIN,然后接收到被动方返回的 ACK 后进入此状态。

4.LAST_ACK:被动方(服务器端)发起关闭请求,由状态2 进入此状态,具体动作是发送 FIN给对方,同时在接收到ACK 时进入CLOSED状态。

5.CLOSING:两边同时发起关闭请求时(即主动方发送FIN,等待被动方返回ACK,同时被动方也发送了FIN,主动方接收到了FIN之后,发送ACK给被动方),主动方会由FIN_WAIT_1 进入此状态,等待被动方返回ACK。

6.TIME_WAIT:从状态变迁图会看到,四次挥手操作最后都会经过这样一个状态然后进入CLOSED状态。共有三个状态会进入该状态

  • 由CLOSING进入:同时发起关闭情况下,当主动端接收到ACK后,进入此状态,实际上这里的同时是这样的情况:客户端发起关闭请求,发送FIN之后等待服务器端回应ACK,但此时服务器端同时也发起关闭请求,也发送了FIN,并且被客户端先于ACK接收到。

  • 由FIN_WAIT_1进入:发起关闭后,发送了FIN,等待ACK的时候,正好被动方(服务器端)也发起关闭请求,发送了FIN,这时客户端接收到了先前ACK,也收到了对方的FIN,然后发送ACK(对对方FIN的回应),与CLOSING进入的状态不同的是接收到FIN和ACK的先后顺序。

  • 由FIN_WAIT_2进入:这是不同时的情况,主动方在完成自身发起的主动关闭请求后,接收到了对方发送过来的FIN,然后回应 ACK。

tcp状态图借鉴于https://blog.csdn.net/yeswenqian/article/details/40110703

2.3 TIME_WAIT状态

为什么要有TIME_WAIT?我们从2个方面说
1,我们的四次挥手后主动发起关闭的一方会进入TIME_WAIT状态,去等待2个MSL,因为主动关闭方发送的最后一个ACK没有传递到服务端(也许因为中间的路由器down了,链路断了),服务端将不断地重新发送他最后一个fin给主动关闭端,如果没有TIME_WAIT这个状态来保持接收的状态,那么将收不到服务端重复发送的FIN
2,还有一个情况就是服务端发送一个连接后立马,关闭了这个socket,又新开了一个socket,目标ip,端口号都一样,那么他老的FIN在网络中迷路了,没有快速发到主动关闭端(客户端),而服务端又发送了一个新的连接,此时TCP将不会给客户端处于TIME_WAIT状态的接口接收新的连接,如果经过2个MSL后老的FIN还没有到达客户端,那么客户端将取消TIME_WAIT状态,老的报文也会在链路中消失,因为传输最大的时间是2个MSL

3.0 端口号

如果一个进程想和服务相联系,他必须找到对端服务的端口,传输层中任何一种协议都有16bit unsigned int 的端口号,不管是tcp还是udp还是sctp,IANA维护着端口号分配列表清单如下

  • 0——1023

    此端口为众所周知的端口,都由IANA分配喝控制,可能的话相同端口号分配给TCP/UDP同一给定的服务,例如不管是udp80还是tcp80都被分配给了web服务器,尽管实现普遍都是用tcp,注意这些总所周知的端口分配必须要有超级用户权限

  • 1024——49151

    这个范围的端口是已登记的端口,这些端口不受IANA控制,不过由IANA登记,并提供他们的使用清单,可能的话这些相同的服务有可能分配给不同协议同一端口号

  • 49152——65535

    这个范围的端口是私有端口,IANA不会去管辖这些端口,他就是我们的临时端口比如我们的web服务器的客户端(浏览器)的端口就是在这个范围,49152正好又是65536的四分之三

3.1 套接字对

一个tcp的套接字对是定义该连接的2个端点的四元组:本地ip地址,本地TCP端口号,目的ip地址,目的端口号,通常表示每个端点的2个值,ip地址和端口号被称为套接字

4.0 tcp端口号于并发连接服务器

当我们的客户端发送一个连接给我们的服务端,服务端的服务主进程会fork一个自身的副本(子进程),让子进程来处理这个请求 ,如下图
在这里插入图片描述
然后我们的客户端还有另一个应用需要连接服务器的21端口,此时客户的主机会为自己分配另一个临时端口比如1501,,然后发送到了服务端,服务端又会fork一个子程序去处理此请求,2个进程处理的已连接套接字都不一样
在这里插入图片描述

notes:tcp无法通过查看目的端口来分离外来的分节到不同端点(那个请求发给那个子进程处理),他是通过比对套接字所有4元素才能确定那个端点接收某个到达的分节,就如上图所示,源端口为1500的被分配到子进程1,源端口为2的被分配到子进程2,其他目的端口为21的tcp分节会被发往服务器(主进程,由主进程来fork一个子进程接收)

4.1 缓冲区大小的限制

首先我们ipv4数据报最大的是65535字节(包括ipv4首部),因为标识ipv4长度的字段有16位
ipv6的报文长度为65575字节,因为标识ipv6的报文长度有16位最大表示65535个字节,但是他这个65535不包含40个字节的ipv6头部(ipv4包括),所以一共是65575个字节,我们可以使用Jumbo Payload选项给ipv6报文扩展到32位(不包括ipv6头部),这个需要我们在链路中设置mtu。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值