标签: 网络 读书笔记 校招 面试
传输层协议为运行在不同Host上的进程提供了一种逻辑通信机制
端系统运行传输层协议
传输层可以为应用提供的协议主要有TCP和UDP
传输层VS网络层
网络层: 提供主机之间的逻辑通信
传输层: 提供进程之间的逻辑通信
可靠、按序的交付服务(TCP)
不可靠的交付服务(UDP)
两种服务均不保证
接收端进行多路分组:传输层依据头部信息将收到的Segment交付给正确的Socket,即正确的进程.
发送端进行多路复用:从多个Socket接收数据,为每块数据封装上头部信息,生成Segment,交给网络层.
主机接收到IP数据报(datagram)
主机收到Segment之后,传输层协议提取IP地址和端口号信息,将Segment导向相应的Socket.
主机收到UDP之后,检查目的端口号然后把数据导向相应Socket.
不同源IP地址和源端口号的IP数据包被导向同一个Socket
UDP的Socket用二元组标识:
- (目的IP地址,目的端口号)
Java中使用UDP的方式说明只关心监听的端口,而不管数据从何而来
DatagramSocket socket = new DatagramSocket(servPort);
DatagramPacket packet = new DatagramPacket(new byte[ECHOMAX], ECHOMAX);
while(true){
socket.receive(packet);
System.out.println("Handling client at " + packet.getAddress().getHostAddress()
+ " on port" + packet.getPort());
socket.send(packet);
packet.setLength(ECHOMAX);
}
TCP的Socket用四元组标识:
服务器可能同时支持多个TCP
接收端利用所有的四个值将Segment导向合适的Socket
Web服务器为每个客户端开不同的 Socket
ServerSocket servSock = new ServerSocket(servPort);
while(true){
//可以看出每次有新连接进来,accept方法都会返回一个新的Socket对象
Socket clntSock = servSock.accept();
SocketAddress clientAddress =
clntSock.getRemoteSocketAddress();
System.out.println("Handling client at " + clientAddress);
InputStream in = clntSock.getInputStream();
OutputStream out = clntSock.getOutputStream();
while((recvMsgSize = in.read(receiveBuf)) != -1) {
out.write(receiveBuf, 0, recvMsgSize);
}
clntSock.close();
}
基于Internet IP协议
“Best effort”服务,UDP段可能
无连接
为什么UDP有这么多缺点,还是坚持用UDP协议?
常用于流媒体应用
在UDP上实现可靠数据传输?
UDP报文段结构很简单,主要有以下字段
|———-32bit———-|
|–源端口号–|–目的端口–|
|—-长度—-|—校验和—|
|——–应用数据———|
发送方:
接收方:
为什么链路层提供CRC,传输层还是要用校验和?
什么是可靠?
rdt1.0非常简单,假设下层信道是按序到达,不丢包,且不出现比特差错的.
那么rdt1.0的状态机很简单,不作赘述.
看图即可
rdt2.0中假设的信道:
rdt2.0中假设信道可能会翻转分组中的位(bit)
如何从错误中恢复?
基于这种重传机制的rdt协议称为ARQ(Automatic Repeat reQuest)协议
rdt 2.0中引入的新机制
rdt2.0的状态机
发送端状态机
接收端状态机
rdt2.0看起来能运行了,但是忽略了一个重要的事实,那就是ACK和NAK分组也可能会被损坏.
当ACK/NAK被损坏时,可以采取这几种方法:
如何解决重复分组问题?
因为当发送端发送一个分组时,它会等待接收方的回复,因此这种协议被称为停止-等待协议
rdt2.1的状态机
发送方
接收方
发送方:
接收方:
rdt2.2在rdt2.1的基础上去除了NAK消息.
如何实现?
发送方收到重复ACK之后,采取和收到NAK一样的动作,重传当前分组.
rdt3.0在2.X的基础上完全模拟了真实的信道.信道既可能发生错误,也可能丢失分组.
为了解决这个问题?
方法: 发送方等待”合理”的时间
rdt3.0的FSM
rdt3.0虽然能用,但是性能很差,因为这是一个停等协议.
允许发送方在收到ACK之前连续发送多个分组
窗口:
窗口尺寸为N:最多有N个等待确认的消息
滑动窗口
随着协议的运行,窗口在序列号空间内向前滑动
滑动窗口协议有GBN和SR
发送方:
分组头部包含k-bit序列号
窗口尺寸为N,最多允许N个分组未确认
ACK(n):确认到序列号n的分组均已被正确接收(累计确认)
可能收到重复ACK
为空中的分组设置定时器(timer)
超时Timeout(n)事件:重传序列号大于等于n,还未收到ACK的所有分组
发送方FSM:
接收方:
ACK机制:发送拥有最高序列号的、已被正确接收的分区ACK
乱序到达的分组
接收方FSM
GBN的缺陷
* 重传所有分组,造成资源浪费
SR协议的缺陷
如图
对于第一种和第二种情况,发送方发送的分组序号都是0,但是第一种情况发送的是重发的分组0.
而第二种情况发送的复用的序号0,实际上是一个新的分组0.
发送方无法分辨这两种情况.
因此对于SR协议,要求序列号空间满足Ns+NR<=2k
TCP报文段结构:
序列号:
ACKs:
TCP如何处理乱序的TCP?
TCP规范中没有规定,由TCP实现者做出决策
TCP在IP层提供的不可靠服务的基础上实现可靠的数据传输服务
流水线机制
累积确定
触发重传的事件
如何设置定时器的超时时间,希望设置成大于RTT,但是RTT是动态的
如果设置的过短,会引起不必要的重传
如果设置的过长,则会对段丢失的反应慢
如何估计RTT?
SampleRTT:测量从段发出去到收到ACK的时间忽略重传
SampleRTT的变化:测量多个SampleRTT,求平均值,形成RTT的估计值EstimatedRTT
EstimatedRTT = (1-a) * EstimatedRTT + a * SampleRtt
加权指数平均,典型值: 0.125
定时器超时时间的设置:
B典型值 : 0.25
TCP发送端程序
NextSeqNum = InitialSeqNum
SendBase = InitialSeqNum
loop (forever) {
switch(event):
event: data received from application above
create TCP segment with sequence number NextSeqNum
if (timer currently not running)
start timer
pass segment to IP
NextSeqNum = NextSeqNum + length(data)
event: timer timeout
retransmit not-yet-acknowledged segment with
smallest sequence number
start timer
event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase = y
if (there are currently not-yet-acknowledged segments)
start timer
}
}
TCP 的实现中,如果发生超时,超时时间间隔将重新设置,即将超时时间间隔加倍,导致其很大
重发丢失的分组之前要等待很长时间
通过重复ACK检测分组丢失
Sender会背靠背地发送多个分组
如果sender收到对同一数据的3个ACK,则假定该数据之后的段已经丢失
快速重传算法
event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase = y
if (there are currently not-yet-acknowledged segments)
start timer
}
else {
increment count of dup ACKs received for y
if (count of dup ACKs received for y = 3) {
resend segment with sequence number y
}
TCP sender和receiver在传输数据前需要建立连接
初始化TCP变量
Client: 连接发起者
Server: 等待客户端请求 (具体可参见上面的java代码)
握手步骤:
1. Client发送syn 报文给Server(syn标识位置1)
没有数据
Server分配缓冲区(syn泛洪攻击)
确定Server初始的seq
四次挥手步骤
1. Client向Server发送FIN控制Segment
2. Server收到FIN,回复ACK,关闭连接,发送FIN
3. Client收到FIN,回复ACK
* 等待一段时间(怕Client发出去的ACK丢失)
4. Server收到ACK,连接关闭
步骤图:
原理
攻击者发送大量的syn分组但是不对该分组进行响应,让Server端开启大量buffer浪费服务器端空间
防范
接收方会为TCP连接分配buffer,如果发送方发的太快会导致接收方的buffer溢出,因此需要引入速度匹配机制
接收方会在Segment的头部字段将RcvWindow(接收窗口)的值告诉发送方,发送方限制自己已经发送但是还未收到ACK的数据不超过接收方的空闲RcvWindow尺寸
如果RcvWindow为0,会发生死锁.
所以其实发送方还是可以发送一个很小的段,以便捎回来一个新的RcvWindow.
拥塞
非正式定义:”太多主机发送了太多数据或者发送速度太快,以至于网络无法处理”
表现为:
1. 分组丢失(路由器缓存溢出)
2. 分组延迟过大
拥塞的三个代价:
1. 拥塞时,分组的延时达到最大
2. 拥塞时,分组会导致路由器缓冲区溢出,导致丢失分组
3. 拥塞时,多跳网络中前几跳的努力会因为某一跳路由器的丢失分组而白费
当拥塞到一定程度时,整个网络将无法传输数据
端到端的拥塞控制方法:
1. 网络层不提供显式的支持
2. 端系统观察丢包和延迟等网络行为判断是否发生了拥塞
3. TCP采取这种方法
网络辅助的拥塞控制:
1. 路由器向发送方显式的反馈网络的拥塞信息
2. 简单的拥塞指示(1bit):SNA,DECbit,TCP/IP ECN,ATM
3. 指示发送方应该采取何种速率(定量)
ARB:avaliable bit rate
* “弹性服务”
* 如果发送方路径”underloaded”就使用可用带宽
* 如果发送方路径拥塞就将发送速率降低到最低保障速率
RM(resource management)cells:专门用来指示网络拥塞状况的,穿插在正常的cell中发送
总结起来就是发送方发出去,交换机和接收方置位,然后接收方统一返回给发送方
RMcell中还有显式的ER字段:两个字节(定量的指示)
普通的RMcell中也有一个EFCI位指示拥塞,由交换机控制
如果RM cell前面的data cell的EFCI位被设为1,那么接收方在返回的RM cell中置CI位
TCP使用端到端的拥塞控制而不是使用网络辅助的拥塞控制,因为IP层不会向端系统提供显式的反馈.
设cwnd为拥塞窗口的大小,并假定发送方能通过cwnd调节发送速率
MSS为最大报文段大小
如果ack以非常慢的速度到达,则拥塞窗口将以非常慢的速度增加.另一个方面,如果确认以高速率到达,则该拥塞窗口将会更为迅速的增大.因为TCP使用确认来触发增大他的拥塞窗口的长度.
TCP的拥塞控制算法包含3个部分
加性增,乘性减:AIMD
当一个TCP连接开始时,cwnd初始值为一个MSS的最小值,这时可用带宽远大于发送速率
所以,在慢启动阶段,每收到一个ACK,则拥塞窗口增加一个MSS.这样下去,每过一个RTT,拥塞窗口翻倍
什么时候结束这种指数增长呢?
* 如果检测到丢包事件,TCP将cwnd设置为1并重新开始慢启动,并且将一个变量threshold置为当前拥塞窗口的一半
* 如果当cwnd的值达到threshold时,将进入拥塞避免模式(不再翻翻,线性增加)
* 如果检测到三个ACK(并没有丢包那么严重,还能传一点东西),执行快重传并进入快恢复状态
拥塞避免同样会增加cwnd,但是和慢启动不同,拥塞避免会每个RTT增加一个MSS(线性的)
如何结束拥塞避免呢?
对于引起进入快速恢复的每个冗余ACK,cwnd增加一个MSS.当最后一个ACK到达时,进入拥塞避免.如果出现超时事件,快速恢复在执行如同在慢启动和拥塞避免中相同的动作后,迁移到慢启动状态:当丢包事件发生后,cwnd被设置为一个MSS,并且threshold的值被设置为cwnd的一半.
由于TCP Tahoe版本已经不再使用,这里只讨论Reno版本中的拥塞控制