---恢复内容开始---
一.缓冲区: 将程序和网络解耦
输入缓冲区
输出缓冲区
1 每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。 2 3 write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。 4 5 TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。 6 7 read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。 8 9 这些I/O缓冲区特性可整理如下: 10 11 1.I/O缓冲区在每个TCP套接字中单独存在; 12 2.I/O缓冲区在创建套接字时自动生成; 13 3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据; 14 4.关闭套接字将丢失输入缓冲区中的数据。 15 16 输入输出缓冲区的默认大小一般都是 8K,可以通过 getsockopt() 函数获取: 17 18 1.unsigned optVal; 19 2.int optLen = sizeof(int); 20 3.getsockopt(servSock, SOL_SOCKET, SO_SNDBUF,(char*)&optVal, &optLen); 21 4.printf("Buffer length: %d\n", optVal); 22 23 socket缓冲区解释
Import Subprocess
sub_obj = subprocess.Popen(
‘dir’,
shell=True,
stdout=subprocess.PIPE, #正确结果的存放位置
stderr=subprocess.PIPE #错误结果的存放位置
)
二.两种黏包现象:
1 连续的小包可能会被优化算法给组合到一起进行发送
2 第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱
方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程
1 import socket 2 import subprocess 3 server = socket.socket() 4 ip_port = ('127.0.0.1',8001) 5 6 server.bind(ip_port) 7 8 server.listen() 9 10 conn,addr = server.accept() 11 12 while 1: 13 from_client_cmd = conn.recv(1024) 14 15 print(from_client_cmd.decode('utf-8')) 16 #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令 17 sub_obj = subprocess.Popen( 18 from_client_cmd.decode('utf-8'), 19 shell=True, 20 stdout=subprocess.PIPE, #正确结果的存放位置 21 stderr=subprocess.PIPE #错误结果的存放位置 22 ) 23 #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果 24 std_msg = sub_obj.stdout.read() 25 26 #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据 27 std_msg_len = len(std_msg) 28 # std_bytes_len = bytes(str(len(std_msg)),encoding='utf-8') 29 #首先将数据长度的数据类型转换为bytes类型 30 std_bytes_len = str(len(std_msg)).encode('utf-8') 31 print('指令的执行结果长度>>>>',len(std_msg)) 32 conn.send(std_bytes_len) 33 34 status = conn.recv(1024) 35 if status.decode('utf-8') == 'ok': 36 37 conn.send(std_msg) 38 else: 39 pass
1 import socket 2 3 client = socket.socket() 4 client.connect(('127.0.0.1',8001)) 5 6 while 1: 7 cmd = input('请输入指令:') 8 9 client.send(cmd.encode('utf-8')) 10 11 server_res_len = client.recv(1024).decode('utf-8') 12 13 print('来自服务端的消息长度',server_res_len) 14 15 client.send(b'ok') 16 17 server_cmd_result = client.recv(int(server_res_len)) 18 19 20 print(server_cmd_result.decode('gbk'))
方案二:
Struct模块,
打包:struct.pack(‘i’,长度)
解包:struct.unpack(‘i’,字节)
1 import socket 2 import subprocess 3 import struct 4 server = socket.socket() 5 ip_port = ('127.0.0.1',8001) 6 7 server.bind(ip_port) 8 9 server.listen() 10 11 conn,addr = server.accept() 12 13 while 1: 14 from_client_cmd = conn.recv(1024) 15 16 print(from_client_cmd.decode('utf-8')) 17 #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令 18 sub_obj = subprocess.Popen( 19 from_client_cmd.decode('utf-8'), 20 shell=True, 21 stdout=subprocess.PIPE, #正确结果的存放位置 22 stderr=subprocess.PIPE #错误结果的存放位置 23 ) 24 #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果 25 std_msg = sub_obj.stdout.read() 26 27 #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据 28 std_msg_len = len(std_msg) 29 30 print('指令的执行结果长度>>>>',len(std_msg)) 31 32 msg_lenint_struct = struct.pack('i',std_msg_len) 33 34 35 conn.send(msg_lenint_struct+std_msg)
1 import socket 2 import struct 3 client = socket.socket() 4 client.connect(('127.0.0.1',8001)) 5 6 while 1: 7 cmd = input('请输入指令:') 8 #发送指令 9 client.send(cmd.encode('utf-8')) 10 11 #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度 12 server_res_len = client.recv(4) 13 msg_len = struct.unpack('i',server_res_len)[0] 14 15 print('来自服务端的消息长度',msg_len) 16 #通过解包出来的长度,来接收后面的真实数据 17 server_cmd_result = client.recv(msg_len) 18 19 print(server_cmd_result.decode('gbk'))
三.打印进度条
1 #总共接收到的大小和总文件大小的比值: 2 #all_size_len表示当前总共接受的多长的数据,是累计的 3 #file_size表示文件的总大小 4 per_cent = round(all_size_len/file_size,2) #将比值做成两位数的小数 5 #通过\r来实现同一行打印,每次打印都回到行首打印 6 print('\r'+ '%s%%'%(str(int(per_cent*100))) + '*'*(int(per_cent*100)),end='') #由于float类型的数据没法通过%s来进行字符串格式化,所以我在这里通过int来转换了一下,并用str转换了一下,后面再拼接上*,这个*的数量根据现在计算出来的比值来确定,就能够出来%3***这样的效果。自行使用上面的sys.stdout来实现一下这个直接print的效果。