内容概要
- socket套接字
- 通信循环
- 链接循环
- 黏包问题
- 大文件上传
socket套接字简介
| |
| 思考:需要编写代码操作OSI七层 相当的复杂 |
| 由于操作OSI七层是所有cs架构的程序都需要经历的过程 所以有固定的模块 |
| socket套接字是一门技术 |
| socket模块>>>:提供了快捷方式 不需要自己处理每一层 |
| """ |
| 以后我们写软件连socket的影子都看不到 因为被封装起来 |
| socket是最底层的原理 很多框架都封装了 其实我们不需要深入研究 |
| """ |
用socket编写C/S架构
服务端
| import socket |
| |
| |
| server = socket.socket() |
| """ |
| 通过查看源码得知 |
| 括号内不写参数默认就是基于网络的遵循TCP协议的套接字 |
| """ |
| server.bind(('127.0.0.1', 8080)) |
| """ |
| 服务端应该具备的特征 |
| 固定的地址 |
| ... |
| 127.0.0.1是计算机的本地回环地址 只有当前计算机本身可以访问 |
| """ |
| server.listen(5) |
| """ |
| 半连接池(暂且忽略 先直接写 后面讲) |
| """ |
| sock, addr = server.accept() |
| """ |
| listen和accept对应TCP三次握手服务端的两个状态 |
| """ |
| print(f'客户端地址{addr}') |
| data = sock.recv(1024) |
| print(data.decode('utf8')) |
| sock.send(('hello hoe are you!!!'.encode('utf8')) |
| """ |
| recv和send接收和发送的都是bytes类型的数据 |
| """ |
| sock.close() |
| server.close() |

客户端
| import socket |
| |
| |
| client = socket.socket() |
| client.connect(('127.0.0.1', 8080)) |
| |
| client.send(b'hello baby!!!') |
| data = client.recv(1024) |
| print(data.decode('utf8')) |
| |
| client.close() |

注意: 服务端与客户端首次交互 一边是recv那么另一边必须是sand 两边不能相同否则就会陷入‘冷战’(阻塞)
通信循环
| 1.先解决消息固定问题 |
| 利用input获取用户输入 |
| 2.解决通信循环问题 |
| 将双方用于数据交互的代码循环起来 |
| |
| |
| while True: |
| data = sock.recv(1024) |
| print(data.decode('utf8')) |
| msg = input('请回复消息>>>:').strip() |
| sock.send(msg.encode('utf8')) |
| |
| |
| while True: |
| msg = input('请输入你需要发送的消息>>>:').strip() |
| client.send(msg.encode('utf8')) |
| data = client.recv(1024) |
| print(data.decode('utf8')) |
| |
| ''' |
| 加入循环后双方都可一直来回发送,接收消息 |
| ''' |

代码优化及链接循环
当我们关掉服务端时,客户端会报错:

代码优化办法
| 1.发送消息不能为空 |
| 统计长度并判断即可 |
| 2.反复重启服务端可能会报错>>>:address in use |
| 这个错在苹果电脑报的频繁 windows频率较少 |
| from socket import SOL_SOCKET,SO_REUSEADDR |
| server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) |
| 3.链接循环 |
| """ |
| 如果是windows 客户端异常退出之后服务端会直接报错 |
| 处理方式 |
| 异常处理 |
| 如果是mac或linux 服务端会接收到一个空消息 |
| 处理方式 |
| len判断 |
| """ |
| 客户端如果异常断开 服务端代码应该重新回到accept等待新的客人 |
| |
半连接池
| listen(5) |
| |
| |
| |
| 设置的最大等待人数 >>>: 节省资源 提高效率 |
黏包问题
| |
| data1 = conn.recv(1024) |
| print(data1) |
| data2 = conn.recv(1024) |
| print(data2) |
| data3 = conn.recv(1024) |
| print(data3) |
| |
| |
| client.send(b'hello') |
| client.send(b'jimi') |
| client.send(b'big baby') |
| """ |
| 三次打印的结果 |
| b'hellojimibig baby' |
| b'' |
| b'' |
| """ |
| |
| 会将数据量比较小并且时间间隔比较短的数据整合到一起发送 |
| 并且还会受制于recv括号内的数字大小(核心问题!!!) |
| 流式协议:跟水流一样不间断 |
| |
| """ |
| 问题产生的原因其实是因为recv括号内我们不知道即将要接收的数据到底多大 |
| 如果每次接收的数据我们都能够精确的知道它的大小 那么肯定不会出现黏包 |
| """ |
| |
| 思路: |
| 困扰我们的核心问题是不知道即将要接收的数据多大 |
| 如果能够精准的知道数据量多大 那么黏包问题就自动解决了!!! |
解决黏包问题
| 方向:精准获取数据的大小 |
| ''' |
| struct模块 |
| 该模块可以把一个类型转为固定长度的bytes |
| ''' |
| |
| import struct |
| |
| data1 = 'hello world' |
| print(len(data1)) |
| res1 = struct.pack('i',len(data1)) |
| print(len(res1)) |
| ret1 = struct.unpack('i',res1) |
| print(ret1) |
| |
| """ |
| pack可以将任意长度的数字打包成固定长度 |
| unpack可以将固定长度的数字解包成打包之前数据真实的长度 |
| """ |
| |
| """ |
| 思路: |
| 1.先将真实数据打包成固定长度的包 |
| 2.将固定长度的包先发给对方 |
| 3.对方接收到包之后再解包获取真实数据长度 |
| 4.接收真实数据长度 |
| """ |
struct模块可以转对应固定的长度:
黏包问题及大文件传输
服务端
| import socket |
| import os |
| import struct |
| import json |
| |
| |
| server = socket.socket() |
| server.bind(('127.0.0.1', 8080)) |
| server.listen(5) |
| |
| conn, addr = server.accept() |
| |
| data_dict = { |
| 'file_name':'老铁没毛病合集.mp4', |
| 'file_desc':'封控一个多月了', |
| 'file_size':os.path.getsize(r'struct模块.py') |
| } |
| |
| dict_json_str = json.dumps(data_dict) |
| dict_bytes = dict_json_str.encode('utf8') |
| dict_package_header = struct.pack('i', len(dict_bytes)) |
| |
| conn.send(dict_package_header) |
| |
| conn.send(dict_bytes) |
| |
| with open(r'04 struct模块.py', 'rb') as f: |
| for line in f: |
| conn.send(line) |
客户端
| import socket |
| import struct |
| import json |
| |
| client = socket.socket() |
| client.connect(('127.0.0.1', 8080)) |
| ''' |
| recv括号内的数字尽量不要写太大 1024 2048 4096足够了 |
| 字典数据很难突破上面的数值 |
| |
| 所以针对大文件的接收应该采用循环的形式一次接受一点点 |
| ''' |
| |
| dict_header_len = client.recv(4) |
| |
| dict_real_len = struct.unpack('i', dict_header_len)[0] |
| |
| dict_data_bytes = client.recv(dict_real_len) |
| dict_data = json.loads(dict_data_bytes) |
| print(dict_data) |
| |
| recv_size = 0 |
| with open(dict_data.get('file_name'),'wb') as f: |
| while recv_size < dict_data.get('file_size'): |
| data = client.recv(1024) |
| recv_size += len(data) |
| f.write(data) |
| |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)