跳转到主要内容

使用gevent-websocket的简单WebSocket服务器,适用于Django

项目描述

简介

WSGI被认为与WebSocket大部分不兼容,但这并不一定正确。正如gevent-websocket所示,您可以使用gunicorn轻松运行WSGI/WebSocket服务器。

然而,能够将WebSocket服务器与Django集成将非常理想。也就是说,不仅能够访问Django模型,还能够访问整个Django基础设施。虽然使用模板渲染可能没有太多意义,但能够访问来自django项目的中间件和身份验证功能将非常方便。并且能够使用django表单处理来验证用户输入,这意味着您可以编写更健壮的代码。

此模块允许您做到这一点。您可以在正常WSGI服务器旁边运行一个服务器,该服务器侦听WebSocket连接请求。传入的连接最初由Django URL路由设施(但使用单独的urlconf)处理,并应用Django中间件。然后,您的WebSocket视图函数被调用,从那时起处理连接。

沙箱

进入test_project

$ virtualenv .
. bin/activate
pip install -r requirements.txt

您需要打开两个shell

./project/manage.py runserver 0.0.0.0
gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" project.wsgi:websocket -b 0.0.0.0:8001

访问http://localhost:8000/chat/以打开聊天窗口。

您可以在多个浏览器中打开多个窗口,并在它们之间聊天。您可以更改用户名,以及名称应显示的颜色。

还有一些其他示例WebSocket服务器

安装

使用以下命令从PyPI安装:

$ pip install django-gevent-websocket

它将安装其各种依赖项

  • django

  • gevent-websocket

  • gunicorn

用法

设置

将以下内容添加到您的 $PROJECT_DIR/wsgi.py

from django_websocket.wsgi import get_wsgi_websocket_application
websocket = get_wsgi_websocket_application()

将以下内容添加到您的 $PROJECT_DIR/settings.py

WEBSOCKET_URLCONF = '<path-to-websocket-urlconf>'

建议的urlconf可以是 $PROJECT_DIR/websocket_urls.py

然后您需要将您的WebSocket urls添加到该文件中:它们将与您的正常urls非常相似,但只会包含应该接受WebSocket连接的端点

from django.conf.urls import patterns, url

urlpatterns = patterns('',
    url(r'^chat/$', 'path.to.view', name='chat'),
)

如果您想使用 {% websocket_url %} 获取指向正确WebSocket服务器和端口的绝对urls,那么您需要设置

视图

WebSocket视图函数看起来非常像普通的django视图函数,实际上,它是由常规django请求周期调用的,但它不返回HttpResponse对象。相反,您从请求中获取websocket对象,并从它那里 .receive() 数据,或者 .send(data) 到它。

为了使事情变得简单,您可以使用装饰器来包装您的视图

from django_websocket.decorators import websocket

@websocket
def view_function(request, websocket, *args, **kwargs):
    # do stuff here.

此装饰器将websocket作为视图的第二个参数添加:您仍然有请求对象,允许您进行权限检查等操作。如果您愿意,仍然可以使用任何常规django装饰器。

视图中的异步性

WebSocket视图应该监听来自客户端需要处理的数据,但可能还需要监听来自另一个源的数据,这些数据将被发送回客户端。

这样做的一个简单方法是使用 select.select 函数,并使用它来编写非阻塞代码,等待信号继续。通常,您将想要循环,直到WebSocket关闭,然后等待信号

from select import select

from django_websocket.decorators import websocket

@websocket
def view_function(request, websocket):
    ws_sock = websocket.handler.socket.fileno()
    other_sock = ... # other socket to listen on.

    while not websocket.closed():
        fd = select([ws_sock, other_sock], [], [])[0][0]

        if fd == ws_sock:
            data = websocket.receive()
            # Deal with incoming data.
        else: ## fd == other_sock:
            # Deal with data from the other source
            websocket.send(message)

一个“其他”来源可能是一个Redis PubSub订阅,它允许您订阅频道,并在这些频道中的任何一个上有新数据可用时通知您。

来自 django_websocket.servers.chat

from select import select

import redis

from ..decorators import websocket

@websocket
def chat(request, websocket, *args, **kwargs):

    conn = redis.StrictRedis()
    subs = conn.pubsub()
    subs.subscribe('CHAT')

    def incoming():
        data = websocket.receive()
        if data:
            conn.publish('CHAT', data)

    def outgoing():
        msg_type, channel, message = subs.parse_response()
        if msg_type == 'message':
            websocket.send(message)

    sockets = {
        websocket.handler.socket.fileno(): incoming,
        subs.connection._sock.fileno(): outgoing
    }

    while not websocket.closed:
        fd = select(sockets.keys(), [], [])[0][0]
        sockets[fd]()

模板标签

因为您应该为WebSocket视图有不同的urlconf,所以您需要使用一个稍微不同的模板标签来访问WebSocket urls

{% load websockets %}

<script>
  var ws = new WebSocket("{% websocket_url 'urlname' %}");

  // Do something with your shiny new WebSocket!
  // ws.send('foo');
</script>

启动服务器

您需要单独启动 geventwebsocket 服务器:没有django管理命令(这反映了run_gunicorn的弃用)

gunicorn -k "geventwebsocket.gunicorn.workers.GeventWebSocketWorker" $PROJECT_DIR.wsgi:websocket -b 127.0.0.1:8001

注意这运行在不同于您的django开发服务器(或生产中的gunicorn)的端口上。在生产中,您可能将两者都放在nginx代理或类似的后面。如果您想这样做,您可能需要进行一些复杂的设置,以便WebSocket连接的分离可以正常工作。我做了类似以下的事情

server {
  listen  80;
  proxy_set_header Host $host;

  location /static/ {
    alias ...
  }

  if ($http_upgrade) {
    rewrite ^(.*)$ /__ws__/$1 break;
  }

  location /__ws__/ {
    proxy_pass http://127.0.0.1:8001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    rewrite ^/__ws__/(.*)$ $1 break;
    return;
  }

  location / {
    proxy_pass http://127.0.0.1:8000;
  }
}

注意双重的重写。当存在Upgrade头时,它重写url,然后重写回来,以便url在django内部匹配。

项目详细信息


下载文件

下载适用于您平台的应用程序。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分布

django-gevent-websocket-0.2.0.tar.gz (6.2 kB 查看哈希值)

上传时间 源代码

由以下支持