博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python31 Socket2
阅读量:5991 次
发布时间:2019-06-20

本文共 24599 字,大约阅读时间需要 81 分钟。

socket接收大数据

#server端import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:    #接收数据为0,则断开            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))  # 在发送数据前查看数据是否为空        conn.send(cmd_res.encode('utf-8'))        print ("send done")server.close()#client端import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res = client.recv(1024)    print (cmd_res)client.close()先执行server,在执行clientclient执行结果:>>:pwdserver执行结果:new conn: ('127.0.0.1', 57790)等待新指令执行指令: b'pwd''pwd' �����ڲ����ⲿ���Ҳ���ǿ����еij������������ļ���before send 0send done等待新指令#可以看到server端发送的数据长度为0(pwd用来显示当前路径)#那么此时就卡到client的cmd_res = client.recv(1024)这里,因为client端收不到数据,当前就会这么一直卡着。
#serverimport socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:                print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))          if len(cmd_res) == 0:   #避免数据为0,,修改cmd_res进行提示            cmd_res = 'cmd has no output......'        conn.send(cmd_res.encode('utf-8'))        print ("send done")server.close()#clientimport socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res = client.recv(1024)    print (cmd_res)client.close()client执行结果:>>:pwdb'cmd has no output......'>>:
#server ()import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:    #接收数据为0,则断开            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))  # 在发送数据前查看数据是否为空        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(cmd_res.encode('gbk'))        #client发送ipconfig命令来从Windows获取信息,但当前是在Windows环境,所以使用gbk才能将数据正常的封装过去        print ("send done")server.close()#clientimport socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res = client.recv(1024)    print (cmd_res.decode('gbk'))     #client将发送ipconfig命令给server端,server返回gbk格式的内容给clientclient.close()client执行结果:>>:ipconfigWindows IP 配置无线局域网适配器 WLAN:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 1:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::141a:83:bbf0:3c7%2   IPv4 地址 . . . . . . . . . . . . : 10.213.36.2   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 10.213.36.250以太网适配器 VMware Network Adapter VMnet1:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::e08f:e633:7d98:5a39%20   IPv4 地址 . . . . . . . . . . . . : 192.168.52.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::318f:38e3:25d9:c249%11   IPv4 地址 . . . . . .>>:

image_1c587s9agfrj1dja1dbeerjbqk9.png-16.8kB

上图中在cmd里也使用ipconfig命令,但是可以看到的是显示的内容比通过python显示的内容要多,这是因为我们设置接收长度为1024,所以client一次最多只能接收1024,但实际内容数据超过了1024。
剩余的内容当前已经被缓存到缓冲区了(buffer)。

server端:import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(str(len(cmd_res)).encode('utf-8'))        #发送数据长度给client,这样client就可以判断(根据1024)需要接收多少次数据。        #但是这里要注意的是,整数是不可以被encode,只有字符串才可以被encode,所以这里要使用str将其改为字符串格式        conn.send(cmd_res.encode('gbk'))  #发送实际数据给client,注意实际数据是从Windows获取的,所以数据格式是gbk        print ("send done")server.close()client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)    #接收命令结果的长度    # cmd_res = client.recv(1024)    print ('接收结果大小:',cmd_res_size.decode())    #打印接收数据的总大小    received_size = 0    while received_size != int(cmd_res_size.decode()):        #接收来的数据大小不等于数据的总大小,说明数据还没接收完,就会一直循环接收数据;        #由于cmd_res_size发送过来的是bytes类型,所以需要先decode,在int将字符串改为整数。        data = client.recv(1024)        received_size += len(data) #每次接收数据可能小于1024,所以需要len判断        print (data.decode('gbk'))  #发送过来的Windows数据是gbk    else:        print ("cmd res receive done....",received_size)client.close()client执行结果:>>:ipconfig接收结果大小: 1185Windows IP 配置无线局域网适配器 WLAN:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 1:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::141a:83:bbf0:3c7%2   IPv4 地址 . . . . . . . . . . . . : 10.213.36.2   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 10.213.36.250以太网适配器 VMware Network Adapter VMnet1:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::e08f:e633:7d98:5a39%20   IPv4 地址 . . . . . . . . . . . . : 192.168.52.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::318f:38e3:25d9:c249%11   IPv4 地址 . . . . . . . . . . . . : 192.168.142.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 蓝牙网络连接:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接* 12:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : server执行结果:new conn: ('127.0.0.1', 60035)等待新指令执行指令: b'ipconfig'before send 1185send done等待新指令

image_1c5a2hjc51ao41rdo1qk91h5hs829.png-5.7kB

当前client卡在上图的位置就不动了
print ("cmd res receive done....",received_size)这行代码没有被打印出来(说明卡住了)

server端:import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(str(len(cmd_res)).encode('utf-8'))        conn.send(cmd_res.encode('gbk'))          print ("send done")server.close()client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)        # cmd_res = client.recv(1024)    print ('接收结果总大小:',cmd_res_size.decode())        received_size = 0    while received_size != int(cmd_res_size.decode()):        data = client.recv(1024)        received_size += len(data)         # print (data.decode('gbk'))          print ('数据实际大小:',received_size)   #先不打印数据,来打印大小看看    else:        print ("cmd res receive done....",received_size)client.close()client执行结果:接收结果总大小: 1185数据实际大小: 1024数据实际大小: 1390#可以看到server端发送数据的总大小和实际接收数据的总大小不一致,然后根据while循环两个数据不相等时client就会一直等待接收数据,所以导致当前程序卡在client等待接收数据这里。
修改client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)    # cmd_res = client.recv(1024)    print ('接收结果总大小:',cmd_res_size.decode())    received_size = 0    while received_size < int(cmd_res_size.decode()):        #这里讲!=修改为<(只要小于就接收)        data = client.recv(1024)        received_size += len(data)        # print (data.decode('gbk'))  #发送过来的Windows数据是gbk        print ('数据实际大小:',received_size)    else:        print ("cmd res receive done....",received_size)client.close()client执行结果:>>:ipconfig接收结果总大小: 1185数据实际大小: 1024 #循环一次打印一次(第一次打印)数据实际大小: 1390 #循环一次打印一次(第二次打印)cmd res receive done.... 1390#当设置只要received_size小于cmd_res_size时,就会接收数据,所以client这里现在就不会卡在接收数据这里了。
修改client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)    # cmd_res = client.recv(1024)    print ('接收结果总大小:',cmd_res_size.decode())    received_size = 0    received_data = b''  #设置一个空的数据内容变量    while received_size < int(cmd_res_size.decode()):        data = client.recv(1024)        received_size += len(data)        received_data += data #将每次接收过来的数据内容相加    else:        print ("cmd res receive done....",received_size)        print (received_data.decode('gbk')) #在这里打印接收到的数据client.close()client执行结果:>>:ipconfig接收结果总大小: 1185cmd res receive done.... 1390#这里长度不一致的原因是中文导致的,因为在server端的conn.send(str(len(cmd_res)).encode('utf-8'))这行代码中(len(cmd_res)这里是先进行len的操作后才去.encode;  然后发送给client的数据长度是.encode后的数据长度;  .encode之前判断的是字符的长度,但.encode之后计算的长度就不一样了。Windows IP 配置无线局域网适配器 WLAN:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 1:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::141a:83:bbf0:3c7%2   IPv4 地址 . . . . . . . . . . . . : 10.213.36.2   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 10.213.36.250以太网适配器 VMware Network Adapter VMnet1:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::e08f:e633:7d98:5a39%20   IPv4 地址 . . . . . . . . . . . . : 192.168.52.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::318f:38e3:25d9:c249%11   IPv4 地址 . . . . . . . . . . . . : 192.168.142.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 蓝牙网络连接:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接* 12:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . :
修改server端:import socket,osserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(str(len(cmd_res.encode())).encode('utf-8'))        #这里我们在cmd_res后面加了个.encode,封装后就将中文长度也计算了,然后在通过.encode('utf-8')封装成bytes数据类型传输。                #第一个encode是为了能够正确len计算大小,第二次encode是为了将字符串编程bytes类型(字符串不能被send)        conn.send(cmd_res.encode('utf-8'))          #这里可以看到封装使用了utf-8,这是因为client端使用了received_data = b'',client端将数据改为bytes类型后,client就可以通过received_data.decode()正常解码了。        print ("send done")server.close()修改client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)    # cmd_res = client.recv(1024)    print ('接收结果总大小:',cmd_res_size.decode())    received_size = 0    received_data = b''  #设置一个空的数据内容变量    while received_size < int(cmd_res_size.decode()):        data = client.recv(1024)        received_size += len(data)        received_data += data    else:        print ("cmd res receive done....",received_size)        print (received_data.decode())         #因为received_data = b'',所以解码的时候就不要在decode为gbk了。client.close()client执行结果:>>:ipconfig接收结果总大小: 1595cmd res receive done.... 1595#可以看到这会数据长度就一致了。Windows IP 配置无线局域网适配器 WLAN:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 本地连接* 1:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 以太网:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::141a:83:bbf0:3c7%2   IPv4 地址 . . . . . . . . . . . . : 10.213.36.2   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 10.213.36.250以太网适配器 VMware Network Adapter VMnet1:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::e08f:e633:7d98:5a39%20   IPv4 地址 . . . . . . . . . . . . : 192.168.52.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 VMware Network Adapter VMnet8:   连接特定的 DNS 后缀 . . . . . . . :    本地链接 IPv6 地址. . . . . . . . : fe80::318f:38e3:25d9:c249%11   IPv4 地址 . . . . . . . . . . . . : 192.168.142.1   子网掩码  . . . . . . . . . . . . : 255.255.255.0   默认网关. . . . . . . . . . . . . : 以太网适配器 蓝牙网络连接:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . : 隧道适配器 本地连接* 12:   媒体状态  . . . . . . . . . . . . : 媒体已断开连接   连接特定的 DNS 后缀 . . . . . . . :

粘包

image_1c5anku2o19f01qa8o7v1rhd1jj13.png-141.7kB

使用同样的代码在linux中运行,结果就报错了; 图中可以看到在打印长度后面紧跟着数据内容。

server端:conn.send(str(len(cmd_res.encode())).encode('utf-8'))conn.send(cmd_res.encode('utf-8'))  #之所以在linux报错,是因为上面两行代码粘在一起发送了(数据长度和数据内容一起发送了),这种情况俗称socket粘包。#造成粘包的原因是因为这两行代码上下邻着的,缓冲区会将这两行代码当做一条代码一起给发送出去。
修改server端:import socket,os,timeserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(str(len(cmd_res.encode())).encode('utf-8'))        time.sleep(0.01)    #将两个代码之间用其他代码隔离开就解决粘包的问题了        conn.send(cmd_res.encode('utf-8'))          print ("send done")server.close()client执行结果:>>:ifconfig接收结果总大小: 969cmd res receive done.... 969ens32     Link encap:以太网  硬件地址 00:0c:29:c2:d0:8c            inet 地址:192.168.142.128  广播:192.168.142.255  掩码:255.255.255.0          inet6 地址: fe80::a734:95ce:c197:a382/64 Scope:Link          UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1          接收数据包:495949 错误:0 丢弃:0 过载:0 帧数:0          发送数据包:210917 错误:0 丢弃:0 过载:0 载波:0          碰撞:0 发送队列长度:1000           接收字节:560538040 (560.5 MB)  发送字节:13121792 (13.1 MB)lo        Link encap:本地环回            inet 地址:127.0.0.1  掩码:255.0.0.0          inet6 地址: ::1/128 Scope:Host          UP LOOPBACK RUNNING  MTU:65536  跃点数:1          接收数据包:1619 错误:0 丢弃:0 过载:0 帧数:0          发送数据包:1619 错误:0 丢弃:0 过载:0 载波:0          碰撞:0 发送队列长度:1           接收字节:130666 (130.6 KB)  发送字节:130666 (130.6 KB)>>:#从执行结果中可以看到没有在出现粘包的问题。#不过不建议使用sleep,假如在某些时时要求比较高的环境下,这样会影响程序的运行;而且使用sleep的话,如果时间过短还是可能会出现粘包的现象。
修改server端:import socket,os,timeserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        print ('执行指令:',data)        cmd_res = os.popen(data.decode()).read()        print("before send", len(cmd_res))        if len(cmd_res) == 0:            cmd_res = 'cmd has no output......'        conn.send(str(len(cmd_res.encode())).encode('utf-8'))        client_ack = conn.recv(1024)  #等待客户端发送确认信息        #在这里我们多加一个server与client的交互,当server端send后,client端接收到数据后再发送一个确认信息给server端。        print (client_ack.decode())        conn.send(cmd_res.encode('utf-8'))          print ("send done")server.close()修改client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    client.send(cmd.encode('utf-8'))    cmd_res_size = client.recv(1024)    print ('接收结果总大小:',cmd_res_size.decode())    client.send("Client ACK".encode('utf-8')) #这里定义发送确认信息    received_size = 0    received_data = b''      while received_size < int(cmd_res_size.decode()):        data = client.recv(1024)        received_size += len(data)        received_data += data    else:        print ("cmd res receive done....",received_size)        print (received_data.decode()) client.close()

socket发送文件

ftp server

1.读取文件名
2.检测文件是否存在
3.打开文件
4.检测文件大小
5.发送文件大小和md5给客户端
6.等客户端确认
7.开始边读边发数据
8.MD5

server端:import socket,os,hashlibserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        cmd,filename = data.decode().split()    #将客户端发送的指令get 和 文件名分开        #   假如客户端输入的是 get test.txt,那么当前filename就等于test.txt        print (filename)        if os.path.isfile(filename):    #判断test.txt如果是一个文件            f = open(filename,"rb")     #通过rb打开,接下来就不用在给encode成byte格式了            file_size = os.stat(filename).st_size #获取 文件大小            conn.send(str(file_size).encode('utf-8'))  #发送文件大小(需要先转成字符串,才能encode)            conn.recv(1024)     #等待收取确认包(ACK)            m = hashlib.md5()            for line in f:                conn.send(line)  #一行一行的将数据发送出去                m.update(line)                # 将一行一行的数据进行MD5                # update的数据必须是byte格式才能进行MD5,不过当前f本身就是通过rb来读取的,所以当前就是byte格式            print ("file md5",m.hexdigest())    # 打印MD5值            # 当前m已经对每一行数据进行MD5了,所以当前发送的是最终的MD5值            # 通过hexdigest就是通过的字符串格式来显示MD5值            f.close()        print("send done")server.close()client端:import socketclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    if cmd.startswith("get"):        client.send(cmd.encode('utf-8'))        server_response = client.recv(1024) #数据大小        print ("server response:",server_response)         client.send(b"ready to recv file")  #回复确认包,防止粘包        file_total_size = int(server_response.decode()) #先decode,再int        received_size = 0        filename = cmd.split()[1]           #将get和文件名分开,然后将文件名赋值给filename        f = open(filename + ".new","wb")    #将读取过来的数据,存储到一个新文件中(用于区别server和client的文件)        while received_size < file_total_size:            data = client.recv(1024)    #一行一行数据接收            received_size += len(data)            f.write(data)   #一行一行数据写入            print (file_total_size,received_size)        else:            print ("file recv done")            f.close()client.close()cient执行结果:>>:get test.txtserver response: b'44688'44688 102444688 204844688 307244688 409644688 512044688 614444688 716844688 819244688 921644688 1024044688 1126444688 1228844688 1331244688 1433644688 1536044688 1638444688 1740844688 1843244688 1945644688 2048044688 2150444688 2252844688 2355244688 2457644688 2560044688 2662444688 2764844688 2867244688 2969644688 3072044688 3174444688 3276844688 3379244688 3481644688 3584044688 3686444688 3788844688 3891244688 3993644688 4096044688 4198444688 4300844688 4403244688 44688file recv done>>:server执行结果:new conn: ('127.0.0.1', 64667)等待新指令test.txtfile md5 926394ca9dc106ff006da5027f45ebd3send done等待新指令

image_1c5mt1l4hor91534dig19d1g0vm.png-6.5kB

可以看到源文件与新文件大小相同

下面让客户端也计算MD5

server端:import socket,os,hashlibserver = socket.socket()server.bind(('localhost',9999))server.listen()while True:    conn,addr = server.accept()    print ('new conn:',addr)    while True:        print ("等待新指令")        data = conn.recv(1024)        if not data:            print ('客户端已断开!')            break        cmd,filename = data.decode().split()        print (filename)        if os.path.isfile(filename):            f = open(filename,"rb")            file_size = os.stat(filename).st_size            conn.send(str(file_size).encode('utf-8'))            conn.recv(1024)            m = hashlib.md5()            for line in f:                conn.send(line) #这里和下面send MD5容易粘包                m.update(line)            md5_value = m.hexdigest()            #print ("file md5",md5_value)            f.close()            conn.send(md5_value.encode('utf-8'))   #发送MD5            #这里和上面send(line)容易粘包(因为临近)            print ('已发送MD5,请验证!')        print("send done")server.close()client端:import socket,hashlibclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    if cmd.startswith("get"):        client.send(cmd.encode('utf-8'))        server_response = client.recv(1024)        print ("server response:",server_response)        client.send(b"ready to recv file")        file_total_size = int(server_response.decode())        received_size = 0        filename = cmd.split()[1]        f = open(filename + ".new","wb")        m = hashlib.md5()        while received_size < file_total_size:            data = client.recv(1024)            received_size += len(data)            m.update(data)            f.write(data)            # print (file_total_size,received_size)        else:            print ("file recv done",received_size,file_total_size)            f.close()            new_file_md5 = m.hexdigest()        server_file_md5 = client.recv(1024)        print ("server file md5:",server_file_md5.decode())        print ("client file md5:",new_file_md5)client.close()

client执行结果:

image_1c5nf61oh1uno6i216b7of81sih13.png-5.8kB
注意:received_size和file_total_size值不一致
可以看到当前client端已经卡住了,卡在server_file_md5 = client.recv(1024)这里,这是因为server端粘包了,将MD5信息提前发送给了client;
这里可能认为通过client端的while received_size < file_total_size:这个条件,received_size小于file_total_size时就不会在接收了,也的确是这样,但是要注意的是,在client最后一次循环接收数据时,received_size也是小于file_total_size的,可是server是将粘包的数据一起发送过来的,刚好就大于了file_total_size。

比如:client正常接收的数据是5000,server端此时已经发送了480还剩200正常数据没发,此时client最后一次循环接收数据,可是server端因为粘包的问题将最后正常的200数据+300的MD5数据一起发给了client,此时client接收的数据总数达到了5300,超了300。

最后因为MD5是通过粘包的情况已经发送给client并写入新的文档中了,所以client端就一直卡在server_file_md5 = client.recv(1024)接收数据这里。

image_1c5nfjib6qjjm821q141tj11qfo1g.png-7.1kB

上图中可以看到因为粘包的问题,client已经将MD5这个值写入到新文件中了,所以后面client不会再接收到MD5数据了。

修改client端:import socket,hashlibclient = socket.socket()client.connect(('localhost',9999))while True:    cmd = input(">>:").strip()    if len(cmd) == 0:continue    if cmd.startswith("get"):        client.send(cmd.encode('utf-8'))        server_response = client.recv(1024)        print ("server response:",server_response)        client.send(b"ready to recv file")        file_total_size = int(server_response.decode())        received_size = 0        filename = cmd.split()[1]        f = open(filename + ".new","wb")        m = hashlib.md5()        while received_size < file_total_size:            if file_total_size - received_size > 1024: #符合此条件说明还要接收不止一次数据                size = 1024            else:                size = file_total_size - received_size  #符合此条件表示正常数据还剩余多少            data = client.recv(size)    #这里就通过size来判断还要接收多少正常的数据            received_size += len(data)            m.update(data)            f.write(data)            # print (file_total_size,received_size)        else:            print ("file recv done",received_size,file_total_size)            f.close()            new_file_md5 = m.hexdigest()        server_file_md5 = client.recv(1024)        print ("server file md5:",server_file_md5.decode())        print ("client file md5:",new_file_md5)client.close()执行结果:>>:get test.txtserver response: b'44688'file recv done 44688 44688server file md5: 926394ca9dc106ff006da5027f45ebd3client file md5: 926394ca9dc106ff006da5027f45ebd3>>:#可以看到这回就没有出现粘包的情况,client端正常接收数据。

当前存在一个问题就是MD5拖慢了整个进程的速度,只要使用MD5的话就会耗费一定的时间,而且在这里我们是每读取一行数据,就进行一次MD5的计算。

有时会出现address is in use的报错情况,可以在server上使用下面代码解决

server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
不使用的话,就需要等待1分钟左右的时间才会恢复。

转载于:https://blog.51cto.com/daimalaobing/2087348

你可能感兴趣的文章
凌动智行被纽交所暂停交易、未来还将被除名,已启动退市程序
查看>>
RecyclerView 性能优化 | Android offer 收割机
查看>>
平方和获千万元级融资,投资方为明势资本和洪泰基金
查看>>
Android中的 targetsdkversioin
查看>>
使用pyspider爬取巨量淘宝MM图片
查看>>
分布式服务治理框架Dubbo
查看>>
ApiBoot DataSource Switch 使用文档
查看>>
12月27日云栖精选夜读 | Python拼接字符串的七种方式
查看>>
PostgreSQL 10.0 的三种日志
查看>>
浅析:SEO是什么意思?
查看>>
php runtime 中 headers already sent 问题解决方案
查看>>
[UWP]不那么好用的ContentDialog
查看>>
【许晓笛】EOS 数据库与持久化 API —— 实战
查看>>
使用C#创建快捷方式
查看>>
IIS中“绑定”,“IP地址全部未分配”到底是个什么玩意
查看>>
Jenkins ant build.xml文件详解(学习笔记六)
查看>>
Git 使用
查看>>
响应式滑块动画插件
查看>>
【JAVA秒会技术之秒杀面试官】JavaEE常见面试题(一)
查看>>
博客搬家通知
查看>>