在django中运行socket server

今天碰到一个棘手的问题,项目需要通过GPS向服务器上传设备信号,只能通过Socket连接进行传输,而不能通过HTTP协议上传。而我的服务器后台用的是django,那么如何在django项目中嵌入Socket监听服务呢?

一开始的思路是通过多线程在django的启动脚本中启动socket,但是发现一旦启动脚本就会报错:
[98] Address already in use.

看来同一进程中不能多次绑定socket连接,那么只有通过单独写一个进程来监听了。

SocketServer 监听脚本

方便起见,利用SocketServer库来创建Socket服务:

1
2
3
4
5
6
7
8
9
10
11
#定义socket handler类
class GpsTCPHandler(SocketServer.BaseRequestHandler):
#重写handler方法
def handle(self):
pass
if __name__=="__main__":
HOST,PORT = "0.0.0.0",9000
server = SocketServer.TCPServer((HOST,PORT),GpsTCPHandler)
server.serve_forever()

脚本中嵌入django ORM

我们在接收到socket上传的数据后需要对后台业务进行处理,但是,脚本不是在django项目中,那么我们如何不写sql而直接改掉django的数据呢?幸运的是django支持外部直接调用ORM方法而不用创建一个django项目。

我们在django项目目录下新建一个socket脚本,然后写入如下代码:

1
2
3
4
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "yunshan_order.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

首先需要设置django setting,然后通过get_wsgi_application方法启动django 应用(1.9.X版本必须)。注意这块代码要放置到业务逻辑代码之前。

把python脚本设置为服务

ubuntu 16.04的服务用的是systemctl工具,需要在/lib/systemd/system下创建一个新的服务ysting.service

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=ysting
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
ExecStart=/usr/bin/python /home/kevin/codes/Yunshan/yunshan_order/gpsocket.py
ExecStop = /usr/bin/python
PrivateTmp=true
[Install]
WantedBy=multi-user.target

然后执行
systemctl daemon-reload

这样就可以通过service的方式启动socket服务了。

设置脚本为后台服务

按照上述方法设置为服务后有个问题,服务进程会在那里一直等待。我们需要将它设置为后台进程,不随shell的结束而结束。

创建脚本文件 gps.sh:

1
2
3
cd /home/kevin/codes/Yunshan/yunshan_order
setsid python gpscoket.py &
exit 0

setsid 是将进程的父进程设置为init,&是转到后台。

socket.error: [Errno 104] Connection reset by peer

服务器启动一段时间后发现经常出现 Connection reset by peer的问题,解决方法是将TCPServer替换为ThreadingTCPServer:

server = SocketServer.ThreadingTCPServer((HOST,PORT),GpsTCPHandler)