学习总结(三十)
作者:互联网
一.半连接数:
三次握手没有完成 称之为半连接
原因1 恶意客户端没有返回第三次握手信息
原因2 服务器没空及时处理你的请求
socket中 listen(半连接最大数量)
二.粘包问题
TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了!
UDP 用户数据报协议
粘包 仅发生在TCP协议中
-
发送端 发送的数据量小 并且间隔短 会粘
-
接收端 一次性读取了两次数据的内容 会粘
-
接收端 没有接收完整 剩余的内容 和下次发送的粘在一起
无论是那种情况,其根本原因在于 接收端不知道数据到底有多少
解决方案就是 提前告知接收方 数据的长度
发送端
1.使用struct 将真实数据的长度转为固定的字节数据
2.发送长度数据
3.发送真实数据
接收端
1.先收长度数据 字节数固定
2.再收真实数据 真实可能很长 需要循环接收
发送端和接收端必须都处理粘包 才算真正的解决了
案例: 远程CMD程序
客户端
import socket from 二_CMD程序 import smallTool import struct client = socket.socket() try: client.connect(("127.0.0.1",1688)) print("链接成功!") while True: msg = input("请输入要执行指令:").strip() if msg == "q": break if not msg: continue # 发送指令 # 先发长度 len_bytes = struct.pack("q",len(msg.encode("utf-8"))) client.send(len_bytes) # 在发指令 client.send(msg.encode("utf-8")) data = smallTool.recv_data(client) print(data.decode("GBK")) client.close() except ConnectionRefusedError as e: print("链接服务器失败了!",e) except ConnectionResetError as e: print("服务器挂了!", e) client.close()
服务端
import socket import subprocess import struct from 二_CMD程序 import smallTool server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(("127.0.0.1",1688)) server.listen() # back while True: # socket,addr一个元组 客户端的ip和port client,addr = server.accept() print("客户端链接成功!") # 循环收发数据 while True: try: cmd = smallTool.recv_data(client) if not cmd: break print(cmd) p = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 不要先读err错误信息 它会卡主 原因不详 linux不会有问题 tasklist netstat - ano啥的 data = p.stdout.read() err_data = p.stderr.read() len_size = len(data) + len(err_data) print("服务器返回了: %s " % len_size) len_bytes = struct.pack("q",len_size) # 在发送真实数据前先发送 长度 client.send(len_bytes) # 返回的结果刚好就是二进制 # 发送真实数据 client.send(data + err_data) except ConnectionResetError as e: print("客户端了挂了!",e) break client.close()
自定义报头
当需要在传输数据时 传呼一些额外参数时就需要自定义报头
报头本质是一个json 数据
具体过程如下:
发送端
1 发送报头长度
2 发送报头数据 其中包含了文件长度 和其他任意的额外信息
3 发送文件内容
接收端
1.接收报头长度
2.接收报头信息
3.接收文件内容
服务端
import socket import os import struct import json """ 客户端接链成功我就给你发个文件过去 固定的文件下载 """ server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(("127.0.0.1",1688)) server.listen() # back while True: # socket,addr一个元组 客户端的ip和port client,addr = server.accept() print("客户端链接成功!") f = None try: path = r"F:\2.半链接数.mp4" file_size = os.path.getsize(path) # 我想把文件名发过去 file_info = {"file_name":"半链接数.mp4","file_size":file_size,"md5":"xxxxxxxxx"} json_str = json.dumps(file_info).encode("utf-8") # 发送报头长度 client.send(struct.pack("q",len(json_str))) # 发报头 client.send(json_str) # 发文件了 # 发送文件数据 f = open(path,"rb") # 循环发送文件内容 每次发2048 while True: temp = f.read(2048) if not temp: break client.send(temp) print("文件发送完毕!") except Exception as e: print("出问题了",e) finally: if f:f.close() client.close() # 无论是否抛出异常 文件都要关闭 #server.close()
客户端
""" 客户端输入指令 服务器接收指令并执行 最后返回执行结果 """ import socket import struct import json client = socket.socket() try: client.connect(("127.0.0.1",1688)) print("链接成功!") # 1.先收报头长度 head_size = struct.unpack("q",client.recv(8))[0] # 2.收报头数据 head_str = client.recv(head_size).decode("utf-8") file_info = json.loads(head_str) print("报头数据:",file_info) file_size = file_info.get("file_size") file_name = file_info.get("file_name") # 3.再收文件内容 # 已接收大小 recv_size = 0 buffer_size = 2048 f = open(file_name,"wb") while True: if file_size - recv_size >= buffer_size: temp = client.recv(buffer_size) else: temp = client.recv(file_size - recv_size) f.write(temp) recv_size += len(temp) print("已下载:%s%%" % (recv_size / file_size * 100)) if recv_size == file_size: break f.close() except ConnectionRefusedError as e: print("链接服务器失败了!",e)
标签:总结,socket,学习,print,client,file,三十,import,size 来源: https://www.cnblogs.com/xzcvblogs/p/10946451.html