一尘不染

使用websockets和python/django(/twisted?)

django

websockets有趣的部分是从服务器向浏览器发送本质上未经请求的内容,对吗?

好吧,我正在使用GregorMüllegger的django-websocket。使Websockets在Django中工作确实是一个很棒的早期尝试。

我完成了“ hello world”。它的工作方式是:当请求是websocket时,会将对象websocket附加到请求对象。因此,在解释websocket的视图中,我可以执行以下操作:

request.websocket.send('We are the knights who say ni!')

很好 我在浏览器中收到的消息像超级按钮一样。

但是,如果我不想在不发出浏览器请求的情况下进行操作,该怎么办?

OK,所以首先我将websocket保存在会话字典中:

request.session['websocket'] = request.websocket

然后,在外壳中,我按会话密钥获取会话。当然,会话字典中有一个websocket对象。快乐!

但是,当我尝试执行以下操作时:

>>> session.get_decoded()['websocket'].send('With a herring!')

我得到:

Traceback (most recent call last):
File "<console>", line 1, in <module>
error: [Errno 9] Bad file descriptor

好的,所以我对套接字一无所知,但是我知道足以在调试器中四处嗅探,瞧瞧,我发现调试器中的套接字(与请求中的真正websocket绑定在一起)具有fd = 6,而我从会话保存的Websocket中抢到的那个具有fd = -1。

面向套接字的人可以帮助我解决这些问题吗?


阅读 485

收藏
2020-03-30

共1个答案

一尘不染

我是django-websocket的作者。我不是Websocket和网络领域的真正专家,但是我认为我对所发生的事情有很好的了解。很抱歉,进入细节。即使大多数答案不是特定于你的问题,也可能会在其他时候为你提供帮助。:-)

网络套接字如何工作
让我简短地解释一下什么是网络套接字。Websocket的开始看起来像是从浏览器建立的普通HTTP请求。它通过HTTP标头指示它希望将协议“升级”为Web套接字而不是HTTP请求。如果服务器支持websocket,则表示同意握手,并且服务器和客户端现在都知道它们将使用以前用于HTTP请求的已建立tcp套接字作为连接来交换websocket消息。

除了发送和等待消息外,他们当然还具有随时关闭连接的能力。

django-websocket如何滥用python的wsgi请求环境来劫持套接字
现在,让我们详细了解django-websocket如何在django请求-响应网络中实现HTTP请求的“升级”。

Django通常使用WSGI规范来与Web服务器通信,例如apache或gunicorn等。该规范的设计仅考虑到HTTP的通信模型非常有限。它假定它获取一个HTTP请求(仅传入数据)并返回响应(仅传出数据)。这使得将django强制引入允许双向通信的websocket的概念非常棘手。

我在django-websocket中为实现此目的而做的是,我非常深入地研究了WSGI和django的请求对象以检索底层套接字的内部。然后,此tcp套接字用于直接处理将HTTP请求升级到websocket实例。

现在到你的原始问题…

我希望以上内容可以使我们很明显,当建立一个websocket时,没有必要返回HttpResponse。这就是为什么通常在django-websocket处理的视图中不返回任何内容的原因。

但是,我想坚持一个视图的概念,该视图包含逻辑并根据输入返回数据。这就是为什么你只应在视图中使用代码来处理websocket的原因。

从视图返回后,websocket将自动关闭。这样做是有原因的:我们不想在不确定的时间内保持打开套接字的状态,而不必依靠客户端(浏览器)将其关闭。

这就是为什么你无法在视图外部使用django-websocket访问websocket的原因。然后,当然将文件描述符设置为-1,表明其已关闭。

免责声明
上面我曾解释说,我正在django的周围环境中进行挖掘,以便以一种非常骇人听闻的方式来访问底层套接字。这非常脆弱,而且由于WSGI并非为此而设计,因此也不应该起作用!上面我还解释了视图结束后websocket是关闭的-但是,在websocket关闭(并关闭了tcp套接字)之后,django的WSGI实现尝试发送HTTP响应-它不知道websockets并认为它在正常的HTTP请求-响应周期。但是套接字已经关闭,发送将失败。这通常会在Django中引起异常。

这并没有影响我对开发服务器的测试。浏览器永远不会注意到(你知道..套接字已经关闭;-)-但是在每个请求中引发未处理的错误不是一个很好的概念,并且可能会泄漏内存,无法正确处理数据库连接关闭以及许多麻烦的事情如果你使用django-websocket进行更多的实验,那将在某些时候中断。

这就是为什么我真的建议你不要将Websocket与django一起使用的原因。它不是设计使然。Django(尤其是WSGI)将需要彻底检查以解决这些问题(有关Websockets和WSGI的信息,请参阅此讨论)。从那时起,我建议使用诸如eventlet之类的东西。Eventlet具有有效的websocket实现(我从eventlet借用了django-websocket初始版本的一些代码),由于它只是纯Python代码,因此你可以从django中导入模型和其他所有内容。唯一的缺点是你需要运行另一个仅用于处理WebSocket的Web服务器。

2020-03-30