运行的时候会发生粘包问题
import socket
import subprocess
HOST = '127.0.0.1' # 服务端绑定0.0.0.0的IP 放行所有的客户端请求
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 1024
ADDR = (HOST, PORT)
# STREAM 流式协议 TCP协议 DGRAM 数据报协议 UDP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR) # 将服务端的IP和地址绑定到套接字
server.listen(5) # 监听链接 定义半链接池大小
print('服务端启动,监听地址:%s :%s' % (ADDR))
while True: # 链接循环 不停的接受请求 一直提供服务
# 等待客户端请求
conn, addr = server.accept() # 接受客户端链接
while True: # 通信循环 不停的收发消息
# send 和recv都会发起系统调用
try:
data = conn.recv(BUFSIZE)
if not data:
break
res = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data = res.stdout.read()
stderr_data = res.stderr.read()
print(len(stderr_data) + len(stdout_data), addr)
conn.send(stdout_data)
conn.send(stderr_data)
except Exception:
# 针对Windows系统
break
conn.close() # (必选操作)关闭客户端套字节 回收资源的操作
import socket
HOST = '127.0.0.1' # 绑定服务器公网IP
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 8096
ADDR = (HOST, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
while True: # 开启通信循环 不停的收发消息
cmd = input('cmd>>>').strip()
if not cmd: # 不让客户端发送空消息
continue
client.send(cmd.encode('utf-8'))
res_data = client.recv(BUFSIZE)
if not res_data:
break
print(res_data.decode('gbk')) # Windows采用gbk Unix采用utf-8
client.close() # 关闭客户端 必要的选择
在运行时永远不会发生粘包
from socket import *
import subprocess
HOST = '127.0.0.1'
PORT = 8080
BUFSIZE = 1024
ADDR = (HOST, PORT)
server = socket(AF_INET, SOCK_DGRAM)
server.bind(ADDR)
while True:
cmd, addr = server.recvfrom(BUFSIZE)
if not cmd:
break
print('命令为>>>', cmd, 'from', addr)
res = subprocess.Popen(cmd.decode('utf-8'),
shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout_msg = res.stdout.read()
stderr_msg = res.stderr.read()
server.sendto(stdout_msg, addr)
server.sendto(stderr_msg, addr)
server.close()
from socket import *
HOST = '127.0.0.1'
PORT = 8080
BUFSIZE = 1024
ADDR = (HOST, PORT)
client = socket(AF_INET, SOCK_DGRAM)
while True:
cmd = input('>>>').strip()
if not cmd:
continue
client.sendto(cmd.encode('utf-8'), ADDR)
data, addr = client.recvfrom(BUFSIZE)
if not data:
break
print(data.decode('gbk')) # Windows
client.close()
由于接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
struct模块
可以把一个类型,如int类型,转成固定长度的bytes
>>>import struct
>>>x = struct.pack('i', 6666)
>>>x
>>>b'\n\x1a\x00\x00'
>>>len(x)
>>>4 # 长度为4字节
>>>struct.unpack('i', x)[0]
>>>6666
先发送返回内容的长度信息,再发送真实的返回信息给客户端
import socket
import subprocess
import struct
HOST = '127.0.0.1'
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 1024
ADDR = (HOST, PORT)
# STREAM 流式协议 TCP协议 DGRAM 数据报协议 UDP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR) # 将服务端的IP和地址绑定到套接字
server.listen(5) # 监听链接 定义半链接池大小
print('服务端启动,监听地址:%s :%s' % (ADDR))
while True: # 链接循环 不停的接受请求 一直提供服务
# 等待客户端请求
conn, addr = server.accept() # 接受客户端链接
while True: # 通信循环 不停的收发消息
# send 和recv都会发起系统调用
try:
data = conn.recv(BUFSIZE)
if not data:
break
res = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data = res.stdout.read()
stderr_data = res.stderr.read()
print(len(stderr_data) + len(stdout_data), addr)
total_size = len(stdout_data) + len(stderr_data)
# 先发头--->是对数据的描述信息
header = struct.pack('i', total_size)
conn.send(header)
# 再发真实的信息
conn.send(stdout_data)
conn.send(stderr_data)
except Exception:
# 针对Windows系统
break
conn.close() # (必选操作)关闭客户端套字节 回收资源的操作
import socket
import struct
HOST = '127.0.0.1'
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 8096
ADDR = (HOST, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
while True: # 开启通信循环 不停的收发消息
cmd = input('cmd>>>').strip()
if not cmd: # 不让客户端发送空消息
continue
client.send(cmd.encode('utf-8'))
# 服务端知道数据的总大小
header = client.recv(4) # 接收4字节的大小
total_size = struct.unpack('i', header)[0] # 取小元组的0索引就是数据的大小
recv_size = 0
while recv_size < total_size:
recv_data = client.recv(BUFSIZE)
recv_size += len(recv_data)
print(recv_data.decode('gbk'), end='')
else:
print()
client.close() # 关闭客户端 必要的选择
可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节。
发送时
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时
先接收报头长度,用struct模块的unpack方法取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容。
import socket
import subprocess
import struct
import json
HOST = '127.0.0.1'
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 1024
ADDR = (HOST, PORT)
# STREAM 流式协议 TCP协议 DGRAM 数据报协议 UDP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR) # 将服务端的IP和地址绑定到套接字
server.listen(5) # 监听链接 定义半链接池大小
print('服务端启动,监听地址:%s :%s' % (ADDR))
while True: # 链接循环 不停的接受请求 一直提供服务
# 等待客户端请求
conn, addr = server.accept() # 接受客户端链接
while True: # 通信循环 不停的收发消息
# send 和recv都会发起系统调用
try:
data = conn.recv(BUFSIZE)
if not data:
break
res = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout_data = res.stdout.read()
stderr_data = res.stderr.read()
total_size = len(stdout_data) + len(stderr_data)
print(len(stderr_data) + len(stdout_data), addr)
# 制作报头字典
header_dic = {
"total_size": total_size
}
json_header_str = json.dumps(header_dic)
json_header_str_bytes = json_header_str.encode('utf-8')
# 先发头--->是对数据的描述信息
# 发送头的长度信息
conn.send(struct.pack('i', len(json_header_str_bytes)))
# 发头信息
conn.send(json_header_str_bytes)
# 再发真实的信息
conn.send(stdout_data)
conn.send(stderr_data)
except Exception:
# 针对Windows系统
break
conn.close() # (必选操作)关闭客户端套字节 回收资源的操作
import socket
import struct
import json
HOST = '127.0.0.1'
PORT = 8080 # 0-65535 1024前被系统保留使用
BUFSIZE = 8096
ADDR = (HOST, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
while True: # 开启通信循环 不停的收发消息
cmd = input('cmd>>>').strip()
if not cmd: # 不让客户端发送空消息
continue
client.send(cmd.encode('utf-8'))
# 接收端
# 先接收4字节,提取头信息的长度
header_len = struct.unpack('i', client.recv(4))[0]
# 接收头信息并解析
json_header_str_bytes = client.recv(header_len)
json_header_str = json_header_str_bytes.decode('utf-8')
header_dic = json.loads(json_header_str)
print('报头信息:', header_dic)
total_size = header_dic['total_size']
recv_size = 0
while recv_size < total_size:
recv_data = client.recv(BUFSIZE)
recv_size += len(recv_data)
print(recv_data.decode('gbk'), end='')
else:
print()
client.close() # 关闭客户端 必要的选择