socket(套接字)
基于socket实现客户端与服务端通信
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
socket初识
127.0.0.1:本地回环地址,只能本机访问
-----------------------------服务端------------------------------ import socket server = socket.socket() # 类似于买手机 server.bind(('127.0.0.1', 8080)) # 类似于插手机卡 bind((IP,PORT)) server.listen(5) # 开机 半连接池 conn, addr = server.accept() # 待机等待接电话 data = conn.recv(1024) # 接听别人说话 只接收1024个字节 bytes print(data) conn.send(b'hello!') # 跟别人说话 conn.close() # 关闭通信连接 server.close() # 关闭服务端 -----------------------------客户端------------------------------ import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) # 找服务器 client.send(b'hello how much?') data = client.recv(1024) print(data) client.close()
TCP协议的特点
会将数据量比较小的,并且时间间隔比较短的数据,一次性打包发送给接收端
-----------------------------服务端------------------------------ import socket server = socket.socket() server.bind(('127.0.0.1',8088)) server.listen(5) # 半连接池 conn,addr = server.accept() data = conn.recv(1024) print(data) data = conn.recv(1024) print(data) data = conn.recv(1024) print(data) >>>:b'hellohellohello' b'' b'' -----------------------------客户端------------------------------ import socket client = socket.socket() client.connect(('127.0.0.1',8088)) client.send(b'hello') client.send(b'hello') client.send(b'hello')
通信循环
-----------------------------服务端------------------------------ import socket """ 服务端: 要有固定的ip和port 24小时不间断提供服务 """ server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) conn, addr = server.accept() # 阻塞 while True: try: data = conn.recv(1024) # 阻塞 if len(data) == 0: break # 针对linux和mac系统 客户端异常断开反复收空的情况 print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close() -----------------------------客户端------------------------------ import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
struct模块
import struct data = 'seionksngjgm,xmdnabnk ko' res = struct.pack('i',len(data)) print('res:',res) # res: b'x18x00x00x00' print('len(res):',len(res)) # 4 ret = struct.unpack('i',res) print('ret:',ret) # (24,) print('ret[0]:',ret[0]) # 24
链接循环
-----------------------------服务端------------------------------ import socket import subprocess import struct import json """ 服务端: 要有固定的ip和port 24小时不间断提供服务 """ server = socket.socket() server.bind(('127.0.0.1', 8081)) server.listen(5) # 半连接池 while True: conn, addr = server.accept() # 阻塞 while True: try: data = conn.recv(1024).decode('utf-8') # 阻塞 if len(data) == 0: break # 针对linux和mac系统 客户端异常断开反复收空的情况 obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() print(len(stdout + stderr)) header_dic = { 'filename': 'cls.av', 'len': len(stdout + stderr) } header_bytes = json.dumps(header_dic).encode('utf-8') # 制作报头 header = struct.pack('i', len(header_bytes)) # 将需要发送给客户端的数据打包成固定4个字节 conn.send(header) conn.send(header_bytes) conn.send(stdout + stderr) except ConnectionResetError: break conn.close() server.close() -----------------------------客户端------------------------------ import socket import struct import json client = socket.socket() client.connect(('127.0.0.1', 8081)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) header = client.recv(4) # 对这个头进行解包,获取真实数据的长度 head_len = struct.unpack('i', header)[0] head_dic = json.loads(client.recv(head_len).decode('utf-8')) print(head_dic) # 对需要接受的数据 进行循环接收 total_size = head_dic['len'] recv_size = 0 res = b'' while recv_size < total_size: data = client.recv(1024) res += data recv_size += len(data) print(res.decode('gbk'))
粘包问题
-----------------------------服务端------------------------------ import socket """ 服务端: 要有固定的ip和port 24小时不间断提供服务 """ server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) # 半连接池 while True: conn, addr = server.accept() # 阻塞 while True: try: data = conn.recv(1024) # 阻塞 if len(data) == 0: break # 针对linux和mac系统 客户端异常断开反复收空的情况 print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close() -----------------------------客户端------------------------------ import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
首先:只有在TCP协议中才会出现粘包现象,因为TCP协议是流式协议
特点:TCP协议会将数据量小并且时间间隔比较短的数据一次性打包发送出去
本质:其实还是因为我们不知道需要接收的数据的长短
# 如何解决粘包问题?
# 1 发送数据直接先告诉对方数据量的大小
# 2 利用struct模块定制我们自己的消息传输协议
最终方法:解决粘包问题
1.先发报头
2.再发字典
3.再发你的真实数据
1.先收4个长度的报头
2.解包拿到字典数据长度
3.接收字典(反序列化) 》》》 获取字典里面所有信息
4.接收真实数据