Websockets in Django (without Channels)
Tags: beginners, programming, python, websockets
Published: 2021-11-15 21:09:43


## What are websockets?

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. 
![websockets](https://i.imgur.com/c5ZDyEv.png)

## Introduction of Asynchronous Programming in python.

Unlike Javascript asynchronous execution wasn't supported in python. And this was a huge drawback especially in case of web development, since asynchronous servers and apps are essential in developing web apps.

But with the introduction of `async/await`   to python 3.5, things started to change.
###  `asyncio` 

- In **synchronous execution** the program executes sequentially in synchronous to the processor clock.
- **Coroutines** are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed.
  (threading is preemptive)
- **Subroutines** are special cases of coroutines.[3] When subroutines are invoked, execution begins at the start, and once a subroutine exits, it is finished; an instance of a subroutine only returns once, and does not hold state between invocations. By contrast, coroutines can exit by calling other coroutines, which may later return to the point where they were invoked in the original coroutine;
- Coroutines are very similar to **threads**. However, coroutines are cooperatively multitasked, whereas threads are typically preemptively multitasked. Coroutines provide concurrency but not parallelism. The advantages of coroutines over threads are that they may be used in a hard-realtime context (switching between coroutines need not involve any system calls or any blocking calls whatsoever),
```python
var q := new queue

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume

coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce

call produce
```
- **Generators**, also known as semicoroutines (not discussed here)



- asyncio python example.
```python
import asyncio
import random
async def fetch():
	print("Started..")
	await asyncio.sleep(2) #Fetching delay
	print("Done!")
async def receive():
	for i in range(10):
		print(i)
		await asyncio.sleep(0.225) # Receiving delay
		
async def  main():
	task1 = asyncio.create_task(fetch()) #returns a future
	task2 = asyncio.create_task(receive())
	await task1 # wait for the promise to return something
	print("task one awaited")
	await task2

asyncio.run(main()) # event loop beginning

```

So having able to write native coroutines in python opens the door to asynchronizing python web servers and apps .

## ASGI
In WSGI applications takes a single request and returns response at a time. This single and synchronous callable limits WSGI for long lived connections like websocket connections.
![](https://i.imgur.com/nGlkLRf.png)
![](https://i.imgur.com/5JDEX8L.png)
![asgi overview](https://i.imgur.com/bXOfR29.png)

[source1](https://florimond.dev/en/posts/2019/08/introduction-to-asgi-async-python-web/)
[source2](https://morioh.com/p/e544d71b9af2)

ASGI consists of two different components:

- A **protocol server**, which terminates sockets and translates them into connections and per-connection event messages.
- An **application**, which lives inside a protocol server, is instanciated once per connection, and handles event messages as they happen.
- ASGI relies on the following mental model: when the client connects to the server, we instanciate an application. We then feed incoming bytes into the app and send back whatever bytes come out.

## How to Add Websockets to a Django App
1. There will be Django defined ASGI application function in asgi.py file. 
```python
import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')

application = get_asgi_application()

```
2. And we need to create a wrapper for the django provided asgi application. 

```python
from django.core.asgi import get_asgi_application
from websocket_app.websocket import websocket_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')

django_application = get_asgi_application()

async def application(scope, receive, send):
    if scope['type'] == 'http':
        await django_application(scope, receive, send)
    elif scope['type'] == 'websocket':
        await websocket_application(scope, receive, send)
    else:
        raise NotImplementedError(f"Unknown scope type {scope['type']}")

# websocket.py
async def websocket_applciation(scope, receive, send):
    pass

```
3. defining the websockets function
```python
# websocket.py
async def websocket_applciation(scope, receive, send):
    while True:
        event = await receive()

        if event['type'] == 'websocket.connect':
            await send({
                'type': 'websocket.accept'
            })

        if event['type'] == 'websocket.disconnect':
            break

        if event['type'] == 'websocket.receive':
            if event['text'] == 'ping':
                await send({
                    'type': 'websocket.send',
                    'text': 'pong!'
                })

```
4. For running the asgi app we require an asynchronous web server (daphene or uvicor)
in case of uvicorn :
```bash
pip install uvicorn
uvicorn websocket_app.asgi:application
```
5. The following js script can be used for testing the code.
```javascript

> ws = new WebSocket('ws://localhost:8000/')
  WebSocket {url: "ws://localhost:8000/", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}
> ws.onmessage = event => console.log(event.data)
  event => console.log(event.data)
> ws.send("ping")
  undefined
  pong!

```
[source
](https://dev.to/jaydenwindle/adding-websockets-to-your-django-app-with-no-extra-dependencies-2f6h)