传输层主要工作在终端设备
传输层:管理端到端的通信连接,解决跨设备跨网络的进程与进程之间的通信
FTP | HTTP(网站) | HTTPS(安全) | DNS | TELNET |
---|---|---|---|---|
21 | 80 | 443 | 53 | 23 |
UDP首部
16位源端口号 | 16位目的端口号 | 16位UDP长度 | 16位UDP校验和 |
---|---|---|---|
包括数据在内的长度(最小值为8字节,只包含头部) | 检验传输中是否出错 |
-----------------------------UDP数据------------------------
序号:
确认号:
数据偏移:
TCP标记:
标记 | 含义 |
---|---|
URG | Urgent:紧急位,URG=1,表示紧急数据 |
ACK | Acknowledgement:确认位,ACK=1,确认号才生效 |
PSH | Push:推送位,PSH=1,尽快把数据交付给应用层 |
RST | Reset:重置位,RST=1,重新建立连接 |
SYN | Synchronization:同步位,SYN=1表示连接请求报文 |
FIN | Finish:终止位,FIN=1 表示释放连接 |
窗口:
紧急指针:
通过超时重传来保证可靠传输
超时定时器:每发送一个消息,都需要设置一个定时器
将交流的双方简化为发送方和接收方
发送方:发送消息1后就停止发送消息,等待接收方的确认消息1;收到确认消息1后发送消息2,停止发送消息,等待确认消息2……,一直按照停止等待的方式发送消息
接收方:停止发送确认消息,等待发送方的消息,收到消息1后,发送确认消息1,然后停止发送,等待发送方发送新的消息……,也是按照停止等待的方式接收消息
1. 发送方的消息在发送过程中丢失:
此时发送方发送消息1后,在等待一段时间后还没收到接收方的确认消息1,会再次发送消息1 ,即超时重传
2. 接收方发送的确认消息丢失:
发送方在发送完消息1后,等待一段时间后还没收到确认消息1,会再次发送消息1,同样为超时重传
3. 应该在这次收到的确认消息在很久以后才收到:
发送方没收到确认消息1,等待一段时间后,再次发送消息1,同样为超时重传
此时虽然25和27已经收到,但是让不能往前推动,因为23、24还没收到,此时发送方等待一段时间后,没收到确认消息,再次从23开始重传
根据上述问题,进行选择重传来解决
TCP协议特有的功能
通过窗口大小控制对方发送速率
最后在接收方有空余窗口时向发送方发送空余的窗口数1000这条消息丢失了,此时接收方和发送方进入一个死锁局面,由于TCP可靠传输只针对的是确认消息和发送消息的序列号,所以关于窗口的特殊消息是不做可靠传输的,不会超时重传,因此这里需要一个坚持定时器,解决这个死锁局面。
坚持定时器:
在发送的数据量到达慢启动阈值时,采用
发送的数据量的增长图示
指数增长部分使用慢启动算法,到达慢启动阈值后,采用拥塞避免算法
标记 | 含义 |
---|---|
ACK | Acknowledgement:确认位,ACK=1,确认号才生效 |
SYN | Synchronization:同步位,SYN=1 表示连接请求报文 |
FIN | Finish:终止位,FIN=1 表示释放连接 |
第一次:发送方向接收方发送请求连接消息,消息序列号为x
第二次:接收方向发送方发送请求连接、确认连接消息,seq为本消息的序列号,ack为确认的消息的序列号加一,表示对seq=x已经确认
第三次:发送方向接收方发送确认连接消息,seq=x+1表示本消息的序列号,ack=y+1表示对seq=y的消息的确认
发送方的结束请求第二次等待阶段 (FIN-WAIT-2):等待接收方将自己的数据发送完
主动释放连接的一方在双方都确认释放连接之后还会有一个等待释放状态(等待计时器TIME-WAIT)
服务端:
import socket
def server():
# 1、创建套接字
s=socket.socket()
host='127.0.0.1'
# IP地址,这里为回环地址
port=6666
# 2、绑定套接字
s.bind((host,port))
# 3、监听
s.listen(5)
# 4、发送消息
while True:
# 接收到的连接即为客户端,向客户端发送消息
c,addr=s.accept()
print('Connet Addr:',addr)
c.send(b'Welcome to my course.')
# 字符串前加b表示这是一个bytes对象,由于客户端接收的为bytes对象
c.close()
if __name__ == '__main__':
server()
客户端:
import socket
def client(i):
# 1、创建套接字
s=socket.socket()
# 2、连接套接字
s.connect(('127.0.0.1',6666))
print('Recv msg:%s,Clinet:%d'%(s.recv(1024),i))
s.close()
if __name__ == '__main__':
for i in range(10):
client(i)
先run服务端,再run客户端;
服务端结果:
D:\software\python\python.exe D:/工作学习/计算机网络/编程/套接字/服务端.py
Connet Addr: ('127.0.0.1', 37445)
Connet Addr: ('127.0.0.1', 37446)
Connet Addr: ('127.0.0.1', 37447)
Connet Addr: ('127.0.0.1', 37448)
Connet Addr: ('127.0.0.1', 37449)
Connet Addr: ('127.0.0.1', 37450)
Connet Addr: ('127.0.0.1', 37451)
Connet Addr: ('127.0.0.1', 37452)
Connet Addr: ('127.0.0.1', 37453)
Connet Addr: ('127.0.0.1', 37454)
客户端结果:
D:\software\python\python.exe D:/工作学习/计算机网络/编程/套接字/客户端.py
Recv msg:b'Welcome to my course.',Clinet:0
Recv msg:b'Welcome to my course.',Clinet:1
Recv msg:b'Welcome to my course.',Clinet:2
Recv msg:b'Welcome to my course.',Clinet:3
Recv msg:b'Welcome to my course.',Clinet:4
Recv msg:b'Welcome to my course.',Clinet:5
Recv msg:b'Welcome to my course.',Clinet:6
Recv msg:b'Welcome to my course.',Clinet:7
Recv msg:b'Welcome to my course.',Clinet:8
Recv msg:b'Welcome to my course.',Clinet:9
网络套接字:
无论是否需要跨网络,数据都会经过协议栈
域套接字:
使用S开头的域套接字文件进行通信,不需要经过协议栈
所以:
如果是单机通信,推荐使用域套接字,跨网络通信使用网络套接字