本地回环地址
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"))