燃料电池汽车远程监控中服务器的设计和实现
2018-11-06许建淼马天才XuJianmiaoMaTiancai
许建淼,马天才 Xu Jianmiao,Ma Tiancai
燃料电池汽车远程监控中服务器的设计和实现
许建淼,马天才 Xu Jianmiao,Ma Tiancai
(同济大学 汽车学院,上海 201804)
为满足燃料电池汽车远程监控中的车载终端和服务器通信的基本要求,以SocketServer库为基础,基于Python语言开发实现了服务器和车载终端的多线程非堵塞通信;并在此基础上加入密码验证和IP黑名单,满足了服务器的基本安全需要;加入超时连接处理功能进行了性能优化;利用Threading库设计了车载终端管理台,实现了服务器对车载终端的主动控制。
非堵塞多线程通信;安全通信;车载终端管理台;汽车远程车载监控
0 引 言
我国燃料电池汽车正处于商业化示范运行考核与应用阶段[1]。为实现“十三五”规划中提出的“燃料电池汽车要产业化,到2020年要实现燃料电池车批量生产和规模化示范应用”的目标,国家近几年大力扶持相关企业。为了规范市场,国家新能源补贴政策里明确规定燃料电池汽车的在线远程监控是强制必要的。因此,如何实现燃料电池汽车的远程监控是实现燃料电池汽车市场化的主要问题之一。
物联网可将虚拟世界与物理世界结合,通过数字信息可以控制物理世界关键设备甚至基础设备[2]。安全问题在新兴物联网中并未得到广泛关注,但特别重要,这在燃料电池汽车的远程监控中更为突出。所以,在此不拘于实现通信功能,也考虑加入安全功能完善监控体系。
Python是一门高级编程语言,SocketServer库是Python中基于Socket库的扩展库。Socket是TCP/IP协议的基本封装接口。汽车车载终端考虑到硬件成本和自身的空间大小,系统一般都比较精简,只支持一些底层网络服务。因此,Python中SocketServer库特别适合车载终端和服务器通信的程序开发,所以以此为基础开发Socket服务器。
1 总体构架
总体架构分为本地端和服务器端。本地端是车载终端;服务器端为非堵塞多线程通信和安全通信的Socket服务器和车载终端管理器。非堵塞多线程通信和安全通信的Socket服务器可以满足多台车载终端即时向服务器进行数据传输,服务器接收到数据后,会对车载终端回发应答,通信过程中有IP黑名单和密码验证两种安全手段,在一定程度上保障服务器的安全性;在服务器上管理员可以主动选择车载终端建立连接,对选定的车载终端发送指令进行远程控制,便于管理员应对车载终端的一系列意外情况,保证车载终端和服务器的通信稳定和安全。具体如图1所示。
图1 总体构架图
2 Socket服务器的设计和实现
Socket服务器主要有两种功能:非堵塞多线程通信功能和安全通信功能。分别介绍两种功能的具体设计和实现。
2.1 非堵塞多线程通信功能的设计和实现
2.1.1 非堵塞式通信功能
应用进程在调用接收函数接收报文时,在阻塞模式下,若没有到达的数据,则调用将一直阻塞直到有数据到达或出错;而在非阻塞模式下,将直接返回而不需等待。因此,非堵塞式通信相较于堵塞式通信,可以充分利用服务器的资源快速处理各个车载终端的连接,提高通信效率。
Python中Select库是对底层操作系统的直接访问的接口,可以监控sockets、files和pipes,等待IO完成,当有可读、可写或者异常事件产生时,可以做出响应。所以,使用Select库监听sockets,可以较容易地实现非堵塞通信。
with _ServerSelector() as selector:
selector.register(self,selectors.EVENT_ READ)
while not self._shutdown_request:
ready = selector.select(poll_interval)
if ready:
self._handle_request_noblock()
其中,poll_interval为select循环执行时间,程序中poll_interval=0.5,即每0.5 s对sockets进行一次监听,其他时间释放资源。当有车载终端接触服务器,select在下一个0.5 s后执行时监听到数据,立即响应执行下一步操作即执行self._handle_request_noblock函数。由此实现非堵塞式通信。
2.1.2 多线程通信功能
线程是系统分配处理器时间的基本单元,一个进程中可以有多个线程同时执行代码,实际上,同一时间只有一个线程在运行,处理器通过分时在各个线程间频繁切换,因为时间间隔很短,所以感觉是多线程同时进行。多线程有利于充分利用服务器的资源。
考虑到服务器的服务对象车载终端有多台,设计多线程通信符合项目的实际需求。多线程实现是依靠SocketServer库中的ThreadingMixIn类(如下代码),对BaseServer中的process_request进行重写,在其中引入Threading库,将每一个process_request都独立成一个单独的线程,并在程序后端RequestHandlerClass的handle函数内采用while循环,将通过验证的合法车载终端的连接线程保留下来,从而实现多线程通信功能。
class ThreadingMixIn
def process_request(self,request,client_ address):
t=threading.Thread(target=self.process_request_thread,args=(request,client_address))
t.daemon=self.daemon_threads
t.start()
2.1.3 整体功能
1)SocketServer库的机理说明。
实现最基础通信的核心在于SocketServer中的BaseServer,它需要2个实参server_ip和RequestHandlerClass。server_ip为本服务器的地址和开放端口;RequestHandlerClass为车载终端接触服务器后的主要处理方法集。BaseServer有两大方法:handler_request和server_forever。在此采用server_forever,其中包含select函数,实现了非堵塞连接。当车载终端与服务器接触时,select会立即做出响应,调用_handle_request_noblock函数。在_handle_request_noblock函数中起主要作用的是verify_request和finish_request方法,前者在安全通信中用于安全验证,后者调用RequestHand- lerClass,执行开发者设计的一系列操作。finish_ request执行完成后调用shutdown_ request退出,至此整个流程完成。具体程序运行流程如图2所示,从左至右为代码的层次结构,从上到下为代码的执行前后。
因为要求即时通信,同时具有一定的可靠性,不允许数据传输中丢包,对数据的顺序也有要求,所以采用SocketServer库中的TCPServer。TCPServer类是对BaseServer的继承,定义了Socket的地址簇、套接字类型和协议,并且提供了get_request方法(在BaseServer中_handle_ request_noblock中必须调用的方法),对Base Server在TCP中的运用进行了完善,满足TCP通信要求。
图2 程序流程图
2)SocketServer库的实现过程。
SocketServer库中提供给用户BaseReques- tHandler方法,用于方便编写BaseServer所需的RequestHandlerClass实参。在此首先创建Threaded TCPRequestHandler类继承SocketServer.BaseRequest Handler,并对其中setup,handle,finish函数进行重写。setup是开始阶段服务器对尝试连接的车载终端进行地址解析,并设置套接字的超时值。handle是BaseRequestHandler内的核心代码,主要包括服务器处理车载终端的连接的一系列行为,对尝试连接的车载终端进行密码验证;未通过密码验证则主动断开客户端的连接,通过密码验证后服务器保留此线程,并接受车载终端传来的数据并存入数据库;服务器对超时未响应的车载终端采取主动断开连接;服务器对车载终端发出的指令采取相应的响应(接收到车载终端发出exit后,服务器主动断开与之连接,释放出此线程资源)。finish是结束阶段服务器与车载终端断开连接后的相应行为。
接下来采用ThreadedTCPServer类对Socket Server. TCPServer和ThreadingMixIn进行继承,具体代码如下。其中重写了verify_request函数,即根据IP黑名单过滤掉非法IP,不允许其与服务器尝试连接,保障服务器的安全(具体介绍在2.2安全通信功能的设计和实现)。
最后在主程序中将本服务器的IP地址、开放端口号和ThreadedTCPRequestHandler代入ThreadedTCPServer完成配置,然后再使用ThreadedTCPServer. server_forever既可(具体参照如下代码前两行)。至此基本上完成了非堵塞多线程通信的整体 功能。
class ThreadedTCPServer(SocketServer. Threading MixIn,SocketServer.TCPServer):
def verify_request(self,request,client_ address):
client_ip = client_address[0]
if client_ip in illegal_ip:
print(client_ip+"为非法IP无法连接服务器")
return False
else:
return True
2.2 安全通信功能的设计和实现
安全通信功能主要依靠密码验证和IP黑名单,两者相辅相成,共同实现服务器的通信安全,如图3所示。
图3 安全通信实现简图
2.2.1 IP黑名单
IP黑名单为第1层前端验证。具体作用是将密码验证中出现5次以上错误的IP放置于IP黑名单,禁止此IP在一定时间内连接服务器,有效地防止不法人员对服务器进行暴力猜解攻击,在一定程度上保障服务器的安全。具体实现在verify_request函数中,对BaseServer类中的verify_request函数进行重写(具体参照上段代码),对illegal_ip中的IP验证过滤,拒绝和非法车载终端连接。此函数在_handle_request_noblock中起作用,即车载终端和服务器一开始接触时执行。
2.2.2 密码验证
密码验证为第2层后端验证。在handle函数中首先判断接入IP是否在legal_ip列表,如果在,车载终端和服务器成功建立连接,如果不在,调用add_illegal_ip函数进行验证。在此函数中进行密码验证:车载终端传入服务器的第1波数据被要求必须上传规定格式的账号密码,然后此数据被引入check_ip函数中进行验证,check_ip会对比数据库中用户原登记的数据,对传入的账号密码验证正误。通过验证的车载终端IP加入legal_ip列表中,成为合法用户,服务器将和它建立并保持连接,而未通过验证的车载终端,服务器会主动和它断开连接,并且该IP会被记录在error_ip字典中,此字典记录了错误的IP和错误次数,当验证错误次数达到5次时,该IP会被加入到illegal_ip中,即IP黑名单,illegal_ip会在前端的verify_request中被调用查询。
3 车载终端管理台的设计和实现
车载终端管理台的设计思路是把非堵塞多线程通信和安全通信通过Threading库集成在一个线程中,并将服务器对车载终端的主动控制代码作为一个线程独立开,以此作为主线程(代码如下)。其本质是主动对已经建立连接的多个socket的选择,从而实现与车载终端有区别的双向通信。具体操作为在管理台上主动选择想要控制的车载终端的端口号,然后输入想要发送的指令,最后发送此指令到选定的车载终端中。至此,已经实现了服务器与车载终端非堵塞多线程通信、安全通信和主动管理车载终端等一系列功能。
server = ThreadedTCPServer((HOST,PORT),Threaded TCPRequestHandler)
server_thread = threading.Thread(target=server. server_forever)
server_thread.daemon = True
server_thread.start()
while True:
time.sleep(2)
want_port=input('请输入您想要建立连接的端口号:')
if want_port in client_port:
inp=input('想要发送的指令是:')
message=inp.encode(encoding="utf-8")
client_socket[client_port.index(want_port)].sendall(message)
else:
print(want_port+'端口无车载终端连接')
4 其他优化功能的设计和实现
为了使服务器资源合理分配,设置了超时连接处理,避免服务器为长时间未响应的车载终端消耗资源。功能的实现主要基于套接字的内置方法settimeout,即先于BaseRequestHandler的setup函数内调用self.request的settimeout,并代入设置好的timeout,最后在handle函数中调用如下代码,从而实现这一功能。
try:
data = self.request.recv(1024)
except socket.timeout:
print(' '+self.ip+":"+str(self.port)+"接收超时,即将断开连接")
break
5 验证测试
为了验证服务器功能的可行性和稳定性,搭建测试环境并进行实际测试,车载终端采用STM32主控芯片,通过SIM7100C 4G芯片实现数据的采集和发送;服务器是腾讯云服务器。通过一定时间的跟踪测试发现,开发的服务器性能稳定,并且基本满足了燃料电池汽车在线远程监控的所需功能。
以下为各个测试的效果。
1)Socket服务器的验证。
(1)安全通信测试
车载终端发送错误密码后服务器的反应为
('58.41.201.173',17360)尝试连接中
58.41.201.173非法连接次数:1
58.41.201.173:17360已断开连接!
车载终端发送错误密码5次后服务器的反 应为
('58.41.201.173',17365)尝试连接中
58.41.201.173非法连接次数:5
58.41.201.173:17365已断开连接!
58.41.201.173为非法IP无法连接服务器
车载终端发送正确密码后服务器的反应为
('58.41.201.173',17379)尝试连接中
legal_IP['58.41.201.173']
('58.41.201.173',17379)已经成功连接
(2)非堵塞多线程通信功能的验证
单台车载终端向服务器发送消息时服务器的反应为
('58.41.201.173',17379)已经成功连接
从17379接收到的信息是:tongji
从17379接收到的信息是:automobile
多台车载终端向服务器发送消息时服务器的反应为
('180.160.22.100',1143)尝试连接中
legal_IP['180.160.22.100']
('180.160.22.100',1143)已经成功连接
('222.66.186.53',33812)尝试连接中
legal_IP['180.160.22.100','222.66.186.53']
('222.66.186.53',33812)已经成功连接
从33812接收到的信息是:tongji
从1143接收到的信息是:automobile
从1143接收到的信息是:tongji
从33812接收到的信息是:automobile
2)车载终端管理台的验证。
服务器端的操作为
请输入您想要建立连接的端口号:18210
想要发送的指令是:shutdown
车载终端接收的消息为
从服务器接收到的指令:b'shutdown'
本次是通过车载终端将其从服务器接收到的信息再发回到服务器上显示从而验证了测试。
3)超时处理功能的验证。
超过预定时间,服务器与车载终端主动断开连接为
('58.41.201.173',18241)尝试连接中
legal_IP['58.41.201.173']
('58.41.201.173', 18241)已经成功连接
58.41.201.173:18241接收超时,即将断开连接
58.41.201.173:18241已断开连接!
6 结束语
1)基于Python中的SocketServer库开发了一个非堵塞多线程的Socket服务器,详细地介绍了设计和实现过程,主要核心点有BaseServer类、server_forever函数、select库、BaseRequestHandle类,可以满足汽车远程车载监控项目中多台汽车车载终端和服务器数据传输的基本功能。
2)将互联网中经典的密码验证和IP黑名单等安全手段运用到燃料电池汽车远程监控中,对其设计和实现过程进行了详细的介绍,主要核心点有verify_request函数和check_ip函数,在一定程度上保障了服务器的通信安全。这为以后基于SocketServer的物联网安全研究提供了参考。
3)车载终端管理台的初步实现,使用了Threading进行线程管理,独立出一个线程进行socket有选择地双向连接,从而简单有效地实现需要的功能,经过测试,初步满足项目功能,有待进一步完善。
4)为了验证服务器功能,搭建了真实测试环境进行跟踪测试,证明服务器功能的稳定性和可靠性。
[1]《中国公路学报》编辑部. 中国汽车工程学术研究综述·2017[J]. 中国公路学报,2017,30(6):1-197.
[2]武传坤. 中国的物联网安全:技术发展与政策建议[J]. 人民论坛·学术前沿,2016(17):47-58.
2018-06-19
1002-4581(2018)05-0001-05
U469.7
A
10.14175/j.issn.1002-4581.2018.05.001