以下来自湖科大
计算机网络
公开课笔记及个人所搜集资料
目录
- 一、运输层基本概念
- 1. 概述
- 2. 端口号、复用与分用
- 为什么有进程PID还要使用端口号?
- 关于端口复用
- TCP/UDP的复用和分用
- 为什么上面说UDP最大报文长度是512字节?
- 二、TCP和UDP的对比
- 区别1— —连接与无连接
- 区别2— —字节流与面向报文
- 区别3— —可靠与不可靠
- 区别4— —报文首部
- 区别5— —有序与无序
互联网主机间通信的真正实体是位于通信两端主机中的进程
运输层就是用于解决不同主机间进程的通信。
如下图,不同端口对应不同应用进程,这个端口不是物理端口了,而是区分不同应用进程的标识符:
运输层向高层用户屏蔽了下面网络核心的细节,如网络拓扑、所采用的路由选择协议等,它使应用层看见的,就好像是在两个运输层实体之间,有一条端到端的逻辑通信信道。
运行在计算机上的进程是使用进程标识符PID来标志的。然而因特网上的计算机并不是使用统一的操作系统,不同操作系统例如WINDOWS、LINUX、MAC OS等,使用不同格式的进程标识符,为了使运行不同操作系统的计算机的应用进程之间,能够进行网络通信,就要必须使用统一的方法对TCP/IP体系的应用进程进行标识。TCP/IP体系的运输层使用端口号来区分应用层的不同应用进程
注意TCP/IP体系是包括UDP的,复习第一章
其实端口就相当于是网络和进程间的中间层,客户端无法知道服务端对应进程的PID。而且进程PID每次都会发生变化,所以干脆统一安排一个窗口,让大家忽略细节,需要某类服务时就找那个窗口就行。
这一部分不属于课堂笔记,而是知识点的扩充。
1,同一台主机上的TCP和UDP协议之间是可以用同一个端口的。因为这两个在内核中是两个完全独立的软件模块,网卡收到包后放在缓冲区里,内核协议栈解析数据包的时候首先拆开IP头,然后根据报文头部的传输层协议是TCP还是UDP,交给传输层协议去解析
图源:小林coding
2,如果两个 TCP 服务进程绑定的 IP 地址不同,而端口相同的话,也是可以绑定成功的;
但是如果两个TCP服务进程是IP地址相同,端口又相同,那是会报错的:Address already in use
(这里是没有提前开启端口复用的情况,即下面所说的)
3,Linux3.9以上提供了端口复用技术:
(这部分可以看B站黑马程序员的网络编程)其实就是调用下下方这个函数。
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
端口复用可以做到一个进程里多个套接字用一个端口号,也可以做到多个服务器用一个端口,其实端口和IP地址是在同一个结构体sockaddr,所以端口复用的同时也是IP地址复用。
因此,当某台服务器需要维护或者出现异常,它主动发起关闭,需要等待2MSL,即使我们立马重启,客户端也不能马上连接到这台服务器,因为TCP协议在四次挥手的主动一方规定了2MSL的这一时间,为了不影响客户端,我们可以开启另一台服务器,它允许一模一样的程序,但是里面提前绑定了和旧服务器一模一样的IP和端口号(就是调用上述函数)
但是如果普通情况下多个套接字绑定同一个端口号(无论这些套接字是一个进程开的还是不同进程开的),其实没有太大的意义:
端口复用允许在一个应用程序可以把 n 个套接字绑在一个端口上而不出错。同时,这 n
个套接字发送信息都正常,没有问题。但是,这些套接字并不是所有都能读取信息,只有最后一个套接字会正常接收数据
所以一般也是用于上面说过的备用服务器情况里。
这里的复用不是指上面那种端口复用,而是协议层面的:
发送端(复用):
运输层从多个套接字收集数据,交给网络层发送。
接收端(分用):
运输层将从网络层收到的数据,交付给正确的套接字。
发送方的某些应用进程所发送的不同应用报文,在运输层使用UDP协议进行封装,这称为UDP复用。而另一些应用进程做发送的不同应用报文,在运输层使用TCP协议进行封装,这称为TCP复用,运输层使用端口号来区分不同的应用进程。不管是使用运输层的UDP协议,封装成的UDP用户数据报,还是使用TCP协议封装成的TCP报文段,在网络层都需要使用IP协议封装成IP数据报,这称为IP复用
IP数据报首部中协议字段的值,用来表明IP数据报的数据载荷部分,封装的是何种协议数据单元,取值为6表示封装的是TCP报文段,取值为17表示封装的是UDP用户数据报接收方的网络层,收到IP数据报后,进行IP分用。
若IP数据报首部装协议字段的值为17,则把IP数据报的数据载荷部分所封装的UDP,用户数据报上交运输层的UDP;若协议字段的值为6,则把IP数据报的数据载荷部分所封装的TCP报文段,上交运输层的TCP。
在运输层,对UDP报文段进行UDP分用,对TCP报文段进行TCP分用,也就是根据端口号将它们交付给上层相应的应用进程
在TCP/IP协议中,用“源IP地址”,“目的IP地址”,“源端口号”,“目的端口号”,协议号(IP协议的协议号为4,TCP的协议号为6)这样的一个五元组来标识一个通信。有时候会说四元组,就是没有包括协议号,其实五元组更为你恰当,如果是说标识一个TCP连接,那用四元组还是恰当的。
不同应用层协议对应使用的运输层协议,及熟知端口号如下:
这节课还讲到主机输入网址到显示网页的过程(具体只能看课程了),DNS监听的端口就是53号。
但是有几个需要注意的问题:
客户端向DNS要这个网址的IP地址时,发的就是UDP报文(其实TCP或UDP都可以),报文里源端口号就是挑一个未被占用的==短暂端口号来表示DNS客户端进程(这个进程运行在客户端主机里,就是浏览器进程),目的端口就是53,即**DNS服务器进程所使用的熟知端口号==**
这个要说到MTU了,其实MTU在局域网才是1500字节
,在互联网环境里,路由器默认设置的MTU是576字节
,这使得此时的UDP报文被限制为512字节了,而TCP没有报文这个概念,他是字节流的,没有边界的,所以多大都一样。(局域网上的UDP就是被1500的MTU限制为1472)
因为UDP数据包是有边界的,所以其实也可以被分片呀,一千两千也可以,反正UDP报头会标识这个UDP报文多长,即使被IP层分片,也能被组装回去。但是,因为UDP是不可靠传输协议,为了减少丢包风险,我们尽量控制UDP报文不会被下层的IP协议切割,所以控制UDP报文为五百多字节以内。(容易想到,一个包分成多个包,丢了其中一个就不完整了,要完整就要保证多个包都能正常送达,这比保证一个包难很多)
得到DNS的响应报文后,里面有网站的IP地址, 然后发送http报文,这个也需要加上TCP首部。
下图可以看到,DNS查询请求是UDP,而http请求是TCP:
其实课堂上讲的不够丰富和深入,这里尽量扩展一下。
UDP:用户数据报协议(User Datagram Protocol)
TCP:传输控制协议TCP(Transmission Control Protoclo)
UDP支持单播,多播和广播
(即一对一,一对多,一对全的通信),是无连接
的;
TCP仅支持单播
(即一对一通信),是面向连接
的。
也就是说,在socket编程时,如果使用UDP协议,那么一个套接字可以和多个套接字进行通信,能接收来自多个套接字的信息,而TCP不是,TCP是一对一的,TCP是面向连接的就是这个意思。
UDP不是一对一而是一对多,所以是无连接的。
UDP是面向应用报文
的— —第一部分末尾讲过这个
发送方的应用进程,将应用层报文如http报文交付给运输层的UDP,UDP直接给应用层报文,添加一个UDP首部,使之成为UDP用户数据报,然后进行发送。(应用进程的每个输出操作都正好产生一个UDP数据报)
接受方的UDP收到该UDP用户数据报后,去掉UDP首部,将应用层报文交付给应用进程,也就是说UDP对应用进程交下来的报文,既不合并也不拆分,而是保留这些报文的边界。
报文的边界来自于UDP首部有个长度字段,记录了报文长度
TCP是面向字节流
的
TCP根据发送策略,从发送缓存中提取一定数量的字节,构建TCP报文段并发送。
接收方的TCP一方面从所接收到的TCP报文段中取出数据载荷部分,并存储在接收缓存中;一方面将接收缓存中的一些字节交付给应用进程(这个发送方和接收方都有取一些字节,一定数量字节等字眼,和下图中,字节标号不连续对应)
TCP不保证接收方应用进程所收到的数据块与发送方应用进程和发出的数据块具有对应大小的关系。例如,发送方应用进程交给发送方的TCP,共10个数据块,。但接收方的TCP可能只用了4个数据块,就把收到的字节流交付给了上层的应用进程,不过接收方应用进程收到的字节流必须和发送方应用进程发出的字节流完全一样。
当然接收方的应用进程必须有能力识别收到的字节流,把它还原成有意义的应用层数据,也就是说 TCP是面向自字节流的,这正是TCP实现可靠传输,流量控制以及拥塞控制的基础。
下图只画出了一个方向的数据流,在实际网络中基于TCP连接的两端,可以同时进行TCP报文段的发送和接收,也就是全双工通信。另外图中TCP报文段的数据部分只包含了几个字节,实际当中一个TCP报文段包含上千个字节是很常见的
如果UDP过大IP层还是会对UDP报文进行分片的,但是会在IP层又组装回去,因为UDP有边界。(但这个我们在第一部分末尾说过,为了减少丢包带来的损失,应该控制UDP报文大小不要太大,一般会控制其小于512字节)
而TCP是面向字节流的,所以需要收发双方在应用层规定数据包的结构和收发边界——也就是所谓的避免TCP黏包。在数据包大小上,TCP则没什么限制,因为其本身就没有报文的概念。
UDP向上层提供的是不可靠的传输服务。因此对于UDP用户数据报出现的误码和丢失等问题,UDP并不关心。基于UDP的这个特点,UDP适用于:实时应用,例如IP电话、视频会议
TCP:尽管网际层中的IP协议向上层提供的是无连接不可靠的传输服务,也就是说 IP数据报可能在传输过程中出现丢失或误码,但只要运输层使用TCP协议,就可向其上层提供面向连接的可靠传输服务。我们可将其想象成使用TCP协议的收发双方,基于TCP连接的可靠性到进行数据传输,不会出现误码、丢失、乱序以及重复等传输差错。TCP适用于要求可靠传输的应用,例如:文件传输
TCP可靠传输的实现是依赖于滑动窗口机制,包括流量控制,拥塞控制,当然超时重传也是在保证可靠传输,还有三次握手。
具体应该如下:
TCP是通过序列号、检验和、确认应答信号(这几个在三次握手中起作用)、超时重传、连接管理、窗口控制、流量控制、拥塞控制一起保证TCP传输的可靠性的
UDP也可以实现可靠传输,需要在应用层上加一些可靠传输机制,比如在UDP和HTTP之间加一些协议。— —可以看QUIC协议
对比两者报文的首部。
对于UDP报文的首部而言:由于UDP不提供可靠传输服务,它仅在网际层的基础上添加了用于区分应用进程的端口。其首部只有4个字段,每个字段两个字节,总共8个字节,非常简单的首部。
TCP报文的首部:最小长度为20字节,最大长度为60字节,这是因为TCP要实现可靠传输,流量控制、拥塞控制等服务,其首部自然会比较复杂,首部中的字段比较多,首部长度也比较长
UDP前面其实还有12字节的伪首部(包括源IP地址、目的IP地址、0、17、UDP长度),仅在计算检验和时起作用,并不会传入IP层
17表示UDP,上节讲过。
UDP的报文首部写了这个报文有多长,而TCP没有。因此UDP是基于报文的,它传播的消息有边界,而TCP没有边界! 纯字节流,因此黏包!— —反复理解消息边界,后面看看源码,面试被问过为什么UDP不黏包,UDP报文里有什么然他不黏包(只说字节流是不够的,重要的是边界!报文里标记边界或者标记报文长度)
UDP的无序问题可能得应用层解决了,不然看直播或者视频通话的时候不就会各个帧乱序出现吗。
TCP给每个字节都按序编号,滑动窗口只有里面的字节数据都收到了(不就有序且没缺失了)才会滑动,没收到就阻塞,这个又引出队头阻塞问题。