从0到1建立django项目并且实现websocket服务器功能

本文基于以下环境:

Python3.6.8

Django2.2

Channels2.4.0

channels_redis3.2.0

本文主要翻译自英文资料网站,由于时间有限部分语句可能不通顺请大家凑合看。文后配有项目代码文件。

本文配套源代码在此下载:https://download.csdn.net/download/conganguo/19238875

首先新建一个django项目:

新建django项目:在桌面新建文件夹aa,在aa路径下运行命令:

 

运行django项目:在新建的项目mysite下面,运行命令python manage.py runserver。

系统提示,要运行“python manage.py migrate”.

注:我的电脑中python调用的是我电脑中python安装路径下面的python.exe,如果没配置环境变量的可以把将路径配置到环境变量下。

 

运行命令运行“python manage.py migrate”:

 

再次运行命令python manage.py runserver:

 

在网页中输入网址127.0.0.1:8000,看见小火箭,代表网站已经成功启动!

 

创建一个新的应用APP

自带的主应用可能不适用websocket,因此我们在项目下创建一个新的应用。

确保您与manage.py文件在同一路径下,然后输入以下命令:

$ python manage.py startapp chat

这将创建一个目录chat,其布局如下所示:

chat/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

就本教程而言,我们将仅使用chat/views.py 和chat/__init__.py。因此,可以从chat目录中删除所有其他文件。

删除不必要的文件后,chat目录应如下所示:

chat/
    __init__.py
    views.py

我们需要告诉项目,该chat应用APP已安装。

编辑 mysite/settings.py文件并添加'chat'到INSTALLED_APPS设置。它在此路径下:

# mysite/settings.py

INSTALLED_APPS = [

    'chat',

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

]

添加索引视图:

现在,我们将创建第一个视图,即索引视图。

在您的chat目录中创建一个目录templates,在您刚创建的目录下,创建目录chat,并在chat下创建index.html以保存索引视图的模板。

您的聊天目录现在应如下所示:

chat/

    __init__.py

    templates/

        chat/

            index.html

    views.py

将以下代码放入chat/templates/chat/index.html:

<!-- chat/templates/chat/index.html -->

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8"/>

    <title>Chat Rooms</title>

</head>

<body>

    What chat room would you like to enter?<br>

    <input id="room-name-input" type="text" size="100"><br>

    <input id="room-name-submit" type="button" value="Enter">

 

    <script>

        document.querySelector('#room-name-input').focus();

        document.querySelector('#room-name-input').onkeyup = function(e) {

            if (e.keyCode === 13) {  // enter, return

                document.querySelector('#room-name-submit').click();

            }

        };

 

        document.querySelector('#room-name-submit').onclick = function(e) {

            var roomName = document.querySelector('#room-name-input').value;

            window.location.pathname = '/chat/' + roomName + '/';

        };

    </script>

</body>

</html>

创建房间视图的视图功能。将以下代码放入chat/views.py:

# chat/views.py
from django.shortcuts import render
def index(request):
    return render(request, 'chat/index.html')

要调用视图,我们需要将其映射到URL。

要在聊天目录中创建URLconf,在chat应用下创建一个文件urls.py。您的应用目录现在应如下所示:

chat/

    __init__.py

    templates/

        chat/

            index.html

    urls.py

    views.py

在chat/urls.py文件中包括以下代码:

# chat/urls.py

from django.urls import path

from . import views

urlpatterns = [

    path('', views.index, name='index'),

]

下一步是将根URLconf指向chat.urls模块。在mysite/urls.py,将urlpatterns修改如下:

# mysite/urls.py

from django.conf.urls import include

from django.urls import path

from django.contrib import admin

 

urlpatterns = [

    path('chat/', include('chat.urls')),

    path('admin/', admin.site.urls),

]

让我们验证索引视图是否有效。

在manage.py同级路径下运行以下命令:

$ python manage.py runserver

您将在命令行上看到以下输出:

Watching for file changes with StatReloader

Performing system checks...

 

System check identified no issues (0 silenced).

 

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.

Run 'python manage.py migrate' to apply them.

October 21, 2020 - 18:49:39

Django version 3.1.2, using settings 'mysite.settings'

Starting development server at http://127.0.0.1:8000/

Quit the server with CONTROL-C.

在浏览器敲入http://127.0.0.1:8000/chat/,您应该看到文本“您想进入哪个聊天室?”,以及输入房间名称。

输入“lobby”作为房间名称,然后按Enter。您应该在http://127.0.0.1:8000/chat/lobby/上重定向到会议室视图,但是我们尚未编写会议室视图,因此您将获得“找不到页面”错误页面。

转到运行runserver命令的终端,然后按Control-C停止服务器。

将channels集成到django中

Channels 路由配置与Django URLconf相似,它告诉Channels当Channels服务器接收到HTTP请求时要运行什么代码。

我们将从一个空的路由配置开始。创建文件mysite/routing.py并包含以下代码:

# mysite/routing.py

from channels.routing import ProtocolTypeRouter

 

application = ProtocolTypeRouter({

    # (http->django views is added by default)

})

现在,将Channels库添加到已安装的应用程序列表中。修改settings.py中的INSTALLED_APPS如下:

# mysite/settings.py

INSTALLED_APPS = [

    'channels',

    'chat',

    'django.contrib.admin',

    'django.contrib.auth',

    'django.contrib.contenttypes',

    'django.contrib.sessions',

    'django.contrib.messages',

    'django.contrib.staticfiles',

]

您还需要将Channels指向根路由配置。修改ASGI_APPLICATION如下:

# mysite/settings.py

# Channels

ASGI_APPLICATION = 'mysite.routing.application'

让我们确保Channels开发服务器正常工作。运行以下命令:

$ python3 manage.py runserver

您将在命令行上看到以下输出:

Performing system checks...

 

System check identified no issues (0 silenced).

 

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.

Run 'python manage.py migrate' to apply them.

 

February 18, 2018 - 22:16:23

Django version 1.11.10, using settings 'mysite.settings'

Starting ASGI/Channels development server at http://127.0.0.1:8000/

Quit the server with CONTROL-C.

2018-02-18 22:16:23,729 - INFO - server - HTTP/2 support not enabled (install the http2 and tls Twisted extras)

2018-02-18 22:16:23,730 - INFO - server - Configuring endpoint tcp:port=8000:interface=127.0.0.1

2018-02-18 22:16:23,731 - INFO - server - Listening on TCP address 127.0.0.1:8000

通过以下显示表明,已从Django开发服务器变为Channels开发服务器:Starting ASGI/Channels development server at http://127.0.0.1:8000/

在浏览器中转到http://127.0.0.1:8000/chat/

转到运行runserver命令的终端,然后按Control-C停止服务器。

建立consumer,实现客户端连接服务端

添加房间视图

现在,我们将创建第二个视图,使您可以查看在特定聊天室中发布的消息。

创建一个新文件chat/templates/chat/room.html。您的应用目录现在应如下所示:

chat/

    __init__.py

    templates/

        chat/

            index.html

            room.html

    urls.py

    views.py

敲入如下代码chat/templates/chat/room.html:

<!-- chat/templates/chat/room.html -->

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8"/>

    <title>Chat Room</title>

</head>

<body>

    <textarea id="chat-log" cols="100" rows="20"></textarea><br>

    <input id="chat-message-input" type="text" size="100"><br>

    <input id="chat-message-submit" type="button" value="Send">

    {{ room_name|json_script:"room-name" }}

    <script>

        const roomName = JSON.parse(document.getElementById('room-name').textContent);

 

        const chatSocket = new WebSocket(

            'ws://'

            + window.location.host

            + '/ws/chat/'

            + roomName

            + '/'

        );

 

        chatSocket.onmessage = function(e) {

            const data = JSON.parse(e.data);

            document.querySelector('#chat-log').value += (data.message + '\n');

        };

 

        chatSocket.onclose = function(e) {

            console.error('Chat socket closed unexpectedly');

        };

 

        document.querySelector('#chat-message-input').focus();

        document.querySelector('#chat-message-input').onkeyup = function(e) {

            if (e.keyCode === 13) {  // enter, return

                document.querySelector('#chat-message-submit').click();

            }

        };

 

        document.querySelector('#chat-message-submit').onclick = function(e) {

            const messageInputDom = document.querySelector('#chat-message-input');

            const message = messageInputDom.value;

            chatSocket.send(JSON.stringify({

                'message': message

            }));

            messageInputDom.value = '';

        };

    </script>

</body>

</html>

在chat/views.py敲入代码:

# chat/views.py

from django.shortcuts import render

 

def index(request):

    return render(request, 'chat/index.html', {})

 

def room(request, room_name):

    return render(request, 'chat/room.html', {

        'room_name': room_name

    })

在中为房间视图创建路线chat/urls.py:

# chat/urls.py

from django.urls import path

 

from . import views

 

urlpatterns = [

    path('', views.index, name='index'),

    path('<str:room_name>/', views.room, name='room'),

]

启动渠道开发服务器:

$ python manage.py runserver

在浏览器中转到http://127.0.0.1:8000/chat/并查看索引页面。

输入“ lobby”作为房间名称,然后按Enter。您应该重定向到http://127.0.0.1:8000/chat/lobby/上的会议室页面,该页面现在显示一个空的聊天记录。

输入消息“ hello”,然后按Enter。会发现没有任何反应,为什么?

视图正在尝试打开WebSocket的URL, ws://127.0.0.1:8000/ws/chat/lobby/,但我们尚未创建接受WebSocket连接的使用者。

如果打开浏览器的JavaScript控制台,应该看到如下错误:

WebSocket connection to 'ws://127.0.0.1:8000/ws/chat/lobby/' failed: Unexpected response code: 500

写你的第一个consumer

当Django接受HTTP请求时,它会查询根URLconf来查找视图函数,然后调用该视图函数来处理该请求。同样,当Channels接受WebSocket连接时,它会查询根路由配置以查找使用者,然后在使用者上调用各种功能以处理来自连接的事件。

创建一个新文件chat/consumers.py。您的应用目录现在应如下所示:

chat/

    __init__.py

    consumers.py

    templates/

        chat/

            index.html

            room.html

    urls.py

    views.py

将以下代码放入chat/consumers.py:

# chat/consumers.py

import json

from channels.generic.websocket import WebsocketConsumer

 

class ChatConsumer(WebsocketConsumer):

    def connect(self):

        self.accept()

 

    def disconnect(self, close_code):

        pass

 

    def receive(self, text_data):

        text_data_json = json.loads(text_data)

        message = text_data_json['message']

 

        self.send(text_data=json.dumps({

            'message': message

        }))

创建一个新文件chat/routing.py。您的应用目录现在应如下所示:

chat/

    __init__.py

    consumers.py

    routing.py

    templates/

        chat/

            index.html

            room.html

    urls.py

    views.py

将以下代码放入chat/routing.py:

# chat/routing.py

from django.urls import re_path

from . import consumers

 

websocket_urlpatterns = [

    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),

]

下一步是将根路由配置指向chat.routing 模块。

修改mysite/routing.py:

# mysite/routing.py

from channels.auth import AuthMiddlewareStack

from channels.routing import ProtocolTypeRouter, URLRouter

import chat.routing

 

application = ProtocolTypeRouter({

    # (http->django views is added by default)

    'websocket': AuthMiddlewareStack(

        URLRouter(

            chat.routing.websocket_urlpatterns

        )

    ),

})

让我们验证该/ws/chat/ROOM_NAME/路径是否有效。运行以下带有$的命令,启动Channels开发服务器:

$ python manage.py migrate

Operations to perform:

  Apply all migrations: admin, auth, contenttypes, sessions

Running migrations:

  Applying contenttypes.0001_initial... OK

  Applying auth.0001_initial... OK

  Applying admin.0001_initial... OK

  Applying admin.0002_logentry_remove_auto_add... OK

  Applying contenttypes.0002_remove_content_type_name... OK

  Applying auth.0002_alter_permission_name_max_length... OK

  Applying auth.0003_alter_user_email_max_length... OK

  Applying auth.0004_alter_user_username_opts... OK

  Applying auth.0005_alter_user_last_login_null... OK

  Applying auth.0006_require_contenttypes_0002... OK

  Applying auth.0007_alter_validators_add_error_messages... OK

  Applying auth.0008_alter_user_username_max_length... OK

  Applying auth.0009_alter_user_last_name_max_length... OK

  Applying sessions.0001_initial... OK

$ python3 manage.py runserver

转到http://127.0.0.1:8000/chat/lobby/上的会议室页面,该页面现在显示一个空的聊天记录。

输入消息“ hello”,然后按Enter。现在,您应该在聊天记录中看到回声“ hello”。

转到运行runserver命令的终端,然后按Control-C停止服务器。

第四步,建立channel layer

通道层是一种通信系统。它允许多个使用者实例彼此对话,并与Django的其他部分对话。

我们将使用Redis用作channels的临时存储。要在端口6379上启动Redis服务器(首先确保电脑上有redis,也可以手动启动redis),请运行以下命令:

$ docker run -p 6379:6379 -d redis:5

我们需要安装channels_redis,以便Channels知道如何与Redis交互。运行以下命令:

$ python -m pip install channels_redis

在使用通道层之前,必须对其进行配置。编辑 mysite/settings.py文件,然后配置CHANNEL_LAYERS:

# mysite/settings.py

# Channels

ASGI_APPLICATION = 'mysite.routing.application'

CHANNEL_LAYERS = {

    'default': {

        'BACKEND': 'channels_redis.core.RedisChannelLayer',

        'CONFIG': {

            "hosts": [('127.0.0.1', 6379)],

        },

    },

}

可以配置多个通道层。但是,大多数项目将仅使用单个'default'通道层。

让我们确保通道层可以与Redis通信。打开Django shell并运行以下命令:

$ python manage.py shell

>>> import channels.layers

>>> channel_layer = channels.layers.get_channel_layer()

>>> from asgiref.sync import  async_to_sync

>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})

>>> async_to_sync(channel_layer.receive)('test_channel')

{'type': 'hello'}

键入Control-D退出Django Shell。

将以下代码放入中chat/consumers.py,替换旧代码:

# chat/consumers.py

import json

from asgiref.sync import async_to_sync

from channels.generic.websocket import WebsocketConsumer

 

class ChatConsumer(WebsocketConsumer):

    def connect(self):

        self.room_name = self.scope['url_route']['kwargs']['room_name']

        self.room_group_name = 'chat_%s' % self.room_name

 

        # Join room group

        async_to_sync(self.channel_layer.group_add)(

            self.room_group_name,

            self.channel_name

        )

 

        self.accept()

 

    def disconnect(self, close_code):

        # Leave room group

        async_to_sync(self.channel_layer.group_discard)(

            self.room_group_name,

            self.channel_name

        )

 

    # Receive message from WebSocket

    def receive(self, text_data):

        text_data_json = json.loads(text_data)

        message = text_data_json['message']

 

        # Send message to room group

        async_to_sync(self.channel_layer.group_send)(

            self.room_group_name,

            {

                'type': 'chat_message',

                'message': message

            }

        )

 

    # Receive message from room group

    def chat_message(self, event):

        message = event['message']

 

        # Send message to WebSocket

        self.send(text_data=json.dumps({

            'message': message

        }))

当用户发布消息时,JavaScript函数将通过WebSocket将消息传输到ChatConsumer。ChatConsumer将收到该消息,并将其转发到与房间名称相对应的组。然后,同一组中(并因此在同一房间中)的每个ChatConsumer将接收来自该组的消息,并通过WebSocket将其转发回JavaScript,然后将其附加到聊天日志中。

让我们验证/ws/chat/ROOM_NAME/路径是否有效。启动Channels开发服务器,请运行以下命令:

$ python manage.py runserver

http://127.0.0.1:8000/chat/lobby/处打开浏览器标签,进入会议室页面。打开第二个浏览器选项卡到同一房间页面。

在第二个浏览器选项卡中,键入消息“ hello”,然后按Enter。现在,您应该在第二个浏览器选项卡和第一个浏览器选项卡的聊天日志中都看到“ hello”字样。

也可以在http://coolaf.com/tool/chattest上面,通过连接ws://127.0.0.1:8000/ws/chat/lobby/,监听信息。

至此,客户端可以connect服务端,并且服务端也可以给每个连接的客户端推送数据。

本文参考了文档:

https://channels.readthedocs.io/en/2.x/tutorial/part_1.html,如果英文阅读不方便可以翻译中文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赫曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值