Skip to content

Instantly share code, notes, and snippets.

@stresszero
Last active October 10, 2022 08:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stresszero/9a4f203e42fcd318df6c39f865df1ecc to your computer and use it in GitHub Desktop.
Save stresszero/9a4f203e42fcd318df6c39f865df1ecc to your computer and use it in GitHub Desktop.
socketio.py
import os
import socketio
import django
# Django 기본생성 파일이 아닌 경우 밑에서 Django models를 임포트하면 에러 발생함
# Django models 임포트 하기 전에 django.setup() 해줘야 에러 안 남
# Django 환경설정 파일 로드
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "togedog_dj.settings")
django.setup()
from django.conf import settings
from .mongodb import save_message
from cores.models import UserStatus
from cores.utils import censor_text
from users.models import User
# 소켓.io 서버 인스턴스 생성
# async_mode: 비동기처리 방법 설정, 따로 설정하지 않으면 eventlet부터 통신 시도
# cors_allowed_origins: '*'로 설정하면 cors 모두 허용
# logger=True로 설정하면 서버에서 발생하는 이벤트 로그를 터미널에 출력함, fatal error는 False로 설정해도 출력됨
sio = socketio.Server(
async_mode="eventlet",
cors_allowed_origins=settings.CORS_ALLOWED_ORIGINS,
# cors_allowed_origins='*',
# logger=True
)
# sio.event: connect와 disconnect는 클라이언트가 서버에 접속하거나 접속을 끊을 때 발생
# 이벤트 명으로 connect, message, disconnect 등은 명시적으로 정의된 이벤트로, python-socketio에서 정해진 대로만 사용 가능
# @sio.event는 밑에서 데코레이터 받은 함수명(def 함수명)을 이벤트로 받으며 @sio.on('이벤트명')는 인자 값으로 이벤트명을 받음
# 따라서 @sio.on으로는 함수명으로 쓰지 못하는 한글이나 공백이 들어간 문자열을 이벤트명으로 사용 할 수 있음
# sio.emit안의 인자인 to와 room은 완전히 똑같음, 아무거나 써도 되고 특정 room으로만 메시지를 보낼 때 사용
@sio.event
def connect(sid, environ, auth):
"""
socket.io 클라이언트가 연결되면 아래 코드를 실행한다, connect는 미리 정의된 이벤트이며
파라미터인 sid, environ, auth 들도 python-socketio 패키지에서 미리 정해놓은 것들이다.
- environ: http 헤더를 포함한 http 리퀘스트 데이터를 담는 WSGI 표준 딕셔너리
- auth: 클라이언트에서 넘겨준 인증 데이터, 데이터가 없으면 None이 된다
- 클라이언트가 보낸 auth의 유저id값으로 해당 유저가 존재하지 않거나
- 인증이 불가한 유저는 return False해서 채팅방에 입장 못하게 처리
- return False 하는 대신 raise ConnectionRefusedError으로 연결 거부 메시지를 전송할 수도 있음
"""
user = User.objects.filter(id=auth["userId"])
if not user.exists() or user.get().status == UserStatus.BANNED.value:
# 잘못된 사용자가 연결 시도하는 경우 바로 연결해제 처리
# 다음과 같이 emit하면 다른 사용자도 튕기는 문제가 발생함
# sio.emit("connect_error", {"message": "invalid user"})
# 클라이언트 코드:
# useEffect(() => {
# socket.on('connect_error', data => {
# if (data.message === 'invalid user') {
# socket.close();
# navigate('/');
# }
# });
# return () => {
# socket.close();
# };
# }, []);
# 따라서 emit없이 False 반환만 하면 권한없는 사용자가 채팅방에 접속할 수 없고 바로 연결 해제됨
return False
@sio.on('join')
def handle_join(sid, data):
"""
유저가 채팅방에 입장하면 아래 코드를 실행한다
- 브라우저에서 채팅방 입장시 connect 이벤트가 먼저 발생하고 이어서 join 이벤트가 발생됨
- data를 자바스크립트 객체로 받으면 파이썬 딕셔너리로 사용 가능
- data에는 유저 닉네임과 방 번호가 포함됨(key: nickname, room)
- save_session(sid, data): 채팅방에 접속한 클라이언트의 sid를 기준으로 data를 세션 데이터로 저장
"""
sio.save_session(sid, data)
sio.enter_room(sid, room=data['room'])
sio.emit(
'add_message',
{
"user_nickname": '함께하개 관리자',
"text": f"{data['nickname']}님이 들어왔어요."
},
to=data['room']
)
@sio.on('send_message')
def handle_send_message(sid, message, nickname, room, currentTime, userMbti, userImage, userId):
"""
send_message 이벤트로 받은 데이터를 MongoDB에 저장하고 add_message로 emit한다
- censor_text(): 욕설 필터링 함수, 채팅 메시지에 욕설이 있으면
- 그 부분만 *으로 수정한 문자열을 반환함
- save_message(): MongoDB에 메시지 데이터를 저장하고 고유한 ObjectId(24자리 문자열)반환
- 필요한 메시지 처리가 완료되면 data를 add_message로 emit
"""
data = {
"user_nickname": nickname,
"user_id" : userId,
"user_mbti" : userMbti,
"user_image" : userImage,
"text" : censor_text(message),
"message_id" : save_message(message, nickname, userId, room),
"time" : currentTime,
}
sio.emit('add_message', data, to=room)
@sio.event
def disconnect(sid):
"""
socket.io 클라이언트가 연결 해제되면 아래 코드를 실행한다
- get_session(sid): 위에서 save_session으로 저장한 sid별 세션 데이터 딕셔너리를 반환함
- disconnected_username: 연결 해제된 sid의 유저 닉네임
- room_number: 연결 해제된 sid가 있던 방 번호
- 퇴장한 유저 정보를 담아 add_message로 emit
"""
disconnected_username = sio.get_session(sid).get("nickname", "")
room_number = sio.get_session(sid).get("room")
sio.emit(
"add_message",
{
"user_nickname": "함께하개 관리자",
"text" : f"{disconnected_username}님이 퇴장하셨어요."
},
to=room_number,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment