python网络编程:TCP通讯模板、粘包及解决方案、自定义报头
本文目录:
一、TCP通讯模板
TCP客户端
import socket c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 msg = input(">>>:") if not msg:continue c.send(msg.encode("utf-8")) print("send!") # 收数据 data = c.recv(1024).decode("utf-8") print("receiver!") print(data) # 关闭资源 c.close()
TCP服务器
import socket # 使用TCP 可以直接默认 server = socket.socket() # 指定端口 和 ip 端口 0 - 1023是系统保留的 server.bind(("127.0.0.1",65535)) # 监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击) server.listen(5) # 为了可以不断的接受客户端连接请求 while True: # 接受连接请求 c,addr = server.accept() # 为了可以重复收发数据 while True: try: # 1024 程序的最大缓冲区容量 返回值类型为bytes类型 data = c.recv(1024).decode("utf-8") # 如果客户端断开连接(客户端调用了close) recv 返回值为空 此时应该结束循环 if not data:# 在linux中 客户端异常关闭 服务器也会收空 print("client closed!") c.close() break #解码 print(data) # 回复数据 将原始数据转为大写 c.send(data.upper().encode("utf-8")) except ConnectionResetError: print("客户端异常关闭!!") c.close() break # 关闭资源 server.close() # TCP断开连接的正确姿势 # 客户端调用close # 服务器判断如果接收数据为空则相应的调用close
二、远程CMD程序
CMD客户端
import socket c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 msg = input(">>>:") if not msg:continue c.send(msg.encode("utf-8")) # while True: # # 收数据 data = c.recv(1024).decode("gbk") print(data) # 关闭资源 c.close() # 问题? 服务器发送的数据超过了接收端缓冲区大小 可直接修改大小来满足服务器传输的大小 但是不长远 # 上述问题 称之为粘包 # 思考: 循环每次读取一小部分 直到取完为止 # 什么时候可以结束循环 前提是让客户端直知道你的数据到底有多长 # 正确思路: """ 发送方 1.先告诉对方你要发的数据的长度 2.在发送真实数据 接收方 1.先接收数据的长度信息 2.根据长度信息循环获取直到以获取的长度等于总长度 """
CMD客户端2
import socket,time c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 c.send("dir".encode("utf-8")) time.sleep(1) c.send("dir".encode("utf-8")) data = c.recv(1024).decode("gbk") print(data) # 关闭资源 c.close() # 问题2 当客户端连续两行代码都发送一个dir时 服务器收到了一个dirdir # 两个命令黏在一起 # TCP协议内的一个nagle算法 如果数据量小 并且时间间隔短会将数据合并一个包
CMD服务器
# 1.服务器先启动 -> 客户端发送指令 -> 服务器接收后使用subprocess执行命令->将执行结果返回给客户端 import socket,subprocess # 使用TCP 可以直接默认 server = socket.socket() # 指定端口 和 ip 端口 0 - 1023是系统保留的 server.bind(("127.0.0.1",65535)) # 监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击) server.listen(5) # 为了可以不断的接受客户端连接请求 while True: # 接受连接请求 c,addr = server.accept() # 为了可以重复收发数据 while True: try: # 1024 程序的最大缓冲区容量 返回值类型为bytes类型 cmd = c.recv(1024).decode("utf-8") # 如果客户端断开连接(客户端调用了close) recv 返回值为kong 此时应该结束循环 if not cmd:# 在linux中 客户端异常关闭 服务器也会收空 print("client closed!") c.close() break #解码 print(cmd) # 执行命令 p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 将错误信息和正确信息拼接到一起 res = p.stdout.read() + p.stderr.read() print("执行结果长",len(res)) # 将执行结果发送给客户端 c.send(res) except ConnectionResetError: print("客户端异常关闭!!") c.close() break # 关闭资源 server.close() # TCP断开连接的正确姿势 # 客户端调用close # 服务器判断如果接收数据为空则相应的调用close
三、解决粘包问题
CMD客户端
import socket,struct c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 msg = input(">>>:") if not msg:continue c.send(msg.encode("utf-8")) # 1.先获取长度 bytes_len = c.recv(4) #对方是i格式 固定4字节 # 2.转回整型 total_len = struct.unpack("i",bytes_len)[0] # 已经接收的长度 recv_len = 0 # 一个表示最终数据的bytes finally_data = b'' # 3.收到的长度小于总长度就继续 while recv_len < total_len: # 循环收数据 data = c.recv(1024) recv_len += len(data) finally_data += data # 整体解码 print(finally_data.decode("gbk")) # 关闭资源 c.close() # 问题? 服务器发送的数据超过了接收端缓冲区大小 可直接修改大小来满足服务器传输的大小 但是不长远 # 上述问题 称之为粘包 # 思考: 循环每次读取一小部分 直到取完为止 # 什么时候可以结束循环 前提是让客户端直知道你的数据到底有多长 # 正确思路: """ 发送方 1.先告诉对方你要发的数据的长度 2.在发送真实数据 接收方 1.先接收数据的长度信息 2.根据长度信息循环获取直到以获取的长度等于总长度 自定义报头未讲 """
CMD服务器
# 1.服务器先启动 -> 客户端发送指令 -> 服务器接收后使用subprocess执行命令->将执行结果返回给客户端 import socket,subprocess,struct # 使用TCP 可以直接默认 server = socket.socket() # 指定端口 和 ip 端口 0 - 1023是系统保留的 server.bind(("127.0.0.1",65535)) # 监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击) server.listen(5) # 为了可以不断的接受客户端连接请求 while True: # 接受连接请求 c,addr = server.accept() # 为了可以重复收发数据 while True: try: # 1024 程序的最大缓冲区容量 返回值类型为bytes类型 cmd = c.recv(1024).decode("utf-8") # 如果客户端断开连接(客户端调用了close) recv 返回值为kong 此时应该结束循环 if not cmd:# 在linux中 客户端异常关闭 服务器也会收空 print("client closed!") c.close() break #解码 print(cmd) # 执行命令 p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 将错误信息和正确信息拼接到一起 res = p.stdout.read() + p.stderr.read() print("执行结果长",len(res)) # 1.先发送数据的长度 data_len = len(res) # 长度是一个整型 需要转为字节 1000 b'\x001' 2000 b'\x001\x002' # 另外 需要保证 长度信息转换后的结果长度是固定的 否则客户端也会粘包(不知道取多少字节) # struct 模块负责将python中的数据类型 转为c语言中结构体 # 整型转字节 bytes_len = struct.pack("i",data_len) c.send(bytes_len) # 2.发送真实数据 c.send(res) except ConnectionResetError: print("客户端异常关闭!!") c.close() break # 关闭资源 server.close() # TCP断开连接的正确姿势 # 客户端调用close # 服务器判断如果接收数据为空则相应的调用close
#structTest.py文件
import struct # 整型转字节 i 表示int 长度为4字节 q表示long int 长度为8字节 print(len(struct.pack("q",10240000000))) # 字节转整型 得到一个元祖 print(struct.unpack("q",struct.pack("q",10240000000))[0])
四、解决粘包问题2
CMD客户端
import socket,struct,json c = socket.socket() # 连接服务器 c.connect(("127.0.0.1",65535)) while True: # 发送数据 msg = input(">>>:") if not msg:continue c.send(msg.encode("utf-8")) # 1.先获取报头长度 bytes_len = c.recv(4) #对方是i格式 固定4字节 # 2.转回整型 head_len = struct.unpack("i",bytes_len)[0] # 3.接受报头数据 head_bytes = c.recv(head_len) # 4.转为json字符串 并转为字典 head_dic = json.loads(head_bytes.decode("utf-8")) print(head_dic) # 已经接收的长度 recv_len = 0 # 一个表示最终数据的bytes finally_data = b'' # 3.收到的长度小于总长度就继续 while recv_len < head_dic["total_size"]: # 循环收数据 data = c.recv(1024) recv_len += len(data) finally_data += data # 整体解码 print(finally_data.decode("gbk")) # 关闭资源 c.close() # 问题? 服务器发送的数据超过了接收端缓冲区大小 可直接修改大小来满足服务器传输的大小 但是不长远 # 上述问题 称之为粘包 # 思考: 循环每次读取一小部分 直到取完为止 # 什么时候可以结束循环 前提是让客户端直知道你的数据到底有多长 # 正确思路: """ 发送方 1.先告诉对方你要发的数据的长度 2.在发送真实数据 接收方 1.先接收数据的长度信息 2.根据长度信息循环获取直到以获取的长度等于总长度 自定义报头未讲 """
CMD服务器
# 1.服务器先启动 -> 客户端发送指令 -> 服务器接收后使用subprocess执行命令->将执行结果返回给客户端 import socket,subprocess,struct,json # 使用TCP 可以直接默认 server = socket.socket() # 指定端口 和 ip 端口 0 - 1023是系统保留的 server.bind(("127.0.0.1",65535)) # 监听请求 参数为最大半连接数(三次握手未完成的请求 可能是服务器来不及 客户端恶意攻击) server.listen(5) # 为了可以不断的接受客户端连接请求 while True: # 接受连接请求 c,addr = server.accept() # 为了可以重复收发数据 while True: try: # 1024 程序的最大缓冲区容量 返回值类型为bytes类型 cmd = c.recv(1024).decode("utf-8") # 如果客户端断开连接(客户端调用了close) recv 返回值为kong 此时应该结束循环 if not cmd:# 在linux中 客户端异常关闭 服务器也会收空 print("client closed!") c.close() break #解码 print(cmd) # 执行命令 p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 将错误信息和正确信息拼接到一起 res = p.stdout.read() + p.stderr.read() print("执行结果长",len(res)) # 1.组装一个报头信息 head_dic = { "name":"仓老师视频教学 如何做炸鸡!", "md5":"asasasasaas", "total_size":len(res), "type":"video" } # 2.转json字符串 head_str = json.dumps(head_dic) # 3.转字节 head_bytes = head_str.encode("utf-8") # 4.发送报头长度 bytes_len = struct.pack("i",len(head_bytes)) c.send(bytes_len) # 5.发送报头 c.send(head_bytes) # 6.发送真实数据 c.send(res) except ConnectionResetError: print("客户端异常关闭!!") c.close() break # 关闭资源 server.close() # TCP断开连接的正确姿势 # 客户端调用close # 服务器判断如果接收数据为空则相应的调用close
# structTest.py文件
import struct # 整型转字节 i 表示int 长度为4字节 q表示long int 长度为8字节 print(len(struct.pack("q",10240000000))) # 字节转整型 得到一个元祖 print(struct.unpack("q",struct.pack("q",10240000000))[0])