目录
一、反射
二、socket
三、socketserver
一、反射
python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'wupeiqi' 5 6 def func(self): 7 return 'func' 8 9 obj = Foo() 10 11 # #### 检查是否含有成员 #### 12 hasattr(obj, 'name') 13 hasattr(obj, 'func') 14 15 # #### 获取成员 #### 16 getattr(obj, 'name') 17 getattr(obj, 'func') 18 19 # #### 设置成员 #### 20 setattr(obj, 'age', 18) 21 setattr(obj, 'show', lambda num: num + 1) 22 23 # #### 删除成员 #### 24 delattr(obj, 'name') 25 delattr(obj, 'func')
详细解析:
当我们要访问一个对象的成员时,应该是这样操作:
1 class Foo(object):
2 3 def __init__(self): 4 self.name = 'tom' 5 6 def func(self): 7 return 'func'
8 9 obj = Foo() 10 11 # 访问字段 12 obj.name 13 # 执行方法 14 obj.func()
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'tom' 5 6 # 不允许使用 obj.name 7 obj = Foo()
答:有两种方法,如下:
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'alex' 5 6 def func(self): 7 return 'func' 8 9 # 不允许使用 obj.name 10 obj = Foo() 11 12 print obj.__dict__['name']
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'alex' 5 6 def func(self): 7 return 'func' 8 9 # 不允许使用 obj.name 10 obj = Foo() 11 12 print getattr(obj, 'name')
d、比较三种访问方式
- obj.name
- obj.__dict__['name']
- getattr(obj, 'name')
答:第一种和其他种比,...
第二种和第三种比,...
结论:反射是通过字符串的形式操作对象相关的成员。一切事物都是对象!!!
1 import sys 2 3 class WebServer(object): 4 def __init__(self,host,port): 5 self.host = host 6 self.port = port 7 8 def start(self): 9 print('start') 10 11 def stop(self): 12 print("stop") 13 14 def restart(self): 15 self.stop() 16 self.start() 17 18 if __name__ == "__main__": 19 server = WebServer('localhost',333) 20 if hasattr(server,sys.argv[1]): 21 func = getattr(server,sys.argv[1]) 22 func()
类也是对象
1 class Foo(object): 2 3 staticField = "old boy" 4 5 def __init__(self): 6 self.name = 'wupeiqi' 7 8 def func(self): 9 return 'func' 10 11 @staticmethod 12 def bar(): 13 return 'bar' 14 15 print getattr(Foo, 'staticField') 16 print getattr(Foo, 'func') 17 print getattr(Foo, 'bar')
模块也是对象
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def dev(): 5 return 'dev'
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 """ 5 程序目录: 6 home.py 7 index.py 8 9 当前文件: 10 index.py 11 """ 12 13 14 import home as obj 15 16 #obj.dev() 17 18 func = getattr(obj, 'dev') 19 func()
二、socket
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
socket服务端与客户端交流示意图:
socket()模块函数
使用socket.socket()函数来创建套接字,语法如下:
socket(socket_family,socket_type,protocol=0)
如前所述,参数一:socket_family(地址簇)
socket.AF_UNIX 只能用于单一的UNIX系统进程间通信
socket.AF_INET IPV4(默认)
socket.AF_INET6 IPV6
参数二:socket_type(类型)
socket.SOCK_STREAM 流式socket , for TCP (默认)
socket.SOCK_DGRAM 数据报式socket , for UDPsocket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET 可靠的连续数据包服务参数三:protocol(协议)
0 (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
创建一个TCP/IP的套接字,你要这样调用socket.socket():
tcpsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
同样的,创建一个UDP/IP的套接字,你要这样:
udpsocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
套接字对象(内建)方法:
函数 | 描述 |
服务器套接字函数 | |
s.bind(address) | 绑定地址(主机名,端口号对)到套接字 |
s.listen(backlog) | 开始TCP监听 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待链接的到来 |
客户端套接字函数 | |
s.connect() | 主动初始化TCP服务器连接 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
公共用途的套接字函数 | |
s.recv() | 接收TCP数据 |
s.send() | 发送TCP数据 |
s.sendall() | 完整发送TCP数据(不停调用s.send()) |
s.recvfrom() | 接收UDP数据 |
s.sendto() | 发送UDP数据 |
s.getpeername() | 链接到当前套接字的远端的地址(TCP连接) |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回指定套接字的参数 |
s.setsockopt() | 设置指定套接字的参数 |
s.close() | 关闭套接字 |
面向模块的套接字函数 | |
s.setblocking() | 设置套接字的阻塞与非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
s.gettimeout() | 得到阻塞套接字操作的超时时间 |
面向文件的套接字函数 | |
s.fileno() | 套接字的文件描述 |
s.makefile() | 创建一个与该套接字相关联的文件对象 |
例:(win环境)
server端
#!/usr/bin/env python # -*- coding:utf-8 -*- 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.sendall(bytes('不要回答,不要回答,不要回答','utf8')) conn.close()
client端
#!/usr/bin/env python # -*- coding:utf-8 -*- 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()
连续交互通信实例:
server端:(win环境)
#!/usr/bin/env python # -*- coding:utf-8 -*- 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.sendall(bytes('不要回答,不要回答,不要回答','utf8')) while True: try:
client_data = conn.recv(1024) print(str(client_data,'utf8')) server_response = input('>>>:').strip() except Exception:
print("client closed,break")
break
conn.send(bytes(server_response,'utf8'))
conn.close()
server端:(Linux环境)
#!/usr/bin/env python # -*- coding:utf-8 -*- 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.sendall(bytes('不要回答,不要回答,不要回答','utf8')) while True: client_data = conn.recv(1024) print(str(client_data,'utf8'))
server_response = input('>>>:').strip()
if not client_data:break conn.send(server_response) conn.close()
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- 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()
模拟实现简单ssh功能:
server端:
#!/usr/bin/env python # -*- coding:utf-8 -*- 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() while True: client_data = conn.recv(1024) if not client_data:break print("recv cmd:",str(client_data,'utf8')) cmd = str(client_data,'utf8').strip() cmd_call = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE) cmd_result = cmd_call.stdout.read() if len(cmd_result) == 0: cmd_result = b"cmd execution has no output..." ack_msg = bytes("CMD_RESULT_SIZE|%s"%len(cmd_result),"utf8") conn.send(ack_msg) client_ack = conn.recv(50) if client_ack.decode() == "CLIENT_READY_TO_RECV": conn.send(cmd_result) conn.close()
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',9999) sk = socket.socket() sk.connect(ip_port) while True: user_input = input("cmd:").strip() if len(user_input) == 0:continue if user_input == "q":break sk.send(bytes(user_input,'utf8')) #ack_msg = b"CMD_RESULT_SIZE|%s",len(cmd_result) server_ack_msg = sk.recv(100) cmd_res_msg = str(server_ack_msg.decode()).split("|") print("server response:",cmd_res_msg) if cmd_res_msg[0] == "CMD_RESULT_SIZE": cmd_res_size = int(cmd_res_msg[1]) sk.send(b"CLIENT_READY_TO_RECV") res = '' received_size = 0 while received_size < cmd_res_size: data = sk.recv(500) received_size += len(data) res += str(data.decode()) else: print(str(res)) print("----------recv doen----------") sk.close()
三、sockeserver
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
例:
server端:
#!/usr/bin/env python # -*- coding:utf-8 -*- import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def setup(self): print("Building secure connection chanel...") def handle(self): print("New Conn:",self.client_address) while True: data = self.request.recv(1024) if not data:break print("Client Says:",data.decode()) self.request.send(data) def finish(self): print("client conn is done...") if __name__ == '__main__': HOST, PORT = "localhost", 50007 # 把刚才写的类当作一个参数传给ThreadingTCPServer这个类,下面的代码就创建了一个多线程socket server server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 启动这个server,这个server会一直运行,除非按ctrl-C停止 server.serve_forever()
client端:
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',50007) sk = socket.socket() sk.connect(ip_port) while True: msg = input(">>:").strip() sk.sendall(bytes(msg,'utf8')) server_reply = sk.recv(1024) print("Server Reply:",str(server_reply,'utf8')) sk.close()