struct模块是如何使用的呢?
import struct
msg = "我正在学习python的网络编程。"
msg_bs = msg.encode("utf-8") # 将数据编码转为字节
res = struct.pack("i", len(msg_bs)) # 将字节数据的长度打包成固定长度(4)
print(res)
bs = b"'\x00\x00\x00"
res = struct.unpack("i", bs) # 解包,解包后得到一个数组,取第一个元素即可
print(res)
msg_bs_len = res[0]
print(msg_bs_len)
执行结果:
b"'\x00\x00\x00"
(39,)
39
注意:
这里的i是int的意思,4个字节,就是4*8=32位,2**32次方就是可以打包的长度。也就是可以一次满足4G大小数据的打包。
看一组使用struct模块的tcp通信流程
---------------------------------------------struct_server.py-------------------------------------------
# coding:utf-8
import struct
import socket
import subprocess
server = socket.socket()
ip_port = ("127.0.0.1", 8001)
server.bind(ip_port)
server.listen(5)
conn, addr = server.accept()
while 1:
from_client_msg = conn.recv(1024)
print("来自客户端的消息:", from_client_msg.decode("utf-8"))
if not from_client_msg.decode("utf-8"):
print("stop")
break
res = subprocess.Popen(
from_client_msg.decode("utf-8"),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
cmd_result = res.stderr.read()
if not cmd_result:
cmd_result = res.stdout.read()
cmd_result_len = len(cmd_result)
print("一会要发送数据的真实长度:", cmd_result_len)
conn.send(struct.pack('i', cmd_result_len)) # 先发送struct打包后的数据长度
conn.sendall(cmd_result) # 再发送真实数据
conn.close()
server.close()
---------------------------------------------struct_client.py-------------------------------------------
# coding:utf-8
import socket
import struct
client = socket.socket()
ip_port = ("127.0.0.1", 8001)
client.connect(ip_port)
while 1:
cmd = input(">>>: ").strip()
if not cmd:
break
client.send(cmd.encode("utf-8")) # 发送dir或ipconfig命令
from_server_msg = client.recv(4) # 先接收struct打包的后数据长度
from_server_msg_len = struct.unpack('i', from_server_msg) # struct解包
from_server_msg_len = from_server_msg_len[0] # 获取真实数据的长度
print("来自服务端的消息:", from_server_msg, from_server_msg_len)
from_server_real_msg = client.recv(1024) # 再接收真实数据
real_msg_len = len(from_server_real_msg) # 获取第一次获取真实数据的长度
while from_server_msg_len > real_msg_len: # 如果一次没有获取完真实数据
from_server_real_msg += client.recv(1024) # 再次接收真实数据
real_msg_len = len(from_server_real_msg)
print("来自服务端的消息:", from_server_real_msg.decode("gbk"))
client.close()
总结:
1、使用struct模块先把要发送的数据打包成固定长度(4)的字节发送出去,再发送数据。
再看一组struct模块使用的实例
# tcp_server.py
import socket
import struct
sk = socket.socket() # 创建socket对象
sk.bind(("127.0.0.1", 9998)) # 绑定IP和端口
sk.listen() # 开启监听
conn, address = sk.accept() # 等待客户端连接 阻塞
while 1: # 让服务端和客户端循环通信
send_msg = input(">>>:").strip() # 要发送的消息
send_msg_bs = send_msg.encode("utf-8") # 消息编码为字节
conn.send(struct.pack("i", len(send_msg_bs))) # 先发送消息的长度(打包成固定4个字节长度发送出去)
conn.send(send_msg_bs) # 再发送消息给客户端
if send_msg.upper() == "BYE": # 如果发送的消息是BYE就退出循环
break
msg_len = struct.unpack("i", conn.recv(4))[0] # 先接收4个字节的消息长度
recv_msg = conn.recv(msg_len) # 再接收消息
print("来自客户端的消息:", recv_msg.decode("utf-8"))
if recv_msg.decode("utf-8").upper() == "BYE": # 如果来自客户端的消息是BYE就退出循环
break
conn.close() # 关闭conn连接
sk.close() # 关闭sk连接
# tcp_client.py
import socket
import struct
sk = socket.socket() # 创建socket对象
sk.connect(("127.0.0.1", 9998)) # 连接服务端
while 1: # 服务端和客户端循环通讯
msg_len = struct.unpack("i", sk.recv(4))[0] # 先接收消息长度
recv_msg = sk.recv(msg_len) # 再接收消息
print("来自服务端的消息:", recv_msg.decode("utf-8"))
if recv_msg.decode("utf-8").upper() == "BYE": # 如果来自服务端的消息是BYE就直接退出循环
break
send_msg = input(">>>:").strip() # 要发送给服务端的消息
send_msg_bs = send_msg.encode("utf-8")
sk.send(struct.pack("i", len(send_msg_bs))) # 先发送消息的长度
sk.send(send_msg_bs) # 再发送消息
if send_msg.upper() == "BYE": # 如果发送的消息是BYE就直接退出循环
break
sk.close() # 关闭sk连接
把发送消息和接收消息封装到函数中
# server_tcp.py
import socket
import struct
def my_send(sk, msg):
"""
发送消息
:param msg:
:return:
"""
msg_bs = msg.encode("utf-8")
struct_len = struct.pack("i", len(msg_bs))
sk.send(struct_len)
sk.send(msg_bs)
def my_recv(sk):
"""
接收来自消息
:param sk:
:return:
"""
msg_len = struct.unpack("i", sk.recv(4))[0]
msg_bs = sk.recv(msg_len)
return msg_bs.decode("utf-8")
if __name__ == '__main__':
sk = socket.socket() # 创建socket对象
sk.bind(("127.0.0.1", 6666)) # 绑定IP和端口号
sk.listen() # 开启监听
print("开启监听!")
conn, address = sk.accept() # 等待客户端连接 阻塞
print("客户端连接成功!")
while 1: # 开始和客户端聊天,以下程序是服务端先发送消息
send_msg = input(">>>:").strip()
my_send(conn, send_msg)
if send_msg.upper() == "BYE":
break
msg = my_recv(conn)
print(f"来自客户端的消息:{msg}")
if msg.upper() == "BYE":
break
conn.close()
sk.close()
# client_tcp.py
import socket
import struct
def my_send(sk, msg):
"""
发送消息
:param msg:
:return:
"""
msg_bs = msg.encode("utf-8")
struct_len = struct.pack("i", len(msg_bs))
sk.send(struct_len)
sk.send(msg_bs)
def my_recv(sk):
"""
接收来自消息
:param sk:
:return:
"""
msg_len = struct.unpack("i", sk.recv(4))[0]
msg_bs = sk.recv(msg_len)
return msg_bs.decode("utf-8")
if __name__ == '__main__':
sk = socket.socket() # 创建socket对象
sk.connect(("127.0.0.1", 6666)) # 连接服务端
while 1:
recv_msg = my_recv(sk)
print("来自服务端的消息:", recv_msg)
if recv_msg.upper() == "BYE":
break
send_msg = input(">>>:").strip()
my_send(sk, send_msg)
if send_msg.upper() == "BYE":
break
sk.close()
总结:
1、以上例子都是最简单的一发一收的过程,并且一次发送的数据量都非常的小。
如果遇到一次发送数据比较大的时候,应该怎么办呢?