传输层是应用进程之间的逻辑通信,为了能够把网络信息成功地送到目的地,就出现了端口的这一概念。我们在编程时运用的端口号应大于49151,因为前一部分多为系统使用,而端口只有16位,所以我们使用的范围是49152~65535.
UDP
UDP是无连接的传输协议,只能尽最大努力交付,不保证可靠传输,而且UDP是面向报文的,也就是说,应用层把报文传下来后,直接加上头部就交付给IP层。如果报文太小的话,IP的首部就会相对太大,,若报文太大,又有可能进行分片,大大影响IP层得效率!UDP是没有拥塞控制的,这就可以不管网络的情况来不断地发固定大小的包,这对于实时应用(IP电话、实时视频)有一定的好处,不过这可能造成网络的严重拥塞,甚至瘫痪!
上面总是说UDP的不好,现在说说好的。UDP支持一对一、一对多、多对多和多对一的交互通信,而且UDP的首部开销比较小,只有8个字节,这对于TCP那最小20个字节的首部已经是比较可观的。
UDP首部:源端口、目的端口、长度和校验和。每一个都占有两个字节,这里我想声明的是,长度也占有两个字节(16位),这意味着每一个UDP数据包最大可以容纳64M的数据(不管IP层是否会分片)。校验和是检测包是否有错的,它在处理时用到了12位的伪首部,当检测有错后就直接丢弃数据包。
TCP
-
TCP是面向连接的传输协议,在通信前必须先建立连接
-
TCP连接只能有两个端点,即是点对点,提供可靠交付服务。
-
提供全双工通信,双方连接后可以在任何时候发送信息。在这里要注意,我们在平时用的send()或WSAsend()函数发送时,函数只是把我们需要发送的缓存信息送到发送缓存而已,并没有替我们实现发送,实现发送的是协议本身!所以我们在编程的时候就把套接字分为阻塞模式与非阻塞模式两种来区分开来用户想成功发送或者接受后再返回还是一调用完函数后就返回而通过控制I/O来对接受或者发送进行下一步的处理。
-
TCP的面向字节流的,这就是说对于应用层传下来的信息,TCP把其作为无结构的字节流进行处理(这与UDP有很大的区别)。
TCP的可靠传输:
为了实现可靠传输,TCP在这里运用了两个协议,分别是停止等待协议(每收到一个分组就进行确认)和连续ARQ协议(滑动窗口)
停止等待协议:发送方每发出一个分组就会暂时保存以待重发;每一个分组都会进行编号;有一个超时计时器,发送一个分组后若确认包久久不到超出设定的时间就会超时重传。
TCP的首部格式
-
源端口和目的端口(各占2字节)
-
序号seq(4字节):指定在本报文中所发送数据的第一个字节序号
-
确认号ack(4字节):期望收到的下一个序号
-
数据偏移(4位):记录数据包的有用数据起始位置,最大偏移为60字节,即TCP报文的首部最大为60字节。
-
保留(6位)
-
紧急URG(1位):通常用于传输带外数据,1时有效
-
确认ACK(1位):仅当1时有效(确认号字段有效),建立连接之后就要把所有的报文该字段都要置1;
-
推迟PSH(1位):当双方在通信时一方急迫等待另一方作回应时就会在发送包的此字段置1,当接收方收到后就会不等待缓冲区填满就立即交付给上一层作处理。
-
复位RST(1位):当网络连接出现大问题之后就要设置该字段值为1来重新连接。
-
同步SYN(1位):在建立连接时使用,为1时表示该是一个连接请求(SYN=1,ACK=0)还是接受连接请求(SYN=1,ACK=0)
-
终止FIN(1位):用于释放连接
-
窗口(2字节):根据己方接收缓存的大小来设置窗口大小,与确认号合用,来告诉对方我下一个包想接收的是以确认号开始,窗口大小那么长的数据
-
检验和(2字节):与UDP类似
-
紧急指针(2字节):仅在URG=1时有效,用于指定紧急数据的字节数。
-
选项(最大40字节)
滑动窗口注意的几点
-
接收方只能对按序到达数据中的最高序号作确认,对不按序到达的数据暂时存放在接收缓存
-
发送缓存存放的是准备发送的数据和已发送但是未确认的数据
-
接收缓存存放的是按序接收到但未被应用程序读取的数据和未按序达到的数据
-
接收方必须有累积确认的功能
-
假如接收方的缓存中中间缺了几部分的话,可以用选项中的选择确认字段SACK,不过最多可以设置4个字节块的边界信息
TCP连接建立:
-
首先服务端创建控制块TCB(传输控制块)来等待客户端的连接请求,之后处于监听状态
-
客户端也创建一个TCB,之后向服务器发送请求报文。SYN=1,seq=x。规定在申请或答应连接请求时会把SYN置1,由于发送连接请求不能携带数据,但要消耗一个序号,所以随便给seq一个值。
-
客户端接收后,如果同意连接就会发送一个确认包。SYN=1,ACK=1,seq=y,ack=x+1。由于接受连接请求不能携带数据而要消耗一个序号,seq随便置一个值y;刚刚收到的是x,自然下一次想接受的序号是x+1,所以ack=x+1,要使ack有效,ACK=1;SYN由于接受连接请求的需要置1.
-
客户端收到后,也要发送一个确认包,这是防止倘若服务端的确认包延迟到达而重发,之后客户端在接受新的包后建立连接处理完毕断开后,倘若之前那个延迟的包达到,就会以为服务端又要连接,这就出现问题了,所以要客户端再发一个确认包。ACK=1,seq=x+1,ack=y+1。不能携带数据信息,但要消耗一个序号,为使确认号有效,ACK=1,刚刚收到的是y,这次应是y+1;刚刚服务端的确认号是x+1,所以这次要发送x+1开始的信息,序号是x+1。
-
至此连接成功。
TCP释放连接(双方都可以释放连接):
-
首先客户端发送一个请求释放连接的包。FIN=1,seq=u。规定在释放连接时,FIN字段要置1.
-
服务端收到后,会返回一个确认包。ACK=1.,seq=v,ack=u+1。之后服务端就会等待,确保没有数据发出,这个过程就是半关闭状态,此时服务端可以向客户端发送信息,但是客户端不可以发向服务端。
-
待服务端已经没有数据发向客户端后,应用进程就会通知TCP释放连接,发一个包。FIN=1,ACK=1,seq=w,ack=u+1
-
客户端收到后,再发一个确认包,ACK=1,seq=u+1,ack=w+1。之后会等待2MSL(约为4分钟),为什么了?因为如果服务端收不到刚刚的确认信息,就会一直等待客户端的确认信息,在一定时间后服务端会重发一个FIN+ACK包来让客户端再发一次确认。倘若此时客户端已经关闭了连接,那么服务端就永远都关闭不了连接。除了这个之外,TCP还有一个保活计时器,这是倘若建立连接后,如果客户端出现了故障,那么服务端就会白白地等待下去,所以要设置这个计时器,两个小时之内没有收到客户的数据就发送一个探测报文段,之后每隔75分钟发送一次,倘若连续10次没有应答,服务端就会自动断开连接。