Django

[Django] python-socketio로 Socket.io 서버 구성하기

jhkimmm 2021. 12. 27. 14:35

라즈베리파이에 Django로 소형 웹서버를 구축해놓고 아두이노로부터 전달 받은 센서값을 node.js로 구현된 웹서버에 실시간으로 전달해주는 프로젝트를 진행 중입니다.

 

Naive한 웹소켓을 직접 사용하는 것 보다는 쉽게 사용할 수 있는 적절한 라이브러리를 찾아보니 Django에는 channels라는 좋은 웹소켓 라이브러리가 존재하지만 현재 진행 중인 프로젝트가 외부의 node.js 서버와 통신을 해야하다보니 인터페이스가 호환을 위해 socket.io를 파이썬으로 구현한 python-socketio 라이브러리를 사용하게 되었습니다.

즉, Django Server - Nodejs Client인 Socket.io(웹소켓)통신을 구현하면 됩니다.

 

Django라는 프레임워크 자체가 request가 들어오면 쓰레드를 생성해서 reponse를 리턴해주고 쓰레드가 종료되는 구조이다보니, 연결상태를 계속 유지해야하는 websocket을 직접 사용하기에는 어려움이 있어 'eventlet'이라는 네트워크 라이브러리를 함께 사용했습니다. 

 

먼저 필요한 모듈을 설치합니다.

pip install eventlet
pip install python-socketio
pip install django-cors-headers

node.js서버와 통신을 해야하므로 cors-header 문제가 발생할 수 있으니 django-cors-header 모듈도 함께 설치해주었습니다.

#settings.py

INSTALLED_APPS = [
	...
    'rest_framework',
    'socketio',
    'corsheaders',
]

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

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

위와 같이 settings.py의 INSTALLED_APPS에 'socketio','corsheaders'를 추가해주고

MIDDLEWARE에 'corsheaders.middleware.CorsMiddleware'를 추가해줍니다.

 

CORS_ORGIN_ALLOW_ALL을 True로 설정하면 모든 주소에 cors-header를 연동할 수 있습니다.

 

다음은 wsgi.py를 수정 해줍니다.

#wsgi.py
import os

from django.core.wsgi import get_wsgi_application
import socketio
import eventlet
import eventlet.wsgi

...

django_app = get_wsgi_application()
sio = socketio.Server(async_mode='eventlet', cors_allowed_origins='*', cors_credentials=True)
application = socketio.WSGIApp(sio, django_app)
eventlet.wsgi.server(eventlet.listen(('', 8000)), application)

- socketio.Server 생성자의 파라미터로 async_mode='eventlet'을 넘겨주면 비동기 작업에 대해 eventlet을 자동으로 사용합니다.

- Socketio 서버는 socketio.Server 클래스의 인스턴스인데, 이 인스턴스를 socketio.WSGIApp 클래스로 감싸줌으로써 표준 WSGI 어플리케이션으로 변환합니다.

- eventlet용으로 구성된 서버는 제공된 socketio.WSGIApp을 사용하여 일반 WSGI 애플리케이션으로 배포됩니다.

#views.py
...

from socketioexample.wsgi import sio

def index(request):
    global thread
    if thread is None:
        thread = sio.start_background_task(background_thread)
    return HttpResponse(open(os.path.join(basedir, 'static/index.html')))


def background_thread():
    """Example of how to send server generated events to clients."""
    count = 0
    while True:
        sio.sleep(10)
        count += 1
        sio.emit('my_response', {'data': 'Server generated event'},
                 namespace='/test')


@sio.event
def my_event(sid, message):
    sio.emit('my_response', {'data': message['data']}, room=sid)
    
    ...
    
 @sio.event
def disconnect(sid):
    print('Client disconnected')

기본적인 python-socketio의 사용예시는 아래의 공식 github를 참고해서 구현하였습니다.

https://github.com/miguelgrinberg/python-socketio/tree/main/examples/server/wsgi/django_example

 

GitHub - miguelgrinberg/python-socketio: Python Socket.IO server and client

Python Socket.IO server and client. Contribute to miguelgrinberg/python-socketio development by creating an account on GitHub.

github.com