Python——网络编程(二)socket进阶2
#socketserver模块实现并发
socketserver就是一个py文件,可以ctrl进去看看都是如何实现的
socketserver的两个最基本的类:
(以下箭头代表继承关系)
-
server类:用于处理链接
分为以下五种:
右边两个代表专用于Unix的TCP和UDPservice,一般不用
- request类: 用于处理通信
-
其余类都是继承以上两大类(注意继承的优先级是由左到右递减)
“ThreadingUDPServer", "ThreadingTCPServer", "ThreadingMixIn"
"ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"
1 import socketserver 2 3 buffer_size = 1024 4 ip_port = ('222.195.137.208', 8000) 5 6 #需要继承socketserver.BaseRequestHandler类 7 class MyServer(socketserver.BaseRequestHandler): 8 9 #一定需要自己重新定义handle 10 def handle(self): 11 print(self.request) #相当于原来的conn 12 print(self.client_address) #相当于原来的addr 13 14 #通讯循环 15 while True: 16 try: 17 # 接收客户端消息 18 data = self.request.recv(buffer_size) 19 20 if not data:break 21 print('客户端发来的消息: ', data.decode('utf8')) 22 23 #发送消息 24 self.request.sendall(data.upper()) 25 except Exception as e: 26 print(e) 27 break 28 29 30 if __name__ == '__main__': 31 #链接循环 32 33 # 创建多线程服务端实例(来一个链接创建一个实例,进行通信),其中MyServer代表的就是通信循环 34 s = socketserver.ThreadingTCPServer(ip_port, MyServer) 35 #这代表'永远服务',也就是链接循环 36 s.serve_forever()
1 from socket import * 2 3 ip_port = ('222.195.137.208', 8000) 4 buffer_size = 1024 5 6 client = socket(AF_INET, SOCK_STREAM) 7 print('连接中...') 8 client.connect(ip_port) 9 print('连接成功: %s!' %ip_port[0]) 10 11 while True: 12 msg = input('>>') 13 14 if not msg: continue 15 if msg == 'exit': break 16 client.send(msg.encode('utf8')) 17 18 data = client.recv(buffer_size) 19 20 print('收到消息:\n%s' %data.decode('utf8')) 21 22 client.close()
1 import socketserver 2 3 buffer_size = 1024 4 ip_port = ('222.195.137.208', 8000) 5 6 class MyServer(socketserver.BaseRequestHandler): 7 8 def handle(self): 9 print(self.request) #相当于原来的(接收的数据data, udp的套接字对象) --》 元组形式 10 print(self.client_address) #相当于原来的addr 11 #和tcp不同的是,这里不需要再通讯循环了,udp的serverforever帮你做了 12 try: 13 #由于消息已经被接收到了self.request里,这里无需再接收 14 data = self.request[0] 15 16 print('客户端发来的消息: ', data.decode('utf8')) 17 18 #发送消息 19 self.request[1].sendto(data.upper(), self.client_address) 20 except Exception as e: 21 print(e) 22 23 if __name__ == '__main__': 24 25 s = socketserver.ThreadingUDPServer(ip_port, MyServer) 26 s.serve_forever()
1 from socket import * 2 3 ip_port = ('222.195.137.208', 8000) 4 buffer_size = 1024 5 6 client = socket(AF_INET, SOCK_DGRAM) 7 8 while True: 9 msg = input('>>') 10 11 if not msg: continue 12 if msg == 'exit': break 13 client.sendto(msg.encode('utf8'), ip_port) 14 15 data, addr = client.recvfrom(buffer_size) 16 17 print('收到消息:\n%s' %data.decode('utf8')) 18 19 client.close()
如果需要使地址重用的话,加上这句
#验证客户端链接合法性
先介绍两个模块:
os.urandom():
函数定位: Return a string of n random bytes suitable for cryptographic use.
意思就是,返回一个有n个byte那么长的一个string,然后很适合用于加密
hmac
和hashlib模块类似,主要介绍以下几个方法
1 import hmac, os 2 #产生32位的随机bytes 3 msg = os.urandom(32) 4 #设置密匙 5 secret_key = b'wwj' 6 #产生加密信息 7 h1 = hmac.new(secret_key, msg) 8 m1 = h1.digest() 9 10 h2 = hmac.new(msg) 11 m2 = h2.digest() 12 13 #比较两个加密信息是否一致 14 print(hmac.compare_digest(m1, m2))
#验证客户端链接合法性思路
1.服务端给客户端发送指定位数的随机信息
1.客户端收到此随机信息
2.服务端对此信息进行添加密匙的加密
2.客户端对此信息进行添加密匙的加密
3.客户端发送加密信息给服务端
3.服务度收到客户端的加密信息,与自己的加密信息进行比对,相同则认证通过
1 import hmac, os 2 3 msg = os.urandom(32) 4 secret_key = b'wwj' 5 6 conn.sendall(msg) 7 h = hmac.new(secret_key, msg) 8 digest = h.digest() 9 respone = conn.recv(len(digest)) 10 11 print(hmac.compare_digest(digest, respone))
1 import hmac, os 2 3 secret_key = b'wwj' 4 msg = conn.recv(32) 5 h = hmac.new(secret_key, msg) 6 digest = h.digest() 7 conn.sendall(digest)
这样想要合法连接,需要:
1. 知道服务端的认证机制
2. 知道客户端的secret_key