Django を WebSocket サーバにする

Websocket サーバが立つと次のようなことが出来ます。

送信元クライアント
http://127.0.0.1:8000/sample/publish?msg=おはようございます。
![websocket_feb0201.png](https://qiita-image-store.s3.amazonaws.com/0/179446/1b7b243c-4c6e-765a-ecb3-ff22381019ca.png)

受信クライアント
wscat --connect http://127.0.0.1:8000/ws
![websocket_feb0202.png](https://qiita-image-store.s3.amazonaws.com/0/179446/032a0922-b85c-ce44-1898-941a20cdaf78.png)

Arch Linux に wscat をインストールするには

```bash
yay -S wscat
```

Django の設定
参考にしたページ
[Django で WebSocket によるサーバ Push](https://qiita.com/voluntas/items/f286f7671f744860293b)
[Getting Started with Channels](https://channels.readthedocs.io/en/latest/getting-started.html)

使用した Django のバージョン

```text
>>> import django
>>> django.get_version()
'2.0.2'
```

channels のインストール

```bash
pip install django channels
```

Django のプロジェクトの作成

```bash
django-admin startproject ws
cd ws
python manage.py startapp sample
```

Django の動作確認

```bash
python manage.py runserver
```

ブラウザーで、http://127.0.0.1:8000/　にアクセスする
![websocket_feb0203.png](https://qiita-image-store.s3.amazonaws.com/0/179446/a36375bf-ea7a-b215-9190-8bb1b5bb629b.png)

Django の基本が動くことを確認したら、WebSocket サーバに改造する

```py3:ws/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
'sample',
'channels',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

APPEND_SLASH = False

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "asgiref.inmemory.ChannelLayer",
        "ROUTING": "sample.routing.channel_routing",
    },
```

```py3:ws/urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^sample/', include('sample.urls')),
    url(r'^admin/', admin.site.urls),
]
```

```py3:sample/consumers.py
from channels import Group

# WS が繋がってきたら sample というグループに参加させる
def ws_add(message):
    message.reply_channel.send({"accept": True})
    Group("sample").add(message.reply_channel)


# WS が切れたら sample というグループから外させる
def ws_disconnect(message):
    Group("sample").discard(message.reply_channel)
```

```py3:sample/routing.py
from channels.routing import route

# 先ほど作った sample/consumers.py から引っ張ってきてるだけ
from sample.consumers import ws_add, ws_disconnect

channel_routing = [
    # path を指定する 127.0.0.1:8000/ws に繋げるという感じ
    route("websocket.connect", ws_add, path = r'^/ws$'),
    route("websocket.disconnect", ws_disconnect),
]
```

```py3:sample/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    # 127.0.0.1:8000/sample/publish でメッセージを送れるようにする
    url(r'^publish$', views.publish, name='publish'),
]
```

```py3:sample/vies.py
from django.shortcuts import render

# Create your views here.
from django.http import HttpResponse

from channels import Group

def publish(request):
    msg = request.GET.get('msg', 'null')
    Group("sample").send({"text": msg})

    return HttpResponse("Published!")
```

そしてサーバを実行

```bash
pyrhon manager.py runserver
```

受信の待機

```bash
wscat --connect http://127.0.0.1:8000/ws
```

ブラウザーでメッセージを送る

```text
http://127.0.0.1:8000/sample/publish?msg=おはようございます。
```