本节内容
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 废弃了
服务端
-
import socket,os,time
-
server = socket.socket() #实例化服务端,设定协议和地址簇,默认family=AF_INET, type=SOCK_STREAM
-
server.bind(('localhost',9999) ) #绑定监听端口
-
server.listen() #允许服务接受连接
-
-
while True:
-
conn, addr = server.accept() #等待连接
-
print("new conn:",addr)
-
while True:
-
print("等待新指令")
-
data = conn.recv(1024) #接收数据,官方建议最大8192byte
-
if not data: #客户端断开后server端会不断收到空值
-
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.5) #黏包解决low方法
-
conn.recv(1024) #高逼格方法,等待客户端发送接收确认
-
conn.send(cmd_res.encode("utf-8"))
-
print("send done")
-
server.close() #关闭服务
客户端
-
import socket
-
client = socket.socket() #实例化客户端,设定协议和地址簇
-
client.connect(('localhost',9999)) #建立连接
-
-
while True:
-
cmd = input(">>:").strip()
-
if len(cmd) == 0: continue #Socket不允许发送空数据
-
client.send(cmd.encode("utf-8")) #发送数据,数据格式必须是bytes
-
cmd_res_size = client.recv(1024) #接收命令结果的长度
-
client.send("已收到命令结果长度,开始发吧...".encode("utf-8"))
-
print("命令结果大小:",cmd_res_size)
-
received_size = 0
-
received_data = b''
-
while received_size < int(cmd_res_size.decode()) : #防止粘包
-
data = client.recv(1024)
-
received_size += len(data) #每次收到的有可能小于1024,所以必须用len判断(计算长度用bytes格式)
-
#print(data.decode())
-
received_data += data
-
else:
-
print("cmd res receive done...",received_size)
-
print(received_data.decode())
-
#send(1024)不一定会一次性发送1024的数据,默认情况下python等缓冲区满了才发送数据
-
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请求
-
import socketserver
-
-
class MyTCPHandler(socketserver.BaseRequestHandler): #建立请求处理类
-
def handle(self): #重构handle()
-
while True:
-
try:
-
self.data = self.request.recv(1024).strip()
-
print("{} wrote:".format(self.client_address[0]))
-
print(self.data)
-
self.request.send(self.data.upper())
-
except ConnectionResetError as e:
-
print("err",e)
-
break
-
if __name__ == "__main__":
-
HOST, PORT = "localhost", 9999
-
# Create the server, binding to localhost on port 9999
-
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #实例化socketserver一种类
-
server.serve_forever() #调用
三、动态导入
根据需要动态导入
-
#官方建议使用
-
import importlib
-
importlib.import_module('lib.aa')
-
print(aa.C().name)
-
-
#python解释器用
-
lib = __import__('lib.aa') #lib是包,aa是模块(实际上只导入了lib)
-
obj = lib.aa.C() #C是aa中的类
-
print(obj.name) #实例的name方法
四、断言
用于判断是否符合接下来要执行程序的要求
-
aaa = '12313'
-
-
assert type(aaa) is int #用于判断是否符合接下来要执行程序的要求
-
print(aaa/2) #断言正确程序继续,错误报错退出AssertionError
参考:http://www.cnblogs.com/alex3714/articles/5227251.html
来源:https://www.cnblogs.com/hq-blog/p/8076238.html