python2.0 s12 day7

开发的第二阶段 网络编程阶段
之所以叫网络编程,是因为,这里面就不是你在一台机器中玩了.多台机器,CS架构.即客户端和服务器端通过网络进行通信的编程了.
首先想实现网络的通信,你得先学网络通信的一个基础,即两台机器之间是怎么打通的.有的同学说机器有IP地址,能互相ping通就连上了.
7层网络协议,第三层:网络层,第四层,传输层(tcp/ip协议).tcp/ip协议保证了两台机器的通信的可靠的数据传输(tcp/ip协议通信的3次握手).什么叫可靠的. A给B发消息,A发了,A会知道发没发到.
UDP不是可靠的数据传输协议,当A给B发消息,A发送出去了,A不知道B收没收到.A也不管.
UDP依然在使用.因为它快.
tcp/ip传输协议在发送数据前进行建立链接的3次握手.
3次握手后,才进行真正发送数据.那么问题来了.是什么东西在负责发送数据,对方又是什么东西在进行数据接收呢.那就是socket.
socket可以简单直白的认为它是一个管道,具体你在管道里传输什么socket不关心,如:mysql,http,这些对于socket来说就是一辆车.

Socket 基础

socket 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求.
socket起源于Unix,而Unix/Linux 基本哲学之一就是"一切皆文件",对于文件用[打开][读写][关闭]模式来操作.
socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO打开关闭)
socket和file的区别:
file模块是针对某个指定文件进行[打开][读写][关闭]
socket 模块是针对 服务器端 和 客户端socket 进行 [打开][读写][关闭]

socket基础使用实例:
socket_server.py
  import socket
        ip_port = ('127.0.0.1',9999)      
        sk = socket.socket()        #创建一个socket的句柄,如同open()一个文件
        sk.bind(ip_port)            #使用bind()方法,向系统内核注册一个ip,端口 作为这个socket的ip,port的属性,如果此端口没被占用,则
返回含有ip和端口的句柄,如果被占用则报错退出程序 sk.listen(
5)               #这个socket句柄处于监听状态。 while True:                #写一个死循环,用来接收客户端对socket的链接请求 print('server waiting ......') conn,addr = sk.accept()       
       #程序执行到accept()时,阻塞在这里,当客户端使用connect()方法,发送连接请求,accept()方法把客户端的ip也就是addr作为参数,传入sk这个socket实例设置了客户端的ip端口属性,然后把返回实例的内存地址,赋值给新的变量conn。语法就是conn,addr = 实例名.accept()。 client_data
= conn.recv(1024)   #客户端和服务端的连接成功后,socket生成,紧接着在这里调用recv()方法,程序继续阻塞,直到客户端调用send方法,conn.recv(1024) 中的1024指的是一次最大接收1024个字节。 print(str(client_data,'utf8')) conn.send(bytes("不要回答,不要回答",'utf8')) #服务端发送即对socket套接字进行写操作。 conn.close()

socket_clinet.py

  import socket
  ip_port = ('127.0.0.1',9999)
  sk = socket.socket()
  sk.connect(ip_port)
  
  sk.sendall(bytes("请求占领地球",'utf8'))

  server_reply = sk.recv(1024)
  print(str(server_reply,'utf8'))
  sk.close()

 基本的socket用法,很简单就实现了。那么我们深入一些:

1.能不能让客户端和服务端进行通信呢?

2.这是一个客户端,连接这个服务端。当多个客户端连接这个服务端会怎样?(这个测试下来,这种基本的socket是不能多个客户端访问的。)

那么我们来尝试解决问题1.

socket_server02.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print('server waiting ......')

    conn,addr = sk.accept()

    client_data = conn.recv(1024)
    print(str(client_data,'utf8'))
    conn.send(bytes("不要回答,不要回答",'utf8'))
    while True:
        client_data = conn.recv(1024)
        print(str(client_data,'utf8'))

        server_response = input(">>>")
        conn.send(bytes(server_response,'utf8'))
    conn.close()

socket_clinet02.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

sk.sendall(bytes("请求占领地球",'utf8'))

server_reply = sk.recv(1024)
print(str(server_reply,'utf8'))
while True:
    user_input = input(">>>:").strip()
    sk.send(bytes(user_input,'utf8'))
    server_reply = sk.recv(1024)
    print(str(server_reply,'utf8'))
sk.close()

先执行socket_server02.py

在执行socket_client02.py,执行结果如下:

socket_client02.py
不要回答,不要回答     #接到服务器的第一个响应
>>>:dd          #客户端输入dd
你大爷            #哟,服务端骂人
>>>:龟孙            #骂回去
日你先人          #服务器又骂回来一句
>>>:

socket_server02.py
server waiting ......
请求占领地球   #接到的第一个clinet send
dd        #服务端收到dd
>>>你大爷     #服务端回复,d d看不懂,就说了句“你大爷”
龟孙        #乖乖,敢回嘴,我可是服务器
>>>日你先人    #接着骂

 那么我这上面都是正常通信,输入的字符串都是正常的,假如客户端不输入了,直接退出。会怎样?

这里就不赘述老师的试验过程了,最终结果,老师说当clinet客户端停掉,windows和Linux的服务端对这个异常处理的方式不一样

但我自己在mac和Linux下尝试的结果一样,判断接收到clinet_data数据是否为空,为空就跳出循环。

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print('server waiting ......')

    conn,addr = sk.accept()

    client_data = conn.recv(1024)
    print(str(client_data,'utf8'))
    conn.send(bytes("不要回答,不要回答",'utf8'))
    while True:

            client_data = conn.recv(1024)
            if not client_data:break   #判断当,对方传过来的值为空时,直接退出
            print('client_data',str(client_data,'utf8'))

            server_response = input(">>>")
            conn.send(bytes(server_response,'utf8'))


    conn.close()

windows下,当客户端停掉,server端 conn.recv(1024)得到的数据不是空值,而是异常,异常发生后,程序就会退出,所以这里就要用到异常处理方法try语法,具体代码如下:这段代码如果用到Linux中,将无限循环下去,因为客户端停掉后,服务端会认为conn.recv(1024)得到空值,这样无限循环下去。

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
print('server waiting ......')

conn,addr = sk.accept()

client_data = conn.recv(1024)
print(str(client_data,'utf8'))
conn.send(bytes("不要回答,不要回答",'utf8'))
while True:
try:
client_data = conn.recv(1024)
print('client_data',str(client_data,'utf8'))
except Exception:
print("clinet closed,break")
break
server_response = input(">>>")
conn.send(bytes(server_response,'utf8'))

conn.close()

 使用socket写一个远程的ssh工具:

04socket_server.py代码如下:

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习,做成可以远程输入命令,客户端获得输出结果,如果你的命令输出结果很多,那么客户端得到的结
'''
import socket
import subprocess
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print('server waiting ......')

    conn,addr = sk.accept()

    # client_data = conn.recv(1024)
    # print(str(client_data,'utf8'))
    # conn.send(bytes("不要回答,不要回答",'utf8'))
    while True:
            print("客户端{}已经连接,等待客户端传输命令... ...".format(conn.getpeername()))
            client_cmd_re = conn.recv(1024)
            if not client_cmd_re:break   # 判断当,对方传过来的值为空时,直接退出(在客户端我已经判断用户输入不能为空,所以这里为空,只有一种可能,就是客户端关闭程序了.如果没有这句的break,会导致客户端一旦关闭,服务端也跟着关闭.
            # 因为当客户端关闭后,传过来的不知道是什么东西)
            print("用户传来了命令:%s"%client_cmd_re)   # 打印输出用户传来的命令
            cmd_re_str = str(client_cmd_re,'utf8') # 将用户传来的命令转换成str

            # 将命令执行,并获得执行结果
            cmd_exec_result = subprocess.Popen(cmd_re_str,shell=True,stdout=subprocess.PIPE).stdout.read()
            # print(cmd_exec_result)    # 打印执行的结果
            # print(len(cmd_exec_result)) # 打印结果的长度
            if  len(cmd_exec_result) == 0 :    #   判断结果是不是为空,为空说明用户传过来的命令,本服务器无法执行.
                cmd_exec_result = bytes("命令 '{}' 后没有返回执行结果,请检查命令是否正确...".format(cmd_re_str),'utf8')    #  提示命令不对
            #将执行结果发送给客户端
            conn.send(cmd_exec_result)
    conn.close()

04socket_client.py代码如下:

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

# sk.sendall(bytes("请求占领地球",'utf8'))
#
# server_reply = sk.recv(1024)
# print(str(server_reply,'utf8'))
while True:
    input_cmd = input("cmd:").strip()               # 用户输入命令
    if input_cmd == 'q':break                       # 用户如果输入的是q,退出输入命令的循环
    if not input_cmd:continue                       # 用户如果直接按了回车,既为空,则进行下次循环
    print(input_cmd)                                 # 打印此次用户输入的命令
    sk.send(bytes(input_cmd,'utf8'))                 # 将用户的命令通过socket发送给服务器端
    cmd_exec_data = sk.recv(1024)                    # 接收服务器执行命令后返回的结果
    print(str(cmd_exec_data,'utf8'))
sk.close()

 上面的代码已经实现了,client连接到server后,进行基本的简单的命令。却不能进行实时的交互,比如top,cd 这种执行后没法返回的命令。具体如何时间,第七天的课程暂不说明。

那么问题来了,client端在接收设置中用到了sk.recv(1024),设置了每次最大收1024个字节,当你执行一个ifconfig命令,字节将大于1024,客户端就会一次收不完,这些数据,会怎样。这些数据已经被服务端通过socket发送过来了。你再次调用sk.recv(1024)时,继续接收余下的数据。那么怎样才能继续接收呢?目前的代码是循环到下次输入命令时会接收ifconfig命令结果的后续。那它这次的命令就又要排队到后面了。

那么如何进行循环接收呢?当然是在本次执行命令中,在sk.recv(1024)代码前加while 循环。

问题又来了。默认是死循环,while True,那么本次命令循环接收sk.recv(1024)结束条件怎么设置呢。

先说明:这里处理方法只有一种,就是在服务端传给客户端字符串前,先把传输的字节数告诉客户端。客户端循环接收字节,并把接收到的字节数累加统计,然后拿接收到的字节总数和服务端发来的做比较。如果小于继续循环。

但是: 老师给我们理清了思路,尝试了3种测试的方法,通过这3次尝试,让我们渐渐的了解socket传输的内部原理。

尝试方案1:

  客户端在接收调用sk.recv(1024)方法,我们可以尝试调大这个数值,看看结果。是可以,每次多收一些,但是,如果你一个字符串有100*1024bytes,你不能把接收数值调整到100M吧,那内存还不爆掉。并且socket官方建议最大不能超过8192,所以此方案不可行。并且一般设置500即可。

尝试方案2:

   我们在接收哪里直接while True,一直接收,直到接收不到数据时就退出break

  06socket_server.py 不需要改动

  06socket_client.py 代码如下:

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
当服务端发送过来的字符串字节数过大,一次接收不完,客户端通过判断服务器端是不是发送完,来作为循环接收的依据。
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

# sk.sendall(bytes("请求占领地球",'utf8'))
#
# server_reply = sk.recv(1024)
# print(str(server_reply,'utf8'))
while True:
    input_cmd = input("cmd:").strip()               # 用户输入命令
    if input_cmd == 'q':break                       # 用户如果输入的是q,退出输入命令的循环
    if not input_cmd:continue                       # 用户如果直接按了回车,既为空,则进行下次循环
    print(input_cmd)                                 # 打印此次用户输入的命令
    sk.send(bytes(input_cmd,'utf8'))                 # 将用户的命令通过socket发送给服务器端

    res = bytes('','utf8')
    while True:
        cmd_exec_data = sk.recv(500)                    # 接收服务器执行命令后返回的结果
        res += cmd_exec_data
        if not cmd_exec_data :            #这里判断,当接收不到数据时,就退出循环
            break
    print(str(res,'utf8'))
sk.close()

ps:此方式不可行。原因是cmd_exec_data = sk.recv(500) 这里在接收完本次命令执行的所有结果后,会一直阻塞在这里。不会退出循环,此时客户端就卡死了。

总结:sk.recv()和sk.send()都会阻塞

尝试方案3:

  在客户端判断服务端的数据是不是传完了,如果没传完,则继续循环接收。

  05scoket_server.py 不需要改动

  05socket_client.py 代码如下:

  

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
当服务端发送过来的字符串字节数过大,一次接收不完,客户端通过判断服务器端是不是发送完,来作为循环接收的依据.
如何判断服务器端是不是发送完成?
我们认为,客户端每次接收的最大值500字节.那么如果最后一次传过来的数据长度没有500字节,那么我们是不是就可以说这次就是最后一次传输来.
所以while 循环中判断接收到的数据长度小于500,就退出循环接收.
代码如下:
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

# sk.sendall(bytes("请求占领地球",'utf8'))
#
# server_reply = sk.recv(1024)
# print(str(server_reply,'utf8'))
while True:
    input_cmd = input("cmd:").strip()               # 用户输入命令
    if input_cmd == 'q':break                       # 用户如果输入的是q,退出输入命令的循环
    if not input_cmd:continue                       # 用户如果直接按了回车,既为空,则进行下次循环
    print(input_cmd)                                 # 打印此次用户输入的命令
    sk.send(bytes(input_cmd,'utf8'))                 # 将用户的命令通过socket发送给服务器端

    res = bytes('','utf8')
    while True:
        cmd_exec_data = sk.recv(500)                    # 接收服务器执行命令后返回的结果
        res += cmd_exec_data
        if len(cmd_exec_data) < 500:            #当接收到的字节小于500就判断已经接收完了。
            break
    print(str(res,'utf8'))
sk.close()

这里我们执行命令,得到一些数据量还没有那么大的字符串时没有问题,担当字符串相当大时,就出现之前的那种错误了,原因是我们最初认为的客户端每次都是按最大500字节接收(这个观点错了)。两个因素导致单次接收可能没有500字节。

  因素1,服务端发送会因为网络状态发送数据。如果网络不稳定,传个10几个bytes也是可能的。

  因素2,客户端接收也可能因为网络或者性能,导致这次接收延迟了几百毫秒(内部缓冲机制时间限制也许只有100毫秒)。从而导致本次接收不到500字节。

总结:客户端不是每次接收都是按照最大限额的字节接收的。这样我们的代码:

    while True:
        cmd_exec_data = sk.recv(500)                    # 接收服务器执行命令后返回的结果
        res += cmd_exec_data
        if len(cmd_exec_data) < 500:        #这里就会在没接收完时,就退出了本次命令执行结果接收呢。
            break

最终答案来了,代码如下:

07socket_server.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习,做成可以远程输入命令,客户端获得输出结果,如果你的命令输出结果很多,那么客户端得到的结
'''
import socket
import subprocess
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print('server waiting ......')

    conn,addr = sk.accept()

    # client_data = conn.recv(1024)
    # print(str(client_data,'utf8'))
    # conn.send(bytes("不要回答,不要回答",'utf8'))
    while True:
            print("客户端{}已经连接,等待客户端传输命令... ...".format(conn.getpeername()))
            client_cmd_re = conn.recv(1024)
            if not client_cmd_re:break   # 判断当,对方传过来的值为空时,直接退出(在客户端我已经判断用户输入不能为空,所以这里为空,只有一种可能,就是客户端关闭程序了.如果没有这句的break,会导致客户端一旦关闭,服务端也跟着关闭.
            # 因为当客户端关闭后,传过来的不知道是什么东西)
            print("用户传来了命令:%s"%client_cmd_re)   # 打印输出用户传来的命令
            cmd_re_str = str(client_cmd_re,'utf8') # 将用户传来的命令转换成str

            # 将命令执行,并获得执行结果
            cmd_exec_result = subprocess.Popen(cmd_re_str,shell=True,stdout=subprocess.PIPE).stdout.read()
            if  len(cmd_exec_result) == 0 :    #   判断结果是不是为空,为空说明用户传过来的命令,本服务器无法执行.
                cmd_exec_result = bytes("命令 '{}' 后没有返回执行结果,请检查命令是否正确...".format(cmd_re_str),'utf8')    #  提示命令不对
            
            
            result_size = len(cmd_exec_result) # 计算本次获得到结果是多少字节
            conn.send(bytes("CMD_RESULT_SIZE|{}".format(result_size),'utf8')) # 首先将本次将要发送的字符串的大小告诉客户端.

            conn.send(cmd_exec_result)   #紧接着把执行结果发过去
    conn.close()

07socket_client.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
当服务端发送过来的字符串字节数过大,一次接收不完,客户端通过判断服务器端是不是发送完,来作为循环接收的依据.
如何判断服务器端是不是发送完成?
根据服务器端发来的大小,判断是否接收完成.
代码如下:
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

# sk.sendall(bytes("请求占领地球",'utf8'))
#
# server_reply = sk.recv(1024)
# print(str(server_reply,'utf8'))
while True:
    input_cmd = input("cmd:").strip()               # 用户输入命令
    if input_cmd == 'q':break                       # 用户如果输入的是q,退出输入命令的循环
    if not input_cmd:continue                       # 用户如果直接按了回车,既为空,则进行下次循环
    print(input_cmd)                                 # 打印此次用户输入的命令
    sk.send(bytes(input_cmd,'utf8'))                 # 将用户的命令通过socket发送给服务器端

    server_ack_msg = sk.recv(100)
    cmd_res_msg = str(server_ack_msg,'utf8').split('|')
    # "CMD_RESULT_SIZE|{}".format(result_size),'utf8')
    # print(cmd_res_msg)

    if cmd_res_msg[0] == "CMD_RESULT_SIZE": # 判断你发来的这个是不是文件大小的标示
        cmd_res_size = int(cmd_res_msg[1])  #如果是,把大小付给cmd_res_size变量

    res = bytes('','utf8')
    recevied_size = 0                   # 初始化字符串大小为0
    while recevied_size < cmd_res_size: # 如果已接收的字符串大小小于服务器传过来的大小,则循环接收
        data = sk.recv(50)              # 接收数据
        recevied_size += len(data)      # 将数据累加
        res += data                     # 累加统计已接收的数据
    print(str(res,'utf8'))              # 打印最终结果
sk.close()

到此为止,我们认为,socket接收大数据的功能已经实现。大功告成~~~,no,还有错误,当数据量大时,会出现 “socket编程中最大的一个坑 粘包 ”

我们看07socket_server.py中最后发送的代码

            conn.send(bytes("CMD_RESULT_SIZE|{}".format(result_size),'utf8')) # 首先将本次将要发送的字符串的大小告诉客户端. 
                            
            conn.send(cmd_exec_result)

我们看到一连两次发送。这就会出现“socket编程中最大的一个坑 粘包 ”,原因是缓冲区的问题,你两次发送,缓冲区会把两次要发送的内容放到一起发送。

那么怎么解决呢?

两个方案:

  1.在两次发送之间sleep(1) 间隔1秒,这样缓冲区就失效。但是这种方式low,如果并发大就会很慢。

  2.发送完文件大小后,使用conn.recv(100)这个能阻塞的方法,获取客户端返回来已经接收到文件大小信息的确认信息。(当然客户端也要加返回给服务端的确认信息。)

最终代码如下:

08socket_server.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
socket学习,做成可以远程输入命令,客户端获得输出结果,如果你的命令输出结果很多,那么客户端得到的结
'''
import socket
import subprocess
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print('server waiting ......')

    conn,addr = sk.accept()

    # client_data = conn.recv(1024)
    # print(str(client_data,'utf8'))
    # conn.send(bytes("不要回答,不要回答",'utf8'))
    while True:
            print("客户端{}已经连接,等待客户端传输命令... ...".format(conn.getpeername()))
            client_cmd_re = conn.recv(1024)
            if not client_cmd_re:break   # 判断当,对方传过来的值为空时,直接退出(在客户端我已经判断用户输入不能为空,所以这里为空,只有一种可能,就是客户端关闭程序了.如果没有这句的break,会导致客户端一旦关闭,服务端也跟着关闭.
            # 因为当客户端关闭后,传过来的不知道是什么东西)
            print("用户传来了命令:%s"%client_cmd_re)   # 打印输出用户传来的命令
            cmd_re_str = str(client_cmd_re,'utf8') # 将用户传来的命令转换成str

            # 将命令执行,并获得执行结果
            cmd_exec_result = subprocess.Popen(cmd_re_str,shell=True,stdout=subprocess.PIPE).stdout.read()
            if  len(cmd_exec_result) == 0 :    #   判断结果是不是为空,为空说明用户传过来的命令,本服务器无法执行.
                cmd_exec_result = bytes("命令 '{}' 后没有返回执行结果,请检查命令是否正确...".format(cmd_re_str),'utf8')    #  提示命令不对


            result_size = len(cmd_exec_result) # 计算本次获得到结果是多少字节
            conn.send(bytes("CMD_RESULT_SIZE|{}".format(result_size),'utf8')) # 首先将本次将要发送的字符串的大小告诉客户端.
            client_ack = conn.recv(50)
            if str(client_ack,'utf8') == "CLIENT_READY_TO_RECV":
                conn.send(cmd_exec_result)
    conn.close()

08socket_client.py

#!/usr/bin/env python3.5
#__author__:'ted.zhou'
'''
当服务端发送过来的字符串字节数过大,一次接收不完,客户端通过判断服务器端是不是发送完,来作为循环接收的依据.
如何判断服务器端是不是发送完成?
根据服务器端发来的大小,判断是否接收完成.
代码如下:
'''
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket()
sk.connect(ip_port)

# sk.sendall(bytes("请求占领地球",'utf8'))
#
# server_reply = sk.recv(1024)
# print(str(server_reply,'utf8'))
while True:
    input_cmd = input("cmd:").strip()               # 用户输入命令
    if input_cmd == 'q':break                       # 用户如果输入的是q,退出输入命令的循环
    if not input_cmd:continue                       # 用户如果直接按了回车,既为空,则进行下次循环
    print(input_cmd)                                 # 打印此次用户输入的命令
    sk.send(bytes(input_cmd,'utf8'))                 # 将用户的命令通过socket发送给服务器端

    server_ack_msg = sk.recv(100)
    cmd_res_msg = str(server_ack_msg,'utf8').split('|')
    # "CMD_RESULT_SIZE|{}".format(result_size),'utf8')
    # print(cmd_res_msg)

    if cmd_res_msg[0] == "CMD_RESULT_SIZE": # 判断你发来的这个是不是文件大小的标示
        cmd_res_size = int(cmd_res_msg[1])  #如果是,把大小付给cmd_res_size变量
        sk.send(b"CLIENT_READY_TO_RECV")

    res = bytes('','utf8')
    recevied_size = 0                   # 初始化字符串大小为0
    while recevied_size < cmd_res_size: # 如果已接收的字符串大小小于服务器传过来的大小,则循环接收
        data = sk.recv(50)              # 接收数据
        recevied_size += len(data)      # 将数据累加
        res += data                     # 累加统计已接收的数据
    print(str(res,'utf8'))              # 打印最终结果
sk.close()
原文地址:https://www.cnblogs.com/zhming26/p/5527202.html