OSI七层协议之传输层
1.ROIT协议
2.TCP与UDP协议
规定了数据传输所需要遵循的原则
ps:数据传输能够遵循的协议有很多 TCP和UDP是较为常见的两个
TCP协议:
概念:传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
三次握手:三次握手建链接
建立双向通道
ps:洪水攻击:同时让大量的客户端朝服务器发送建立TCP链接的请求
四次挥手:四次挥手断链接
断开双向通道
'''
基于TCP传输数据十分安全,因为有双向通道。
基于TCP传输数据,数据不容易丢失!!! 不容易丢失的原因在于二次确认机制。每次发送数据都需要返回确认消息 否则在一定的时间会反复发送
'''
UDP协议
概念:用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
'''
基于UDP协议发送数据 没有任何的通道也没有任何的限制
UDP发送数据没有TCP安全(没有二次确认机制),所以是不可靠的
'''
TCP类似于打电话:你一句我一句 有来有往
UDP类似于发短信:只要发送了 不管别人看没看到 也不管回不回复
应用层
主要取决于程序员自己采用什么策略和协议
常见协议有:HTTP HTTPS FTP...
socket套接字
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
基于网络类型的套接字家族
套接字家族的名字:AF_INET
运行程序的时候,肯定是先确保服务端运行,然后再是客户端
import socket
# 1.创建一个socket对象
server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.接受客户端的链接请求
sock, address = server.accept()
print(sock, address) # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~') # 朝客户端发送数据
data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close() # 断链接
server.close() # 关机
import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024) # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server') # 朝服务端发送数据
# 4.关闭
client.close()
代码优化
'自定义交互内容 循环交互'
1.send与recv
客户端与服务端不能同时执行同一个
有一个收 另外一个就是发
有一个发 另外一个就是收
不能同时收或者发!!!
2.消息自定义
input获取用户数据即可(send方法只能发送bytes类型的数据,所以要注意用encode编码)
3.循环通信
给数据交互环节添加while循环即可
4.服务端能够持续提供服务
不会因为客户端断开连接而报错
异常捕获 一旦客户端断开连接 服务端结束通信循环 调到连接处等待
5.消息不能为空
判断是否为空 如果是则重新输入(主要针对客户端)
6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
7.客户端异常退出会发送空消息(针对mac linux)
针对接收的消息加判断处理即可
半连接池
sever.listen(5)
主要是为了做缓冲,避免太多无效等待
黏包问题
如图所示:客户端向服务端发送三条数据,本应分三次打印,却黏在了一起,组成一条数据打印
黏包原因:
1.TCP特性
流式协议:所有的数据类似于水流 连接在一起的
ps:数据量很小 并且时间间隔很多 那么就会自动组织到一起
2.recv
我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包。
于是,黏包问题被我们转换成获取传输数据大小的问题,只要能拿到数据的长度 ,就能解决黏包问题。由此我们引出struct模块,这个模块能够帮助我们打包数据。将他变成固定的长度。
"""
struct模块无论数据长度是多少 都可以帮你打包成固定长度
然后基于该固定长度 还可以反向解析出真实长度
思路
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收即可
"""
代码展示