Python学习日记(三十一) 黏包问题

系统 1829 0
            
              import
            
            
               subprocess

res 
            
            = subprocess.Popen(
            
              '
            
            
              dir
            
            
              '
            
            ,shell=True,stdout=subprocess.PIPE,stderr=
            
              subprocess.PIPE)

            
            
              print
            
            (
            
              '
            
            
              Stdout:
            
            
              '
            
            ,res.stdout.read().decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))

            
            
              print
            
            (
            
              '
            
            
              Stderr:
            
            
              '
            
            ,res.stderr.read().decode(
            
              '
            
            
              gbk
            
            
              '
            
            ))
          

PIPE把输出的东西装到一个'水管'里,如果在windows中的编码格式是gbk,执行结果:

            
              Stdout:  驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


            
            2019/09/16  13:48    
            
              
                          .

              
              2019/09/16  13:48    
              
                
                            ..

                
                2019/09/16  13:47    
                
                  
                              .idea

                  
                  2019/09/16  13:46                21
                  
                     Client1.py

                  
                  2019/09/16  13:42
                  
                                     0 Client2.py

                  
                  2019/09/16  13:48               207
                  
                     Sever1.py

                  
                  2019/09/16  01:41                70
                  
                     time_test.py

                  
                  2019/09/14  23:51    
                  
                    
                                venv
               
                    
                    4 个文件            298
                    
                       字节
               
                    
                    4 个目录 45,863,636,992
                    
                       可用字节

Stderr: 
                    
                  
                
              
            
          

在这里也可以使用os.popen()但是它会不管正确和错误的结果都放在一起,而用subprocess能够分别拿到正确和错误的信息

 

基于TCP实现的黏包

Sever:

            
              import
            
            
               socket
sk 
            
            =
            
               socket.socket()
sk.bind((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8092
            
              ))
sk.listen()
conn,addr 
            
            =
            
               sk.accept()

            
            
              while
            
            
               True:
    cmd 
            
            = input(
            
              '
            
            
              <<<
            
            
              '
            
            
              )
    conn.send(cmd.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    ret 
            
            = conn.recv(1024).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              print
            
            
              (ret)
conn.close()
sk.close()
            
          

Client:

            
              import
            
            
               socket

            
            
              import
            
            
               subprocess
sk 
            
            =
            
               socket.socket()
sk.connect((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8092
            
              ))

            
            
              while
            
            
               True:
    cmd 
            
            = sk.recv(1024).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    ret 
            
            = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=
            
              subprocess.PIPE)
    std_out 
            
            = 
            
              '
            
            
              stdout:
            
            
              '
            
             + (ret.stdout.read()).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    std_err 
            
            = 
            
              '
            
            
              stderr:
            
            
              '
            
             + (ret.stderr.read()).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              print
            
            
              (std_out)
    
            
            
              print
            
            
              (std_err)
    sk.send(std_out.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    sk.send(std_err.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
sk.close()
            
          

执行结果:

Sever:

            <<<
            
              dir;ls
stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录



            
            <<<
            
              ipconfig
stderr:找不到文件


            
            <<<
          

Client:

            
              stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


stderr:找不到文件

stdout:
Windows IP 配置


以太网适配器 Bluetooth 网络连接 
            
            2
            
              :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
            
            8018%14
            
              
   IPv4 地址 . . . . . . . . . . . . : 
            
            192.168.43.216
            
              
   子网掩码  . . . . . . . . . . . . : 
            
            255.255.255.0
            
              
   默认网关. . . . . . . . . . . . . : 
            
            192.168.43.1
            
              

隧道适配器 本地连接
            
            * 3
            
              :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

stderr:
            
          

当我们在sever端输入dir;ls命令时,只有stdout的结果跑出来,而当我们输入ipconfig这个命令时,系统将上一次dir;ls未执行完的stderr的结果给跑出来。像这样没有接受完全或者接受多了的就是黏包现象。

TCP会有黏包现象但是它不丢包。

 

基于UDP实现的黏包

Sever:

            
              import
            
            
               socket
sk 
            
            = socket.socket(type=
            
              socket.SOCK_DGRAM)
sk.bind((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8092
            
              ))
msg,addr 
            
            = sk.recvfrom(10240
            
              )


            
            
              while
            
             1
            
              :
    cmd 
            
            = input(
            
              '
            
            
              <<<
            
            
              '
            
            
              )
    
            
            
              if
            
             cmd == 
            
              '
            
            
              q
            
            
              '
            
            
              :
        
            
            
              break
            
            
              
    sk.sendto(cmd.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ),addr)
    msg,addr 
            
            = sk.recvfrom(10240
            
              )
    
            
            
              print
            
            (msg.decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))

sk.close()
            
          

Client:

            
              import
            
            
               socket

            
            
              import
            
            
               subprocess
sk 
            
            = socket.socket(type=
            
              socket.SOCK_DGRAM)
addr 
            
            = (
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8092
            
              )
sk.sendto(
            
            
              '
            
            
              Start
            
            
              '
            
            .encode(
            
              '
            
            
              utf-8
            
            
              '
            
            
              ),addr)

            
            
              while
            
             1
            
              :
    cmd,addr 
            
            = sk.recvfrom(10240
            
              )
    ret 
            
            = subprocess.Popen(cmd.decode(
            
              '
            
            
              gbk
            
            
              '
            
            ),shell=True,stderr=subprocess.PIPE,stdout=
            
              subprocess.PIPE)
    std_out 
            
            = 
            
              '
            
            
              Stdout:
            
            
              '
            
             + (ret.stdout.read()).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    std_err 
            
            = 
            
              '
            
            
              Stderr:
            
            
              '
            
             + (ret.stderr.read()).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              print
            
            
              (std_out)
    
            
            
              print
            
            
              (std_err)
    sk.sendto(std_out.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ),addr)
    sk.sendto(std_err.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ),addr)
sk.close()
            
          

执行结果:

Sever:

            <<<
            
              dir;ls
Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录



            
            <<<
            
              dir
Stderr:找不到文件


            
            <<<
            
              ipconfig
Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


            
            2019/09/16  14:43    
            
              
                          .

              
              2019/09/16  14:43    
              
                
                            ..

                
                2019/09/16  14:37    
                
                  
                              .idea

                  
                  2019/09/16  14:43               553
                  
                     Client1.py

                  
                  2019/09/16  13:42
                  
                                     0 Client2.py

                  
                  2019/09/16  14:43               306
                  
                     Sever1.py

                  
                  2019/09/16  01:41                70
                  
                     time_test.py

                  
                  2019/09/14  23:51    
                  
                    
                                venv
               
                    
                    4 个文件            929
                    
                       字节
               
                    
                    4 个目录 45,855,449,088
                    
                       可用字节


                    
                    <<<
                    
                      pwd
Stderr:

                    
                    <<<
                    
                      ip
Stdout:
Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                    
                    2
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                    
                    8018%14
                    
                      
   IPv4 地址 . . . . . . . . . . . . : 
                    
                    192.168.43.216
                    
                      
   子网掩码  . . . . . . . . . . . . : 
                    
                    255.255.255.0
                    
                      
   默认网关. . . . . . . . . . . . . : 
                    
                    192.168.43.1
                    
                      

隧道适配器 本地连接
                    
                    * 3
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 


                    
                    <<<
                  
                
              
            
          

Client:

            
              Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


Stderr:找不到文件

Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


            
            2019/09/16  14:43    
            
              
                          .

              
              2019/09/16  14:43    
              
                
                            ..

                
                2019/09/16  14:37    
                
                  
                              .idea

                  
                  2019/09/16  14:43               553
                  
                     Client1.py

                  
                  2019/09/16  13:42
                  
                                     0 Client2.py

                  
                  2019/09/16  14:43               306
                  
                     Sever1.py

                  
                  2019/09/16  01:41                70
                  
                     time_test.py

                  
                  2019/09/14  23:51    
                  
                    
                                venv
               
                    
                    4 个文件            929
                    
                       字节
               
                    
                    4 个目录 45,855,449,088
                    
                       可用字节

Stderr:
Stdout:
Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                    
                    2
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                    
                    8018%14
                    
                      
   IPv4 地址 . . . . . . . . . . . . : 
                    
                    192.168.43.216
                    
                      
   子网掩码  . . . . . . . . . . . . : 
                    
                    255.255.255.0
                    
                      
   默认网关. . . . . . . . . . . . . : 
                    
                    192.168.43.1
                    
                      

隧道适配器 本地连接
                    
                    * 3
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

Stderr:
Stdout:
Stderr:
                    
                    
                      '
                    
                    
                      pwd
                    
                    
                      '
                    
                    
                       不是内部或外部命令,也不是可运行的程序
或批处理文件。

Stdout:
Stderr:
                    
                    
                      '
                    
                    
                      ip
                    
                    
                      '
                    
                    
                       不是内部或外部命令,也不是可运行的程序
或批处理文件。
                    
                  
                
              
            
          

可以看出UDP不会有黏包现象,会产生丢包现象,没发完就不发了,不完整也不可靠。

 

黏包成因

TCP协议的数据传送

拆包机制

当发送端缓冲区的长度大于网卡的MTU时,TCP会将这次发送的数据拆成几个数据包发送出去。MTU是Maximum Transmission Unit的缩写,意思是网络上传送最大数据包,MTU是字节单位,大部分网络设备的MTU都是1500. 如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

在正常情况下它的拆包可理解为:

Python学习日记(三十一) 黏包问题_第1张图片

面向流的通信特点和Nagle算法

TCP(transport control protocol,传输控制协议),是面向连接的,面向流的,提供高可靠性的服务。收发两端(客户端和服务端)都要有一一成对的socket,因此发送端为了将多个发往接收端的包,更有效地发往对方,使用了优化算法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样接收端就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。

对于空消息:TCP是基于数据流的,于是收发消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而UDP协议是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,UDP协议会帮你封装上消息然后发出去。

可靠黏包的TCP协议:TCP协议数据不会丢,没有收完包,就会下次接收,会继续上次继续接受。

基于tcp协议特点的黏包现象成因

Python学习日记(三十一) 黏包问题_第2张图片

当我们在socket服务端发送值1、2,然后根据优化算法,它会把1先放到这个缓存当中等一等,然后再把2一起封装起来,然后再发出去,因此我们看到的就是黏包现象

这种现象的表面现象是两个send太近且发送的消息太短

发送端可以使1K1K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据。也就是说,应用程序所看到的数据是一个整体,或者说是一个流(stream),一条消息有多少字节对应程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现黏包问题的原因。而UDP协议是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

 

解决黏包的方法

解决方案一:

Sever:

            
              import
            
            
               socket
sk 
            
            =
            
               socket.socket()
sk.bind((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))
sk.listen()
conn,addr 
            
            =
            
               sk.accept()


            
            
              while
            
            
               True:
    cmd 
            
            = input(
            
              '
            
            
              <<<
            
            
              '
            
            
              )
    
            
            
              if
            
             cmd == 
            
              '
            
            
              q
            
            
              '
            
            
              :
        conn.send(b
            
            
              '
            
            
              q
            
            
              '
            
            
              )
        
            
            
              break
            
            
              
    conn.send(cmd.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    num 
            
            = conn.recv(1024).decode(
            
              '
            
            
              utf-8
            
            
              '
            
            
              )
    conn.send(b
            
            
              '
            
            
              ok
            
            
              '
            
            
              )
    res 
            
            = conn.recv(int(num)).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              print
            
            
              (res)

conn.close()
sk.close()
            
          

Client:

            
              import
            
            
               socket

            
            
              import
            
            
               subprocess

sk 
            
            =
            
               socket.socket()
sk.connect((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))

            
            
              while
            
            
               True:
    cmd 
            
            = sk.recv(1024).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              if
            
             cmd == 
            
              '
            
            
              q
            
            
              '
            
            
              :
        
            
            
              break
            
            
              
    res 
            
            = subprocess.Popen(cmd,shell=
            
              True,
                           stdout
            
            =
            
              subprocess.PIPE,
                           stderr
            
            =
            
              subprocess.PIPE)
    std_out 
            
            =
            
               res.stdout.read()
    std_err 
            
            =
            
               res.stderr.read()
    sk.send(str(len(std_out) 
            
            + len(std_err)).encode(
            
              '
            
            
              utf-8
            
            
              '
            
            
              ))
    sk.recv(
            
            1024
            
              )
    
            
            
              print
            
            (
            
              '
            
            
              Stdout:
            
            
              '
            
             + std_out.decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    
            
            
              print
            
            (
            
              '
            
            
              Stderr:
            
            
              '
            
             + std_err.decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    sk.send(std_out)
    sk.send(std_err)

sk.close()
            
          

执行结果:

Sever:

            <<<
            
              dir
 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


            
            2019/09/17  15:33    
            
              
                          .

              
              2019/09/17  15:33    
              
                
                            ..

                
                2019/09/17  15:31    
                
                  
                              .idea

                  
                  2019/09/17  15:33               623
                  
                     Client1.py

                  
                  2019/09/16  13:42
                  
                                     0 Client2.py

                  
                  2019/09/17  15:21               389
                  
                     Sever1.py

                  
                  2019/09/16  01:41                70
                  
                     time_test.py

                  
                  2019/09/14  23:51    
                  
                    
                                venv
               
                    
                    4 个文件          1,082
                    
                       字节
               
                    
                    4 个目录 45,031,833,600
                    
                       可用字节


                    
                    <<<
                    
                      ls

                    
                    
                      '
                    
                    
                      ls
                    
                    
                      '
                    
                    
                       不是内部或外部命令,也不是可运行的程序
或批处理文件。


                    
                    <<<
                    
                      ipconfig

Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                    
                    2
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                    
                    8018%14
                    
                      
   IPv4 地址 . . . . . . . . . . . . : 
                    
                    192.168.43.216
                    
                      
   子网掩码  . . . . . . . . . . . . : 
                    
                    255.255.255.0
                    
                      
   默认网关. . . . . . . . . . . . . : 
                    
                    192.168.43.1
                    
                      

隧道适配器 本地连接
                    
                    * 3
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 


                    
                    <<<
                  
                
              
            
          

Client:

            
              Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
            
            -
            
              669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


            
            2019/09/17  15:33    
            
              
                          .

              
              2019/09/17  15:33    
              
                
                            ..

                
                2019/09/17  15:31    
                
                  
                              .idea

                  
                  2019/09/17  15:33               623
                  
                     Client1.py

                  
                  2019/09/16  13:42
                  
                                     0 Client2.py

                  
                  2019/09/17  15:21               389
                  
                     Sever1.py

                  
                  2019/09/16  01:41                70
                  
                     time_test.py

                  
                  2019/09/14  23:51    
                  
                    
                                venv
               
                    
                    4 个文件          1,082
                    
                       字节
               
                    
                    4 个目录 45,031,833,600
                    
                       可用字节

Stderr:
Stdout:
Stderr:
                    
                    
                      '
                    
                    
                      ls
                    
                    
                      '
                    
                    
                       不是内部或外部命令,也不是可运行的程序
或批处理文件。

Stdout:
Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                    
                    2
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                    
                    8018%14
                    
                      
   IPv4 地址 . . . . . . . . . . . . : 
                    
                    192.168.43.216
                    
                      
   子网掩码  . . . . . . . . . . . . : 
                    
                    255.255.255.0
                    
                      
   默认网关. . . . . . . . . . . . . : 
                    
                    192.168.43.1
                    
                      

隧道适配器 本地连接
                    
                    * 3
                    
                      :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

Stderr:
                    
                  
                
              
            
          

这种写法的好处就是能确定Sever到底要接受多少数据,不好的地方就是多了一次交互

解决方案二: 使用struct模块

用struct模块我们能把一个数据类型转换成固定长度的bytes

Python学习日记(三十一) 黏包问题_第3张图片

这里以数字类型举例,'i'代表int类型:

            
              import
            
            
               struct

            
            
              print
            
            (struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,2048),len(struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,2048)))         
            
              #
            
            
              b'\x00\x08\x00\x00'    4
            
            
              print
            
            (struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,204800),len(struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,204800)))     
            
              #
            
            
              b'\x00 \x03\x00'       4
            
            
              print
            
            (struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,2048000),len(struct.pack(
            
              '
            
            
              i
            
            
              '
            
            ,2048000)))   
            
              #
            
            
              b'\x00@\x1f\x00'       4
            
          

当后面的数值戳过一定范围的时候程序就会报错

Sever:

            
              import
            
            
               socket

            
            
              import
            
            
               struct
sk 
            
            =
            
               socket.socket()
sk.bind((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))
sk.listen()
conn,addr 
            
            =
            
               sk.accept()


            
            
              while
            
            
               True:
    cmd 
            
            = input(
            
              '
            
            
              <<<
            
            
              '
            
            
              )
    
            
            
              if
            
             cmd == 
            
              '
            
            
              q
            
            
              '
            
            
              :
        conn.send(b
            
            
              '
            
            
              q
            
            
              '
            
            
              )
        
            
            
              break
            
            
              
    conn.send(cmd.encode(
            
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    num 
            
            = conn.recv(4
            
              )
    num 
            
            = struct.unpack(
            
              '
            
            
              i
            
            
              '
            
            
              ,num)[0]
    res 
            
            = conn.recv(int(num)).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              print
            
            
              (res)

conn.close()
sk.close()
            
          

Client:

            
              import
            
            
               socket

            
            
              import
            
            
               subprocess

            
            
              import
            
            
               struct

sk 
            
            =
            
               socket.socket()
sk.connect((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))

            
            
              while
            
            
               True:
    cmd 
            
            = sk.recv(1024).decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              )
    
            
            
              if
            
             cmd == 
            
              '
            
            
              q
            
            
              '
            
            
              :
        
            
            
              break
            
            
              
    res 
            
            = subprocess.Popen(cmd,shell=
            
              True,
                           stdout
            
            =
            
              subprocess.PIPE,
                           stderr
            
            =
            
              subprocess.PIPE)
    std_out 
            
            =
            
               res.stdout.read()
    std_err 
            
            =
            
               res.stderr.read()
    len_num 
            
            = len(std_out) +
            
               len(std_err)
    num_by 
            
            = struct.pack(
            
              '
            
            
              i
            
            
              '
            
            
              ,len_num)
    
            
            
              print
            
            (
            
              '
            
            
              Stdout:
            
            
              '
            
             + std_out.decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    
            
            
              print
            
            (
            
              '
            
            
              Stderr:
            
            
              '
            
             + std_err.decode(
            
              '
            
            
              gbk
            
            
              '
            
            
              ))
    sk.send(num_by)
    sk.send(std_out)
    sk.send(std_err)

sk.close()
            
          

执行结果:

              <<<
              
                dir
 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
              
              -
              
                669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


              
              2019/09/17  16:25    
              
                
                            .

                
                2019/09/17  16:25    
                
                  
                              ..

                  
                  2019/09/17  16:23    
                  
                    
                                .idea

                    
                    2019/09/17  16:25               659
                    
                       Client1.py

                    
                    2019/09/16  13:42
                    
                                       0 Client2.py

                    
                    2019/09/17  16:22               400
                    
                       Sever1.py

                    
                    2019/09/17  16:08               288
                    
                       time_test.py

                    
                    2019/09/14  23:51    
                    
                      
                                  venv
               
                      
                      4 个文件          1,347
                      
                         字节
               
                      
                      4 个目录 45,025,951,744
                      
                         可用字节


                      
                      <<<
                      
                        configip

                      
                      
                        '
                      
                      
                        configip
                      
                      
                        '
                      
                      
                         不是内部或外部命令,也不是可运行的程序
或批处理文件。


                      
                      <<<
                      
                        ipconfig

Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                      
                      2
                      
                        :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                      
                      8018%14
                      
                        
   IPv4 地址 . . . . . . . . . . . . : 
                      
                      192.168.43.216
                      
                        
   子网掩码  . . . . . . . . . . . . : 
                      
                      255.255.255.0
                      
                        
   默认网关. . . . . . . . . . . . . : 
                      
                      192.168.43.1
                      
                        

隧道适配器 本地连接
                      
                      * 3
                      
                        :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 


                      
                      <<<
                    
                  
                
              
            
Sever执行结果
              
                Stdout: 驱动器 C 中的卷是 系统
 卷的序列号是 85C0
              
              -
              
                669A

 C:\Users\Administrator\PycharmProjects\Internet_program 的目录


              
              2019/09/17  16:25    
              
                
                            .

                
                2019/09/17  16:25    
                
                  
                              ..

                  
                  2019/09/17  16:23    
                  
                    
                                .idea

                    
                    2019/09/17  16:25               659
                    
                       Client1.py

                    
                    2019/09/16  13:42
                    
                                       0 Client2.py

                    
                    2019/09/17  16:22               400
                    
                       Sever1.py

                    
                    2019/09/17  16:08               288
                    
                       time_test.py

                    
                    2019/09/14  23:51    
                    
                      
                                  venv
               
                      
                      4 个文件          1,347
                      
                         字节
               
                      
                      4 个目录 45,025,951,744
                      
                         可用字节

Stderr:
Stdout:
Stderr:
                      
                      
                        '
                      
                      
                        configip
                      
                      
                        '
                      
                      
                         不是内部或外部命令,也不是可运行的程序
或批处理文件。

Stdout:
Windows IP 配置


以太网适配器 Bluetooth 网络连接 
                      
                      2
                      
                        :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

以太网适配器 本地连接:

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

无线局域网适配器 无线网络连接:

   连接特定的 DNS 后缀 . . . . . . . : 
   本地链接 IPv6 地址. . . . . . . . : fe80::8c6:36a9:6fa6:
                      
                      8018%14
                      
                        
   IPv4 地址 . . . . . . . . . . . . : 
                      
                      192.168.43.216
                      
                        
   子网掩码  . . . . . . . . . . . . : 
                      
                      255.255.255.0
                      
                        
   默认网关. . . . . . . . . . . . . : 
                      
                      192.168.43.1
                      
                        

隧道适配器 本地连接
                      
                      * 3
                      
                        :

   媒体状态  . . . . . . . . . . . . : 媒体已断开
   连接特定的 DNS 后缀 . . . . . . . : 

Stderr:
                      
                    
                  
                
              
            
Client执行结果

实现一个大文件的传输和下载

当我们在网络上传输所有数据时,这些数据都叫数据包,数据包里的所有数据都叫报文,报文里不止有你的数据还有IP地址、MAC地址、端口号等,所有的报文都有报头,这是由协议规定的。所有在网络上传播数据包的协议里都有一个报头。什么时候需要自己定制报文?比如说复杂的应用上就会应用到、传输文件的时候(文件名、文件大小、文件类型、存储路径等)...

其实在网络传输的过程当中处处有协议,协议就是一堆报头和报文(都由字节组成)。

Sever:

            
              import
            
            
               socket

            
            
              import
            
            
               struct

            
            
              import
            
            
               json
buffer 
            
            = 1024
            
              
sk 
            
            =
            
               socket.socket()
sk.bind((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))
sk.listen()

conn,addr 
            
            =
            
               sk.accept()

head_len 
            
            = conn.recv(4
            
              )
struct.unpack(
            
            
              '
            
            
              i
            
            
              '
            
            
              ,head_len)[0]
json_head 
            
            = conn.recv(head_len).decode(
            
              '
            
            
              utf-8
            
            
              '
            
            
              )
head 
            
            =
            
               json.loads(json_head)
file_size 
            
            = head[
            
              '
            
            
              fileSize
            
            
              '
            
            
              ]

with open(r
            
            
              '
            
            
              dir\%s
            
            
              '
            
            %head[
            
              '
            
            
              fileName
            
            
              '
            
            ],
            
              '
            
            
              wb
            
            
              '
            
            
              ) as f:
    
            
            
              while
            
            
               file_size:
        
            
            
              if
            
             file_size >=
            
               buffer:
            content 
            
            =
            
               conn.recv(buffer)
            f.write(content)
            file_size 
            
            -=
            
               buffer
        
            
            
              else
            
            
              :
            content 
            
            =
            
               conn.recv(buffer)
            f.write(content)
            
            
            
              break
            
            
              
conn.close()
sk.close()
            
          

Client:

            
              import
            
            
               socket

            
            
              import
            
            
               struct

            
            
              import
            
            
               os

            
            
              import
            
            
               json

sk 
            
            =
            
               socket.socket()
sk.connect((
            
            
              '
            
            
              127.0.0.1
            
            
              '
            
            ,8080
            
              ))
buffer 
            
            = 1024
            
              
head 
            
            = {
            
              '
            
            
              filePath
            
            
              '
            
             : r
            
              '
            
            
              C:\Users\Administrator\Desktop\专题\海報資料夾\专题海报
            
            
              '
            
            
              ,
        
            
            
              '
            
            
              fileName
            
            
              '
            
             : r
            
              '
            
            
              专题海报
            
            
              '
            
            
              ,
        
            
            
              '
            
            
              fileSize
            
            
              '
            
            
               : None}
file_path 
            
            = os.path.join(head[
            
              '
            
            
              filePath
            
            
              '
            
            ],head[
            
              '
            
            
              fileName
            
            
              '
            
            
              ])
file_size 
            
            =
            
               os.path.getsize(file_path)

head[
            
            
              '
            
            
              fileSize
            
            
              '
            
            ] =
            
               file_size
json_head 
            
            = json.dumps(head)                            
            
              #
            
            
              字典转成字典
            
            
bytes_head = json_head.encode(
            
              '
            
            
              utf-8
            
            
              '
            
            )                  
            
              #
            
            
              字符串转bytes
            
            
              
head_len 
            
            = len(bytes_head)                              
            
              #
            
            
              报头的长度
            
            
pack_len = struct.pack(
            
              '
            
            
              i
            
            
              '
            
            
              ,head_len)

sk.send(pack_len)                                       
            
            
              #
            
            
              先发报头的长度
            
            
sk.send(bytes_head)                                     
            
              #
            
            
              再发bytes类型的报头
            
            
with open(r
            
              '
            
            
              dir\%s
            
            
              '
            
            %file_path,
            
              '
            
            
              rb
            
            
              '
            
            
              ) as f:
        
            
            
              while
            
            
               file_size:
                
            
            
              if
            
             file_size >=
            
               buffer:
                        content 
            
            = f.read(buffer)  
            
              #
            
            
               每次文件读出的内容
            
            
                                      sk.send(content)
                        file_size 
            
            -=
            
               buffer
                
            
            
              else
            
            
              :
                        content 
            
            =
            
               f.read(file_size)
                        sk.send(content)
                        
            
            
              break
            
            
              
sk.close()
            
          

更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论