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

使用websockets和python/django(/twisted?),python,django,sockets,websocket,Python,Django,Sockets,Websocket,WebSocket的有趣之处在于从服务器向浏览器发送未经请求的内容,对吗 我用的是Gregor müllegger的django websocket。让WebSocket在Django工作,这是一个非常棒的早期尝试 我已经完成了“hello world”。其工作方式是:当请求是websocket时,一个对象websocket被附加到请求对象。因此,在解释websocket的视图中,我可以执行以下操作: request.websocket.send('We are the knights who

WebSocket的有趣之处在于从服务器向浏览器发送未经请求的内容,对吗

我用的是Gregor müllegger的django websocket。让WebSocket在Django工作,这是一个非常棒的早期尝试

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

request.websocket.send('We are the knights who say ni!')
那很好。我在浏览器中如痴如醉地收到消息

但是,如果我不想从浏览器发出任何请求就这样做,该怎么办

好的,首先我将websocket保存在会话字典中:

request.session['websocket'] = request.websocket
然后,在shell中,我按会话键获取会话。果然,会话字典中有一个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
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
错误:[Errno 9]错误的文件描述符
悲哀的-(

好吧,我对套接字了解不多,但我知道的足够多,可以在调试器中嗅探,瞧,我看到调试器中的套接字(与请求中真正的websocket绑定)的fd=6,而我从会话保存的websocket中获取的套接字的fd=-1


面向套接字的人能帮我解决这个问题吗?

请求。当您从请求处理程序(视图)返回时,websocket
可能会关闭。简单的解决方案是让处理程序保持活动状态(通过不从视图返回)。如果您的服务器不是多线程的,您将无法接受任何其他同时请求。

我是django websocket的作者。我不是websocket和网络方面的真正专家,但我认为我对正在发生的事情有相当的了解。很抱歉,我要详细说明。即使大多数答案都不具体对于您的问题,它可能在其他方面对您有所帮助。:-)


WebSocket如何工作 让我简单地解释一下什么是websocket。websocket一开始看起来像是从浏览器建立的普通HTTP请求。它通过HTTP头表示希望将协议“升级”为websocket,而不是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响应——它不知道websocket,认为它处于正常的HTTP请求-响应周期。但是套接字已经关闭,发送将失败。这通常会导致django中出现异常

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

这就是为什么我真的建议您不要将WebSocket与django一起使用。这不是故意的。Django,尤其是WSGI,需要全面检修以解决这些问题(请参阅)。从那时起,我建议使用类似的方法。Eventlet有一个工作的websocket实现(我从Eventlet借用了一些代码用于django websocket的初始版本),并且由于它的jus
class YourView(WebSocketView):

    def handler(self, websocket):
        self.request.context.add_listener(websocket)
        while True:
            msg = websocket.wait()
            # Do something with message
resource.send(some_other_message)