Part7 - Socket编程

和自甴很熟 提交于 2020-03-02 03:05:43

本节内容

1.Socket编程

2.Socket多连接实现

3.动态导入

4.断言

一、Socket编程

Socket(TCP和UDP协议的封装)

Socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。

Socket Families(地址簇)        网络层

  • socket.AF_UNIX     unix本机进程间通信(默认进程间不可通信) 
  • socket.AF_INET     IPV4 
  • socket.AF_INET6      IPV6

Socket Types(协议)        传输层

  • socket.SOCK_STREAM  for tcp
  • socket.SOCK_DGRAM   for udp 
  • socket.SOCK_RAW    原始套接字,利用它可以通过IP_HDRINCL套接字选项由用户构造IP头。
  • socket.SOCK_RDM   是一种可靠的UDP形式,即保证交付数据报但不保证顺序。
  • socket.SOCK_SEQPACKET 废弃了

服务端

  1. import socket,os,time  
  2. server = socket.socket()     #实例化服务端,设定协议和地址簇,默认family=AF_INET, type=SOCK_STREAM  
  3. server.bind(('localhost',9999) )      #绑定监听端口  
  4. server.listen()         #允许服务接受连接  
  5.       
  6. while True:  
  7.     conn, addr = server.accept()   #等待连接  
  8.     print("new conn:",addr)  
  9.     while True:  
  10.         print("等待新指令")  
  11.         data = conn.recv(1024)      #接收数据,官方建议最大8192byte  
  12.         if not data:                #客户端断开后server端会不断收到空值  
  13.             print("客户端已断开")  
  14.             break  
  15.         print("执行指令:",data)  
  16.         cmd_res = os.popen(data.decode()).read() #接受字符串,执行结果也是字符串  
  17.         print("before send",len(cmd_res))  
  18.         if len(cmd_res) ==0:  
  19.             cmd_res = "cmd has no output..."  
  20.         conn.send( str(len(cmd_res.encode())).encode("utf-8")    )     #先发大小给客户端  
  21.         #time.sleep(0.5)            #黏包解决low方法  
  22.         conn.recv(1024)             #高逼格方法,等待客户端发送接收确认  
  23.         conn.send(cmd_res.encode("utf-8"))  
  24.         print("send done")  
  25. server.close()      #关闭服务

客户端

  1. import socket  
  2. client = socket.socket()            #实例化客户端,设定协议和地址簇  
  3. client.connect(('localhost',9999))      #建立连接  
  4.       
  5. while True:  
  6.     cmd = input(">>:").strip()  
  7.     if len(cmd) == 0: continue          #Socket不允许发送空数据  
  8.     client.send(cmd.encode("utf-8"))      #发送数据,数据格式必须是bytes  
  9.     cmd_res_size = client.recv(1024)       #接收命令结果的长度  
  10.     client.send("已收到命令结果长度,开始发吧...".encode("utf-8"))      
  11.     print("命令结果大小:",cmd_res_size)  
  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) #每次收到的有可能小于1024,所以必须用len判断(计算长度用bytes格式  
  17.         #print(data.decode())  
  18.         received_data += data  
  19.     else:  
  20.         print("cmd res receive done...",received_size)  
  21.         print(received_data.decode())  
  22. #send(1024)不一定会一次性发送1024的数据,默认情况下python等缓冲区满了才发送数据 
  23. client.close()      #关闭  

二、Socket多连接实现

如何接收一定大小的数据

发送数据前先发送数据大小,接收时不断接收直到达到该大小

Socket粘包问题

  • 问题原因

相邻的两次send()会被在缓冲区中合并成一段数据一起发送

  • 解决办法

    Low:两次send()之间sleep(0.5)

    高逼格:A第一次send B recv后send A recv 后第二次send ...

         也可以利用判断的方法,接收到一定大小就不收了,如上边客户端代码例子。

四种基本Socketserver类(TCP、UDP以及本机进程间通信TCP和UDP)

  • class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
  • class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
  • class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
  • class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)

其他Socketserver类(多线程TCP、UDP等)

  • class socketserver.ThreadingTCPServer
  • class socketserver.ThreadingUDPServer

创建一个SokectServer步骤

  • 必须创建一个请求处理类,并且这个类雅继承BaseRequestHandler,还要重写父类里的handle()。
  • 你必须实例化一种socketserver类,并且穿比address(元组)和上面创建的请求处理类给它。
  • 调用
  • close

server.handle_request()        只能处理一个请求

server.handle_request()        处理多个请求,永远执行

注:handle()处理所有的client请求

  1. import socketserver  
  2.       
  3. class MyTCPHandler(socketserver.BaseRequestHandler):      #建立请求处理类  
  4.     def handle(self):       #重构handle()  
  5.         while True:  
  6.             try:  
  7.                 self.data = self.request.recv(1024).strip()  
  8.                 print("{} wrote:".format(self.client_address[0]))  
  9.                 print(self.data)  
  10.                 self.request.send(self.data.upper())  
  11.             except ConnectionResetError as e:  
  12.                 print("err",e)  
  13.                 break  
  14. if __name__ == "__main__":  
  15.     HOST, PORT = "localhost", 9999  
  16.     # Create the server, binding to localhost on port 9999  
  17.     server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)  #实例化socketserver一种类  
  18.     server.serve_forever()      #调用 

三、动态导入

根据需要动态导入

  1. #官方建议使用  
  2. import importlib  
  3. importlib.import_module('lib.aa')  
  4. print(aa.C().name)  
  5.       
  6. #python解释器用  
  7. lib = __import__('lib.aa')   #lib是包,aa是模块(实际上只导入了lib)  
  8. obj = lib.aa.C()            #Caa中的类  
  9. print(obj.name)             #实例的name方法  

四、断言

用于判断是否符合接下来要执行程序的要求

  1.  aaa = '12313'  
  2.       
  3. assert type(aaa) is int     #用于判断是否符合接下来要执行程序的要求  
  4. print(aaa/2)                #断言正确程序继续,错误报错退出AssertionError 

参考:http://www.cnblogs.com/alex3714/articles/5227251.html

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!