本地回环地址
127.0.0.1
我们先来写一个简单地服务器和客户端
服务端
import socket
server = socket.socket() # 就比如买了一个手机
server.bind(("127.0.0.1",8080)) # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen(5) # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept() # 接听电话等着别人给你打电话
date = conn.recv(1024) # 听别人说话,接收1023个字节数
print(date)
conn.send(b"hello") # 给别人回话
conn.close() # 挂断电话
server.close() # 关机
import socket
client = socket.socket() #拿电话
client.connect(("127.0.0.1",8080)) #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b"hello") # 对别人发消息
date = client.recv(1024) #接收别人说话,没次接收1024个字节
print(date)
client.close() # 挂电话
注意,在我们写服务器与客户端的时候
send与recv必须要一一对应
不能出现两边都相同的
recv是跟内存要数据,至于数据的来源你无需考虑
粘包
我们来看下面的的代码
服务端
import socket
server = socket.socket() # 就比如买了一个手机
server.bind(("127.0.0.1",8088)) # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen(5) # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept() # 接听电话等着别人给你打电话
date = conn.recv(1024) # 听别人说话,接收1023个字节数
print(date)
date = conn.recv(1024) # 听别人说话,接收1023个字节数
print(date)
conn.close() # 挂断电话
server.close() # 关机
客户端
import socket
client = socket.socket() #拿电话
client.connect(("127.0.0.1",8088)) #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b"hello") # 对别人发消息
client.send(b"hello") # 对别人发消息
client.close() # 挂电话
服务端打印结果
b'hellohello'
这是应为;
tcp协议会将时间间隔短的,和文件大小小的会一次打包发个对方
如果我们将代码改一下,将服务端收到的字节数1024改为我们知道的字节数,看看是否还会粘包
服务端
import socket
server = socket.socket() # 就比如买了一个手机
server.bind(("127.0.0.1",8088)) # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
server.listen(5) # 半连接池,最大等待连接数为5个,就比如开机
conn,address = server.accept() # 接听电话等着别人给你打电话
date = conn.recv(5) # 听别人说话,接收1023个字节数
print(date)
date = conn.recv(5) # 听别人说话,接收1023个字节数
print(date)
conn.close() # 挂断电话
server.close() # 关机
客户端
import socket
client = socket.socket() #拿电话
client.connect(("127.0.0.1",8088)) #绑定的是IP地址和端口号,也是一个元组 拨号
client.send(b"hello") # 对别人发消息
client.send(b"hello") # 对别人发消息
client.close() # 挂电话
打印结果
b'hello'
b'hello'
在我们知道了,我么传输的文件大小是多大的时候,规定给接收数据的recv就会避免粘包
解决粘包问题
struct模块
import struct
print("--------------------------1--------------------------")
msg = "asdasdasdasdasd"
print("原字符串的长度")
print(len(msg))
handler = struct.pack("i",len(msg))
print("创建报头的长度")
print(len(handler))
res = struct.unpack("i",handler)[0]
print("解报头过后的长度")
print(res)
print("--------------------------2--------------------------")
msg1 = "asdasdasdasdasdasdasdasd"
print("原字符串的长度")
print(len(msg1))
handler1 = struct.pack("i",len(msg1))
print("创建报头的长度")
print(len(handler1))
res1 = struct.unpack("i",handler1)[0]
print("解报头过后的长度")
print(res1)
"""
--------------------------1--------------------------
原字符串的长度
15
创建报头的长度
4
解报头过后的长度
15
--------------------------2--------------------------
原字符串的长度
24
创建报头的长度
4
解报头过后的长度
24
"""
经过观察这个模块会将自己的长度固定为4,8几个等级,我们一般选i就够用了
我们就可以将这个发过去,解报头,就可以知道原来的数据的字节大小
如果这个字节大小比我们接收的大
我们就然他一直接收,直达接收完为止
代码如下
服务端
import socket
import subprocess
import json
import struct
# 创建套接字
server = socket.socket()
# 绑定端口号,及IP地址
server.bind(("127.0.0.1", 8181))
# 进入半连接池状态
server.listen(5)
while True:
conn, address = server.accept() # 等待客户端连接
while True:
# 判断是否会出错
try:
date = conn.recv(1024).decode("utf-8") # 接收客户端发来的数据
if len(date) == 0: break # 判断客户端发来的数据是否为空,为空就退出循环,关闭这个通道
# 创建一个subprocess的对象
obj = subprocess.Popen(date, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# 将对象拿到的类容做个合并
res = obj.stdout.read() + obj.stderr.read()
# 创建一个字典,将拿到的数据的字符数,装入字典中
dic = {"file_size": len(res)}
# 将字典数据类型,转换为json的字符串数据类型
json_dic = json.dumps(dic)
# 将json字符串数据类型,创建为报头
handler = struct.pack("i", len(json_dic))
# 发送报头
conn.send(handler)
# 发送json字符串的字典
conn.send(json_dic.encode("utf-8"))
# 发送原始数据
conn.send(res)
# 捕捉异常
except ConnectionResetError:
break
# 关闭通道
conn.close()
客户端
import socket
import struct
import json
# 创键套接字
client = socket.socket()
# 绑定端口和IP地址
client.connect(("127.0.0.1", 8181))
while True:
# 用户输入发送内容,并装换成2进制
msg = input("cmd>>>").strip().encode("utf-8")
# 判断用户输入是否为空,为空跳过本次循环
if len(msg) == 0: continue
# 发送数据
client.send(msg)
# 接收报头
handler = client.recv(4)
# 解析报头,注意必须加索引,拿到json过后的字典的字节数
json_dic_size = struct.unpack("i", handler)[0]
# 接收json字典的字节数的json字典
json_dic = client.recv(json_dic_size)
# 将json数据的字典,反序列化
dic = json.loads(json_dic)
# 定义一个字节数为0 的变量
msg_size = 0
# 定义一个空的2进制字符
msg_date = b''
# 判断,如果定义的字节数小于我们字典中的字节数就读取文件,如果等于和大于就退出循环,打印文件类容
while msg_size < dic.get("file_size"):
date = client.recv(1024)
# 文件类容累加
msg_date += date
# 字节数累加
msg_size += len(date)
print(msg_date.decode("gbk"))