diff --git a/03.网络编程与并发/01.网络编程基础.md b/03.网络编程与并发/01.网络编程基础.md index d3e71b8..c89c7e2 100644 --- a/03.网络编程与并发/01.网络编程基础.md +++ b/03.网络编程与并发/01.网络编程基础.md @@ -553,6 +553,67 @@ phone.close() ## 粘包的解决方案 +### 添加结束字符 + +在每次发送一个信息结束的地方,添加一个标识,在从缓冲区获取数据时候,根据表示来判断消息是否已经获取结束 + +- 服务端 + +```python +import socket +import subprocess + +phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +phone.bind(('127.0.0.1', 8080)) + +phone.listen(5) + +while 1: # 循环连接客户端 + conn, client_addr = phone.accept() + print(client_addr) + + while 1: + try: + cmd = conn.recv(1024) + ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + correct_msg = ret.stdout.read() + error_msg = ret.stderr.read() + conn.send(correct_msg + error_msg + "結束".encode("gbk")) + except ConnectionResetError: + break + +conn.close() +phone.close() +``` + +- 客户端 + +```python +import socket + +phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话 + +phone.connect(('127.0.0.1',8080)) # 与客户端建立连接, 拨号 + + +while 1: + cmd = input('>>>') + phone.send(cmd.encode('utf-8')) + + from_server_data = b"" + while True: + from_server_data += phone.recv(1024) + if from_server_data.decode('gbk').count("結束") > 0: + break + + print(from_server_data.decode('gbk')) + +phone.close() +``` + +- 这样只是解决了一次消息没有收完整的情况,但是一次接受1024,可能会将紧跟的下一条消息的开头也一并接收了 + ### struct模块 该模块可以把一个类型,如数字,转成固定长度的bytes @@ -570,6 +631,8 @@ ret1 = struct.unpack('i',ret)[0] print(ret1, type(ret1), len(ret1)) # 但是通过struct 处理不能处理太大 +# 可处理的范围是 -2,147,483,648 ~ 2,147,483,647(即 -2³¹ ~ 2³¹-1)。 +# 如果超出范围,就会发生异常,struct.error ``` 方案一: @@ -643,7 +706,12 @@ while 1: res = b'' while recv_size < total_size: - recv_data = phone.recv(1024) + # 精准的获取数据的长度,防止将后面跟着的数据头部获取到 + if (total_size - recv_size) > 1024: + recv_buffer = 1024 + else: + recv_buffer = total_size - recv_size + recv_data = phone.recv(recv_buffer) res += recv_data recv_size += len(recv_data) @@ -757,13 +825,19 @@ while 1: res = b'' while recv_size < total_size: - recv_data = phone.recv(1024) + # 精准的获取数据的长度,防止将后面跟着的数据头部获取到 + if (total_size - recv_size) > 1024: + recv_buffer = 1024 + else: + recv_buffer = total_size - recv_size + recv_data = phone.recv(recv_buffer) res += recv_data recv_size += len(recv_data) print(res.decode('gbk')) phone.close() + ``` FTP上传下载文件的代码(简单版) @@ -775,7 +849,6 @@ import socket import struct import json sk = socket.socket() -## buffer = 4096 # 当双方的这个接收发送的大小比较大的时候,就像这个4096,就会丢数据,这个等我查一下再告诉大家,改小了就ok的,在linux上也是ok的。 buffer = 1024 #每次接收数据的大小 sk.bind(('127.0.0.1',8090)) sk.listen() @@ -794,9 +867,10 @@ with open(head['filename'],'wb') as f: f.write(content) filesize -= buffer else: - content = conn.recv(buffer) + content = conn.recv(filesize) f.write(content) break +print(head['filename'], "接收成功") conn.close() sk.close() @@ -813,15 +887,14 @@ sk = socket.socket() sk.connect(('127.0.0.1',8090)) buffer = 1024 #读取文件的时候,每次读取的大小 head = { - 'filepath':r'C:\Users\Aaron\Desktop\新建文件夹', #需要下载的文件路径,也就是文件所在的文件夹 - 'filename':'config', #改成上面filepath下的一个文件 + 'filepath':r'C:\Users\simid\Desktop', #需要下载的文件路径,也就是文件所在的文件夹 + 'filename':'KKRIEGER.EXE', #改成上面filepath下的一个文件 'filesize':None, } file_path = os.path.join(head['filepath'],head['filename']) filesize = os.path.getsize(file_path) head['filesize'] = filesize -# json_head = json.dumps(head,ensure_ascii=False) #字典转换成字符串 json_head = json.dumps(head) #字典转换成字符串 bytes_head = json_head.encode('utf-8') #字符串转换成bytes类型 print(json_head)