• Python学习-day8


    一、Socket进阶

            运用socket实现简版ssh,即在客户端输入指令,服务器收到指令后执行并返回结果

     1 import socket
     2 import os
     3 server = socket.socket()
     4 server.bind(('localhost',9999))
     5 server.listen()
     6 
     7 while True:
     8     conn,addr = server.accept()
     9     print('new conn : ',addr)
    10     while True:
    11         print('waiting to be execute ')
    12         data = conn.recv(1024)
    13         if not data:
    14             print('conncetion is cut')
    15             break
    16         print('execute instruction!',data)
    17 
    18         cmd_res = os.popen(data.decode()).read()#popen返回的是内存地址,加read可以读出内容
    19         
    20         print('before send')
    21         if len(cmd_res) == 0:#指令如果没有返回结果如cd,返回客户端'cmd has no output !'
    22             cmd_res = 'cmd has no output !'
    23        
    24         conn.send(cmd_res.encode('utf-8'))
    25         print('send done!')
    26 server.close()
    server
     1 import socket
     2 
     3 client = socket.socket()
     4 client.connect(('localhost',9999))
     5 while True:
     6     cmd = input('>>>:').strip()
     7     if len(cmd)==0:continue
     8     client.send(cmd.encode('utf-8'))
     9 
    10     cmd_res_size = client.recv(1024)#接收命令长度
    11     print('data lenth: ',cmd_res_size.decode())
    12 
    13 client.close()
    client

    但是这样实现,会发现当指令的执行结果内容太长时,无法一次性接收完,然后执行下一条指令时,又会接收到上次没接收完整的内容。因为客户端限制了每次接收的数据最多1024字节,而且socket每次发送接收数据的量是有限的,所以客户端接收的数据最大不能超过8192字节。那么当传输数据超过了客户端能一次接收的最大限制时,就要让客户端分批接收了,可以在发送数据前告知客户端数据的总大小,然后客户端可以计算出分批接收的次数。

     1 import socket
     2 import os
     3 server = socket.socket()
     4 server.bind(('localhost',9999))
     5 server.listen()
     6 
     7 while True:
     8     conn,addr = server.accept()
     9     print('new conn : ',addr)
    10     while True:
    11         print('waiting to be execute ')
    12         data = conn.recv(1024)
    13         if not data:
    14             print('conncetion is cut')
    15             break
    16         print('execute instruction!',data)
    17 
    18         cmd_res = os.popen(data.decode()).read()#popen返回的是内存地址,加read可以读出内容
    19         print('before send',len(cmd_res))
    20         print('before send')
    21         if len(cmd_res) == 0:#指令如果没有返回结果如cd,返回客户端'cmd has no output !'
    22             cmd_res = 'cmd has no output !'
    23 
    24         conn.send(str(len(cmd_res.encode())).encode('utf-8'))
    25         conn.send(cmd_res.encode('utf-8'))
    26         print('send done!')
    27 server.close()
    server
     1 import socket
     2 client = socket.socket()
     3 client.connect(('localhost',9999))
     4 while True:
     5     cmd = input('>>>:').strip()
     6     if len(cmd)==0:continue
     7     client.send(cmd.encode('utf-8'))
     8 
     9     cmd_res_size = client.recv(1024)#接收命令长度
    10     print('data lenth: ',cmd_res_size.decode())
    11 
    12     received_size = 0
    13     received_data = b''
    14     while received_size < int(cmd_res_size.decode()):
    15         data = client.recv(1024)
    16         received_size += len(data)
    17         received_data += data
    18     else:
    19         print('cmd res receive done...',received_size)
    20         print(received_data.decode())
    21         print('received done!')
    22 client.close()
    client

          修改后的程序,运行还是会出现‘粘包’的现象,即服务器端连续调用send方法时,多次发送的数据被拼到一起传送给客户端。因为我们调用send方法时,数据并没有立刻发送到客户端,而是先把数据放到了socket的缓冲池中,等缓冲池满了或者超时了才会发送给客户端。这样就导致了客户端一次接收的数据实际可能包含多次的请求结果。我们没办法直接操作缓冲区,所以要解决这个问题,只能让缓冲区超时。可以在服务器端每次发出数据后,等待客户端返回一个收到数据的确认信息,收到确认信息后再发送下一次的数据。

     1 import socket
     2 import os
     3 server = socket.socket()
     4 server.bind(('localhost',9999))
     5 server.listen()
     6 
     7 while True:
     8     conn,addr = server.accept()
     9     print('new conn : ',addr)
    10     while True:
    11         print('waiting to be execute ')
    12         data = conn.recv(1024)
    13         if not data:
    14             print('conncetion is cut')
    15             break
    16         print('execute instruction!',data)
    17 
    18         cmd_res = os.popen(data.decode()).read()#popen返回的是内存地址,加read可以读出内容
    19         print('before send',len(cmd_res))
    20 
    21         if len(cmd_res) == 0:#指令如果没有返回结果如cd,返回客户端'cmd has no output !'
    22             cmd_res = 'cmd has no output !'
    23         conn.send(str(len(cmd_res.encode())).encode('utf-8'))#发送数据前,先发送数据长度给客户端
    24         client_ack = conn.recv(1024)#接收客户端的确认信息
    25         print(client_ack.decode())
    26         conn.send(cmd_res.encode('utf-8'))
    27         print('send done!')
    28 server.close()
    server
     1 import socket                                                        
     2 client = socket.socket()                                             
     3 client.connect(('192.168.170.133',9999))                             
     4 while True:                                                          
     5     cmd = input('>>>:').strip()                                      
     6     if len(cmd)==0:continue                                          
     7     client.send(cmd.encode('utf-8'))                                 
     8                                                                      
     9     cmd_res_size = client.recv(1024)#接收命令长度                          
    10     print('data lenth: ',cmd_res_size.decode())                      
    11     client.send(b"ready to recv file") #发送确认消息                       
    12                                                                      
    13     received_size = 0                                                
    14     received_data = b''                                              
    15     while received_size < int(cmd_res_size.decode()):                
    16         data = client.recv(1024)                                     
    17         received_size += len(data)                                   
    18         received_data += data                                        
    19     else:                                                            
    20         print('cmd res receive done...',received_size)               
    21         print(received_data.decode())                                
    22         print('received done!')                                      
    23 client.close()                                                       
    24                                                                      
    client

     这样修改后的程序就不会再出现粘包的问题了。

    二、SocketServer

          Python提供了两个基本的socket模块。一个是socket,它提供了标准的BSD Socket API;另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发。

    SocketServer提供了4个基本的服务类:

    TCPServer针对TCP套接字流

    UDPServer针对UDP数据报套接字

    UnixStreamServer和UnixDatagramServer针对UNIX域套接字,不常用。这个四个服务类都是同步处理请求的。一个请求没处理完不能处理下一个请求。要想支持异步模型,可以利用多继承让server类继承ForkingMixIn 或 ThreadingMixIn mix-in classes。

           要实现一项服务,还必须派生一个handler class请求处理类,并重写父类的handle()方法。handle方法就是用来专门是处理请求的。该模块是通过服务类和请求处理类组合来处理请求的。

          SocketServer模块提供的请求处理类有BaseRequestHandler,以及它的派生类StreamRequestHandler和DatagramRequestHandler。从名字看出可以一个处理流式套接字,一个处理数据报套接字。

           

             总结用SocketServer创建一个服务的步骤:

             1.创建一个request handler class(请求处理类),继承自BaseRequestHandler class并重写它的handle()方法,该方法将处理到的请求。

             2.实例化一个server class对象,并将服务的地址和之前创建的request handler class传递给它。

             3.调用server class对象的handle_request() 或 serve_forever()方法来开始处理请求。

     1 import  socketserver
     2 class MyTCPHandler(socketserver.BaseRequestHandler):#定义请求处理类
     3     def handle(self):
     4         while True:
     5             try:
     6                 self.data = self.request.recv(1024).strip()
     7                 print('{} wrote:'.format(self.client_address[0]))
     8                 print(self.data)
     9                 self.request.send(self.data.upper())
    10             except ConnectionResetError as e:
    11                 print('err',e)
    12                 break
    13 
    14 if __name__ == '__main__':
    15     host,port = 'localhost',9999
    16     server = socketserver.ThreadingTCPServer((host,port),MyTCPHandler)#实例化服务类
    17     server.serve_forever()#开启服务
    socketserver
  • 相关阅读:
    Django对静态文件的处理——部署阶段
    使用Django来处理对于静态文件的请求
    Django1.7如何配置静态资源访问
    Spring WebSocket中403错误解决
    FastJSON JSONObject 字段排序 Feature.OrderedField
    国际化(i18n) 各国语言缩写
    【转】java.io.Closeable接口
    【转】spring bean 卸载
    This content should also be served over HTTPS
    Failed to close the ServletOutputStream connection cleanly, Broken pipe
  • 原文地址:https://www.cnblogs.com/iclq/p/5861261.html
Copyright © 2020-2023  润新知