socket协议
一、socket
1、什么是socket?
socket是一个模块, 又称套接字,用来封装 互联网协议(应用层以下的层)。
2、为什么要有socket?
socket可以实现 互联网协议应用层以下的层的工作。
- 提高开发效率
3、怎么使用socket?
import socket
写socket套接字:
Client
Server
服务端:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 9527)) sk.listen(5) # 循环实现可接受多个用户访问 while True: conn, addr = sk.accept() print(addr)
## 循环实现循环通信
while True: # 监听代码块是否有异常出现 try: data = conn.recv(1024) if len(data) == 0: break if data.decode('utf-8') == 'q': break print(data) send_data = input('服务端:') conn.send(send_data.encode('utf-8')) # 捕获异常信息,并打印 # e: 报错信息 except Exception as e: print(e) break conn.close()
客户端:
import socket sk = socket.socket() sk.bind(('127.0.0.1', 9527)) while True: send_data = input('客户端:') sk.send(send_data.encode('utf-8')) data = sk.recv(1024) if len(data) == 0: break if data.decode('utf-8') == 'q': break print(data.decode('utf-8')) sk.close()
二、粘包问题
- 问题: 无法确认对方发送过来数据的大小。
- 问题: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。
解决: 确认对方数据的大小。
演示服务端:
# import socket # import subprocess # # server = socket.socket() # # server.bind( # ('127.0.0.1', 9000) # ) # # server.listen(5) # # while True: # conn, addr = server.accept() # print(addr) # # while True: # try: # # recv从内存中获取数据 # cmd = conn.recv(10) # # if len(cmd) == 0: # continue # cmd = cmd.decode('utf-8') # dir # if cmd == 'q': # break # # # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果 # obj = subprocess.Popen( # # cmd接收解码后的字符串 # cmd, shell=True, # stdout=subprocess.PIPE, # stderr=subprocess.PIPE, # ) # # # 结果交给result变量名 # result = obj.stdout.read() + obj.stderr.read() # print(len(result)) # # gbk # print(result.decode('gbk')) # # # 将结果返回给客户端 # conn.send(result) # # except Exception as e: # print(e) # break # # conn.close() # 问题2: import socket server = socket.socket() server.bind( ('127.0.0.1', 9000) ) server.listen(5) conn, addr = server.accept() data = conn.recv(10) print(data) data = conn.recv(1024) print(data) data = conn.recv(1024) print(data)
客户端:
# import socket # # client = socket.socket() # # client.connect( # ('127.0.0.1', 9000) # ) # # while True: # # cmd = input('客户端输入的内容: ') # # client.send(cmd.encode('utf-8')) # # data = client.recv(19190) # print(len(data)) # print(data.decode('gbk')) # # print('*' * 100) # # data = client.recv(1024) # # print(data.decode('gbk')) # # print('*' * 100) # # data = client.recv(1024) # # print(data.decode('gbk')) # # print('*' * 100) # # data = client.recv(1024) # # print(data.decode('gbk')) # # print('*' * 100) # # data = client.recv(1024) # # print(data.decode('gbk')) # # # # # 问题2: import socket client = socket.socket() client.connect( ('127.0.0.1', 9000) ) client.send(b'hello') client.send(b'hello') client.send(b'hello')
三、解决粘包问题
- 无论哪一端先发送数据
- 客户端
- 先制作报头,并发送 (struct)
- 发送真实数据
- 服务端:
- 接收报头,并解包获取 真实数据长度
- 根据真实数据长度 接收真实数据
recv(真实数据长度)
struct模块
''' 1.struct是什么? 是一个python内置的模块, 它可以将固定长度的数据,打包成固定格式的长度。 # 模式 i: 4 # 其他模式 2.作用: 可以将真实数据,做成一个固定长度多的报头,客户端发送给服务端,服务端可以接收报头, 然后对报头进行解包,获取真实数据的长度,进行接收即可。 ''' import struct data = b'11111111111111' print(len(data)) # 打包制作报头 header = struct.pack('i', len(data)) print(header) print(len(header)) # 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度 res = struct.unpack('i', header)[0] print(res)
客户端:
import socket import struct client = socket.socket() client.connect( ('127.0.0.1', 9000) ) while True: cmd = input('客户端输入的内容: ') cmd_bytes = cmd.encode('utf-8') # 做一个报头 header = struct.pack('i', len(cmd_bytes)) print(len(header)) client.send(header) # 待服务端确认长度后,发送真实数据长度 client.send(cmd_bytes) # 接收服务端返回的报头 headers = client.recv(4) # 解包,接收服务端返回的真实数据 data_len = struct.unpack('i', headers)[0] result = client.recv(data_len) print('接收服务端返回的真实数据长度', len(result)) print(result.decode('gbk'))
服务端:
import socket import subprocess import struct server = socket.socket() server.bind( ('127.0.0.1', 9000) ) server.listen(5) while True: conn, addr = server.accept() print(addr) while True: try: # 获取客户端传过来的报头 header = conn.recv(4) # 解包获取真实数据长度 data_len = struct.unpack('i', header)[0] # 准备接收真实数据 cmd = conn.recv(data_len) if len(cmd) == 0: continue cmd = cmd.decode('utf-8') # dir if cmd == 'q': break # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果 obj = subprocess.Popen( # cmd接收解码后的字符串 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) # 结果交给result变量名 result = obj.stdout.read() + obj.stderr.read() print('发给客户端端的真实数据长度', len(result)) # gbk # print(result.decode('gbk')) # 将结果返回给客户端 # 做一个报头, 返回给客户端 header = struct.pack('i', len(result)) print(len(header)) conn.send(header) conn.send(result) except Exception as e: print(e) break conn.close()
客户端:
import socket import struct import json client = socket.socket() client.connect( ('127.0.0.1', 9000) ) while True: movie_name = input('请输入上传电影的名字: ') # 伪装电影真实数据 movie_len = 1000000 send_dic = { 'movie_name': movie_name, 'movie_len': movie_len } # 序列化 json = json.dumps(send_dic) print(json) print(json.encode('utf-8')) print(len(json.encode('utf-8'))) json_bytes = json.encode('utf-8') # 做一个报头 header = struct.pack('i', len(json_bytes)) # 先发送报头 client.send(header) # 后发送真实数据 client.send(json_bytes)
服务端:
import socket import json import struct server = socket.socket() server.bind( ('127.0.0.1', 9000) ) server.listen(5) while True: conn, addr = server.accept() print(addr) while True: try: # 获取客户端传过来的报头 header = conn.recv(4) # 解包获取真实数据长度 json_len = struct.unpack('i', header)[0] # 接收json(dict)的真实长度 json_bytes_data = conn.recv(json_len) # 将bytes类型数据转为json数据 json_data = json_bytes_data.decode('utf-8') # 反序列化 json ---> dict back_dic = json.loads(json_data) print(back_dic) print(back_dic.get('movie_len')) # 准备接收真实数据 # movie_data = conn.recv(back_dic.get('movie_len')) # print(movie_data) except Exception as e: print(e) break conn.close()