socket 实现并发
一、socket 实现并发
SocketServer是基于socket写成的一个更强大的模块。
SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
在python3中该模块是socketserver
在python2中该模块是Socketserver
1 分情况导入导入模块
2 try:
3 import socketserver #Python 3
4 except ImportError:
5 import SocketServer #Python 2
服务器
服务器要使用处理程序,必须将其出入到服务器对象,定义了5个基本的服务器类型(就是“类”)。BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。注意:BaseServer不直接对外服务。
关系如下:
服务器:
要使用处理程序,必须将其传入到服务器的对象,定义了四个基本的服务器类。
(1)TCPServer(address,handler) 支持使用IPv4的TCP协议的服务器,address是一个(host,port)元组。Handler是BaseRequestHandler或StreamRequestHandler类的子类的实例。
(2)UDPServer(address,handler) 支持使用IPv4的UDP协议的服务器,address和handler与TCPServer中类似。
(3)UnixStreamServer(address,handler) 使用UNIX域套接字实现面向数据流协议的服务器,继承自TCPServer。
(4)UnixDatagramServer(address,handler) 使用UNIX域套接字实现数据报协议的服务器,继承自UDPServer。
这四个类的实例都有以下方法。
1、s.socket 用于传入请求的套接字对象。
2、s.sever_address 监听服务器的地址。如元组("127.0.0.1",80)
3、s.RequestHandlerClass 传递给服务器构造函数并由用户提供的请求处理程序类。
4、s.serve_forever() 处理无限的请求 #无限处理client连接请求
5、s.shutdown() 停止serve_forever()循环
SocketServer模块中主要的有以下几个类:
1、BaseServer 包含服务器的核心功能与混合类(mix-in)的钩子功能。这个类主要用于派生,不要直接生成这个类的类对象,可以考虑使用TCPServer和UDPServer类。
2、TCPServer 基本的网络同步TCP服务器
3、UDPServer 基本的网络同步UDP服务器
4、ForkingTCPServer 是ForkingMixIn与TCPServer的组合
5、ForkingUDPServer 是ForkingMixIn与UDPServer的组合
6、ThreadingUDPServer 是ThreadingMixIn和UDPserver的组合
7、ThreadingTCPServer 是ThreadingMixIn和TCPserver的组合
8、BaseRequestHandler 必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。
9、StreamRequestHandler 实现TCP请求处理类的
10、DatagramRequestHandler 实现UDP请求处理类的
11、ThreadingMixIn 实现了核心的线程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
12、ForkingMixIn 实现了核心的进程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
关系图如下:
创建服务器的步骤:
1:首先必须创建一个请求处理类
2:它是BaseRequestHandler的子类
3:该请求处理类是BaseRequestHandler的子类并重新写其handle()方法
实例化 请求处理类传入服务器地址和请求处理程序类
最后实例化调用serve_forever() #无限处理client请求
记住一个原则:对tcp来说:self.request=conn
示例:
1、tcp_socket_server服务端
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5 #服务端已经实现并发,处理客户端请求
6
7 import socketserver
8
9 class MyServer(socketserver.BaseRequestHandler): #基本的通信循环
10 def handle(self):
11 print('conn is: ',self.request) #与client的链接请求信息
12 print('addr is: ',self.client_address) #获取client的地址和端口号
13 #通信循环
14 while True:
15 #收消息
16 data=self.request.recv(1024)
17 print('收到客户端的消息是',data)
18
19 #发消息
20 self.request.sendall(data.upper())
21
22 if __name__ == '__main__':
23 s=socketserver.ThreadingTCPServer(('127.0.0.1',8000),MyServer) #开启多线程,绑定地址,和处理通信的类
24 s.serve_forever() #连接循环
tcp_socket_client客户端
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5 from socket import *
6 ip_port=('127.0.0.1',8000)
7 back_log=5
8 buffer_size=1024
9
10 tcp_client=socket(AF_INET,SOCK_STREAM)
11 tcp_client.connect(ip_port)
12
13 while True:
14 msg=input('>>: ').strip()
15 if not msg:continue
16 if msg == 'quit':break
17
18 tcp_client.send(msg.encode('utf-8'))
19
20 data=tcp_client.recv(buffer_size)
21 print('收到服务端发来的消息: ',data.decode('utf-8'))
22
23 tcp_client.close()
执行结果:
开启一个服务端程序,再开多个客户端,向服务器发送命令:
1 #客户端1
2 >>: hello #输入要发送的消息
3 收到服务端发来的消息: HELLO
4
5 #客户端2
6 >>: word
7 收到服务端发来的消息: WORD
8
9 #服务端
10 conn is: <socket.socket fd=412, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 62813)>
11 addr is: ('127.0.0.1', 62813)
12 收到客户端的消息是 b'hello' #客户端收到的消息
13
14 conn is: <socket.socket fd=256, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000), raddr=('127.0.0.1', 62816)>
15 addr is: ('127.0.0.1', 62816)
16 收到客户端的消息是 b'word'
2、udp实现并发
记住一个原则:对udp来说:self.request=(client_data_bytes,udp的套接字对象)
实例:
udp_socket_server服务端:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5 import socketserver
6
7 class MyServer(socketserver.BaseRequestHandler):
8 def handle(self):
9 print(self.request)
10 print('收到客户端的消息是',self.request[0])
11 self.request[1].sendto(self.request[0].upper(),self.client_address) #发送的是第1个消息,第2个地址
12
13
14 if __name__ == '__main__':
15 s=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer) #多线程
16 s.serve_forever()
udp_socket_client客户端:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5 from socket import *
6 ip_port=('127.0.0.1',8080)
7 buffer_size=1024
8
9 udp_client=socket(AF_INET,SOCK_DGRAM) #数据报
10
11 while True:
12 msg=input('>>: ').strip()
13 udp_client.sendto(msg.encode('utf-8'),ip_port)
14
15 data,addr=udp_client.recvfrom(buffer_size)
16 # print(data.decode('utf-8'))
17 print(data)
执行结果:
先启动服务端,再开多个客户端,向服务端发送消息。
1 #客户端
2 >>: welcome #输入要发送的消息
3 b'WELCOME'
4
5 >>: hello
6 b'HELLO'
7 >>:
8
9 #服务端
10 (b'welcome', <socket.socket fd=388, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
11 收到客户端的消息是 b'welcome' #服务端接收到的消息
12
13 (b'hello', <socket.socket fd=388, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
14 收到客户端的消息是 b'hello'
二、认证客户端的链接合法性
如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现
tcp_socket_server服务端:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5
6 from socket import *
7 import hmac,os
8
9 secret_key=b'linhaifeng bang bang bang' #加段代码(加盐)
10 def conn_auth(conn):
11 '''
12 认证客户端链接
13 :param conn:
14 :return:
15 '''
16 print('开始验证新链接的合法性')
17 msg=os.urandom(32)
18 conn.sendall(msg)
19 h=hmac.new(secret_key,msg)
20 digest=h.digest()
21 respone=conn.recv(len(digest))
22 return hmac.compare_digest(respone,digest)
23
24 def data_handler(conn,bufsize=1024):
25 if not conn_auth(conn):
26 print('该链接不合法,关闭')
27 conn.close()
28 return
29 print('链接合法,开始通信')
30 while True:
31 data=conn.recv(bufsize)
32 if not data:break
33 conn.sendall(data.upper())
34
35 def server_handler(ip_port,bufsize,backlog=5):
36 '''
37 只处理链接
38 :param ip_port:
39 :return:
40 '''
41 tcp_socket_server=socket(AF_INET,SOCK_STREAM)
42 tcp_socket_server.bind(ip_port)
43 tcp_socket_server.listen(backlog)
44 while True:
45 conn,addr=tcp_socket_server.accept()
46 print('新连接[%s:%s]' %(addr[0],addr[1]))
47 data_handler(conn,bufsize)
48
49 if __name__ == '__main__':
50 ip_port=('127.0.0.1',9999)
51 bufsize=1024
52 server_handler(ip_port,bufsize)
tcp_socket_client客户端:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3 #Author: nulige
4
5 from socket import *
6 import hmac,os
7
8 secret_key=b'linhaifeng bang bang bang' #加盐
9 def conn_auth(conn):
10 '''
11 验证客户端到服务器的链接
12 :param conn:
13 :return:
14 '''
15 msg=conn.recv(32)
16 h=hmac.new(secret_key,msg)
17 digest=h.digest()
18 conn.sendall(digest)
19
20 def client_handler(ip_port,bufsize=1024):
21 tcp_socket_client=socket(AF_INET,SOCK_STREAM)
22 tcp_socket_client.connect(ip_port)
23
24 conn_auth(tcp_socket_client)
25
26 while True:
27 data=input('>>: ').strip()
28 if not data:continue
29 if data == 'quit':break
30
31 tcp_socket_client.sendall(data.encode('utf-8'))
32 respone=tcp_socket_client.recv(bufsize)
33 print(respone.decode('utf-8'))
34 tcp_socket_client.close()
35
36 if __name__ == '__main__':
37 ip_port=('127.0.0.1',9999)
38 bufsize=1024
39 client_handler(ip_port,bufsize)