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不直接对外服务。

 关系如下:

python 文件 buffer对象 python socket buffer_python 文件 buffer对象

 服务器:

  要使用处理程序,必须将其传入到服务器的对象,定义了四个基本的服务器类。

(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),以提供一些异步特性。不要直接生成这个类的对象。

 关系图如下:

python 文件 buffer对象 python socket buffer_服务器_02

python 文件 buffer对象 python socket buffer_python 文件 buffer对象_03

创建服务器的步骤:

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)