- 目录
- OSI七层协议
- socket套接字编程
- 通信操作(cs架构)
- 黏包问题及解决办法
- OSI七层协议之传输层
1.传输层之TCP协议:需要建立双向通道(相当于打电话)
数据安全性:客户端与服务端数据传输会建立不同的通道并且会在相互传输时做备份并且会反复确认是否收到数据,等对方收到数据后才会删除备份
数据传输之三次握手与四次挥手
三次握手
四次挥手
2.传输层之UDP协议:数据传输没有限制(相当于发短信)
数据不安全性:客户端与服务端传输不会建立通道,通过网络传输并且互相不进行备份,传输后直接删除数据
- OSI七层协议之应用层:取决于程序员采用什么样的方法和策略
1.常见协议有:HTTP HTTPS FTP...
- socket套接字编程
1.类型:
基于文件类型的套接字家族:AF_UNIX
基于网络类型的套接字家族:AF_INET
2.推理一
服务端: import socket # 推理一# 创建一个socket对象 server = socket.socket() # 创建对象中的参数都是关键字参数所以不需要传输据
# 绑定访问的ip地址 server.bind(('127.0.0.1', 8090)) # ip地址,端口号# # 半连接池(允许访问的客户端数量) server.listen(8) # 数据传输通道并获得用户地址 sock, addr = server.accept() # 数据交互(发送与接收数据) sock.send(b'hahhaha') # b'aaaa' date = sock.recv(1024) # 接收数据的字节数 print(date) # 关闭交互(断连接与关机) sock.close() server.close() 客户端: import socket # 创建对象 client = socket.socket() # 连接服务器(ip地址与port端口号) client.connect(('127.0.0.1', 8090)) # 循环交互 while True: # 数据交互(接收与发送数据) date = client.recv(1024) print(date.decode('utf8')) # 获得输入的数据 client.send(b'xixixi')
3.推理二
服务端: import socket # 优化-交互循环# 创建一个socket对象 server = socket.socket() # 创建对象中的参数都是关键字参数所以不需要传输据
# 绑定访问的ip地址 server.bind(('127.0.0.1', 8090)) # ip地址,端口号
# 半连接池(允许访问的客户端数量) server.listen(8) # 数据传输通道并获得用户地址 sock, addr = server.accept() # 数据交互(发送与接收数据)# 循环交流 while True: # 获得输入的数据 info = input('请输入信息>>>:').strip() sock.send(info.encode('utf8')) # b'aaaa' date = sock.recv(1024) # 接收数据的字节数 print(date.decode('utf8')) 客户端: import socket # 创建对象 client = socket.socket() # 连接服务器(ip地址与port端口号) client.connect(('127.0.0.1', 8090)) # 循环交互 while True: # 数据交互(接收与发送数据) date = client.recv(1024) print(date.decode('utf8')) # 获得输入的数据 info = input('请输入信息>>>:').strip() client.send(info.encode('utf8'))
4.推理三
服务端: # 优化-客户端终止后服务端报错采用异常捕获处理(返回到接收新用户端操作)并判断是否敲空格 # 优化-交互循环 # 创建一个socket对象 server = socket.socket() # 创建对象中的参数都是关键字参数所以不需要传输据 # 绑定访问的ip地址 server.bind(('127.0.0.1', 8090)) # ip地址,端口号 # 半连接池(允许访问的客户端数量) server.listen(8) while True: # 数据传输通道并获得用户地址 sock, addr = server.accept() # 数据交互(发送与接收数据) # 循环交流 while True: # 采用异常捕获处理错误 try: # 获得输入的数据 info = input('请输入信息>>>:').strip() # 若没有输入数据敲了空格,进行判断 if len(info) == 0: continue sock.send(info.encode('utf8')) # b'aaaa' date = sock.recv(1024) # 接收数据的字节数 if len(date) == 0: break print(date.decode('utf8')) except ConnectionAbortedError: sock.close() break # 客户端: import socket # 创建对象 client = socket.socket() # 连接服务器(ip地址与port端口号) client.connect(('127.0.0.1', 8090)) # 循环交互 while True: # 数据交互(接收与发送数据) date = client.recv(1024) print(date.decode('utf8')) # 获得输入的数据 info = input('请输入信息>>>:').strip() client.send(info.encode('utf8'))
- 黏包问题:传输数据时由于不确定接收数据的字节数,因为传输数据时是似流水一样,所以会在接收数据时将数据归到一起,导致黏包问题
服务端: import socket # 产生一个服务端对象 server = socket.socket() # 获得对方ip地址与端口 server.bind(('127.0.0.1', 8090)) server.listen(8) sock, addr = server.accept() print(sock.recv(1024)) # b'hhahahhahciciiciifkkjjjkkkk' print(sock.recv(1024)) # b'' print(sock.recv(1024)) # b'' 客户端: import socket # 获得客户端对象 client = socket.socket() # 获得对方ip地址与端口 client.connect(('127.0.0.1', 8090)) # 输出数据 client.send(b'hhahahhah') client.send(b'ciciiciif') client.send(b'kkjjjkkkk')
- 黏包问题解决办法
1.解决办法一:获得对方输出数据的字节数
服务端: import socket # 产生一个服务端对象 server = socket.socket() # 获得对方ip地址与端口 server.bind(('127.0.0.1', 8090)) server.listen(8) sock, addr = server.accept() print(sock.recv(9)) # b'hhahahhah' print(sock.recv(9)) # b'ciciiciif' print(sock.recv(9)) # b'kkjjjkkkk' 客户端: import socket # 获得客户端对象 client = socket.socket() # 获得对方ip地址与端口 client.connect(('127.0.0.1', 8090)) print(len('hhahahhah')) # 9 # 输出数据 client.send(b'hhahahhah') client.send(b'ciciiciif') client.send(b'kkjjjkkkk')
2.解决办法二:由于无法确定对方输出的字节数,所以采用struct模块对数据进行固定长度数据打包
2.1 struct模块:将不确定的数据的字节数打包成统一字节数(struct.pack())
解包:将打包成统一字节数的 数据还原原本的数据字节数(struct.unpack)
注:struct模块对数据量特别大的数据无法进行打包
import struct info = '今天天气热的让人发慌' print(len(info)) # 10 res = struct.pack('i', len(info)) print(len(res)) # 4 res1 = struct.unpack('i', res) print(res1[0]) # 10
info_dict = {
'name': '索尼',
'price': 5800,
}
# 字典长度
print(len(info_dict)) # 2
# 进行打包处理
res = struct.pack('i', len(info_dict))
print(len(res)) # 4
# 解包处理
res1 = struct.unpack('i', res)
print(res1) # (2,)
print(res1[0]) # 2
2.2 采用struct模块解决黏包问题
思路:
传输数据
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据
接收数据
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收即可
服务端: import os import struct import socket import json print(os.path.getsize(r'D:\视频\沉香如屑.mp4')) # 729983 # 创建服务端对象 server = socket.socket() # 获得对方ip地址及端口 server.bind(('127.0.0.1', 8090)) server.listen(8) sock, addr = server.accept() # 视频信息字典 info_dict = { 'video_name': '沉香如屑', 'video_content': '如屑', 'video_size': 729983 } # 字典序列化为字符串 json_dict = json.dumps(info_dict) # 查看长度 json_dict_len = len(json_dict.encode('utf8')) # 对信息字典进行打包 dict_len = struct.pack('i', json_dict_len) # 发送打包的字典数据 sock.send(dict_len) # 发送真实的字典数据 sock.send(json_dict.encode('utf8')) # 发送视频数据 with open(r'D:\视频\沉香如屑.mp4', 'rb') as f: for line in f: sock.send(line) # 客户端 import socket import json import struct # 从客户端下载视频# 获得客户端对象 client = socket.socket() # 获得读取的ip地址与端口 client.connect(('127.0.0.1', 8090)) # 接收固定的长度的信息字典 dict_len = client.recv(4) # 解包固定的长度的信息字典 json_dict_len = struct.unpack('i', dict_len)[0] # 获得真实的字典长度 json_dict = client.recv(json_dict_len) # 反序列化字典 info_dict = json.loads(json_dict) print(info_dict) # {'video_name': '沉香如屑', 'video_content': '如屑', 'video_size': 729983}# 5.从数据字典中获取真实数据的各项信息 total_size = info_dict.get('video_size') video_size = 0 with open(r'%s' % r'D:\照片\沉香如屑.mp4', 'wb') as f: while video_size < total_size: data = client.recv(1024) f.write(data) video_size += len(data) print('文件下载完毕') # 文件下载完毕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现