前几天公司有个业务,需要用 Python 在服务器上搭建一个 socket 服务,与 Django 开发的系统配合使用。
那么问题来了, 如果单纯地使用 socket 来搭建一个服务,如果用户访问页面,socket 开启的端口就会被占用,也就是说,一次只能运行一个连接。
那么如何实现允许多个用户同时连接到这个 socket 服务呢,网上找了一些方法,其中我觉得较为简单和熟悉的一种就是使用多线程。
以下是简单的代码框架:
from socket import *
import _thread
HOST = ""
PORT = 8588
BUFSIZ = 1024 #缓冲区大小
ADDR = (HOST,PORT)
tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(60) #连接被转接或者被拒绝之前,传入连接请求的最大数
while True:
tcpCliSock, addr = tcpSerSock.accept()
print("waiting for connect ...")
print("...connect from:", addr)
_thread.start_new_thread(getData, (tcpCliSock, ))
continue
其中,关于 socket 第三方库的使用方法就不多说了,提请注意的一点是 _thread 这个库。
_thread:
在 Python3 版本中,只有 threading 这个库了,但是为了兼容废弃的 thread 这个库,就搞了个这个库出来,在这里用到的方法还算简单,但推荐的还是使用 threading 这个库,说不定哪天改版又被兼容和废弃了。
_thread.start_new_thread(func, (arg1,))
这样就算开启了一个新的线程,函数和参数的调用方法和 threading 是一样的,多线程这个方法很有用,打算以后有时间可以简单梳理一下,在这里知道这样调用就 OK 了。
线程报错的问题:
首先说一下这个 socket 服务的主要内容,是通过用户连接,然后传输一些字节的信息,信息分为两部分,一部分是头部信息,一部分是主体信息。
头部信息长度是固定的,会包含整个信息的长度之类的内容;
主体信息则是我们需要获取的信息内容。
在解析这些信息的时候,我们会用到 Python 的第三方库叫做 struct ,这个库用来 pack 和 unpack tcp 传输过来的信息,这是一个很重要的 第三方库,mark 下,以后还会详细介绍它,但不是今天的重点。
重点是,在我们这个获取的函数里,发生了报错怎么办?
最开始的我的函数是这样写的:
def getData(tcpCliSock):
while True:
doing something
tcpCliSock.close()
_thread.stop()
在函数内部,一旦报错,跟踪的实时流就会卡在报错的地方不再动了。
但这不是我们想要的,所以,在这个函数里面,加了个 try except 来获取报错,并进行处理,退出这个 while 循环。
def getData(tcpCliSock):
while True:
try:
doing something
except:
break
tcpCliSock.close()
_thread.stop()
如果需要记录,在 except 里面还可以加上日志的记录。
然后,关掉这个打开的 socket 连接,关掉这个线程。
整个代码流程如下:
from socket import *
import _thread
def getData(tcpCliSock):
while True:
try:
doing something
except:
break
tcpCliSock.close()
_thread.stop()
if __name__ == '__main__':
HOST = ""
PORT = 8588
BUFSIZ = 1024 #缓冲区大小
ADDR = (HOST,PORT)
tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(60) #连接被转接或者被拒绝之前,传入连接请求的最大数
while True:
tcpCliSock, addr = tcpSerSock.accept()
print("waiting for connect ...")
print("...connect from:", addr)
_thread.start_new_thread(getData, (tcpCliSock, ))
continue