├── .gitignore ├── LICENSE ├── README.md ├── django_socketio ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── fr └── README.md ├── manage.py ├── requirements.txt └── socketio_app ├── __init__.py ├── admin.py ├── apps.py ├── migrations └── __init__.py ├── models.py ├── static └── index.html ├── tests.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | log.txt 2 | 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | ._* 8 | 9 | 10 | .Spotlight-V100 11 | .Trashes 12 | 13 | .AppleDB 14 | .AppleDesktop 15 | .apdisk 16 | 17 | 18 | __pycache__/ 19 | *.py[cod] 20 | 21 | *.so 22 | 23 | .Python 24 | env/ 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | lib/ 31 | lib64/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | 39 | 40 | *.manifest 41 | *.spec 42 | 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | 47 | htmlcov/ 48 | .tox/ 49 | .coverage 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | 54 | *.mo 55 | *.pot 56 | 57 | docs/_build/ 58 | 59 | target/ 60 | 61 | 62 | *.log 63 | *.pot 64 | *.pyc 65 | __pycache__/ 66 | local_settings.py 67 | 68 | .env 69 | db.sqlite3 70 | 71 | staticfiles/ 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Miguel Grinberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-socket.io-django 2 | ![](https://img.shields.io/badge/Python-3.8.10-blue) 3 | ![](https://img.shields.io/badge/Django-3.2.6-%2344B78B) 4 | ![](https://img.shields.io/badge/socket.io-5.4.0-%23fff) 5 | 6 | Raw socket.io integration in a django server. 7 | 8 | 9 | - French version is [here](https://github.com/mokira3d48/python-socket.io-django/tree/master/fr) 10 | 11 |
12 | 13 | 14 | ## Prérequis 15 | 16 | Vous devez installer `python version 3.8.10 ou plus` sur votre machine. 17 | Sous Ubuntu : 18 | 19 | ``` 20 | sudo apt install python3 21 | ``` 22 | Sous certaines distributions linux, 23 | ``` 24 | sudo apt install python 25 | ``` 26 | 27 |
28 | 29 | PS : Si vous verifiez la version et qu'elle n'est la bonne `3.8.10 ou plus`, alors cherchez comment installer ou mettre 30 | à jour ce qui a été installé. 31 | 32 |
33 | 34 | Ensuite, installer le gestionnaire de dépendance `pip3` pour python3. 35 | Sous Ubuntu : 36 | 37 | ``` 38 | sudo apt install python3-pip 39 | ``` 40 | 41 | 42 |
43 | 44 | 45 | ## Version de python 46 | Exécutez la ligne de commande suivante pour vérifier votre version de `python`. 47 | ``` 48 | python --version 49 | ``` 50 | PS : Pour ceux qui sont sur certain système `Linux` notamment `Ubuntu`, exécuter plutôt la ligne de commande suivante : 51 | ``` 52 | python3 --version 53 | ``` 54 | 55 | Chez moi en ce moment, ma version de python est `3.8.10`. 56 | ``` 57 | Python 3.8.10 58 | ``` 59 | Je vous recommande d'avoir cette version ou une version supérieur à celle-ci. 60 | 61 |
62 | 63 | 64 | ## Configuration d'un environnement virtuel 65 | 66 | Avant d'installer les modules, il faut créer un environnement virtuel. C'est dans ce dernier, qu'on va 67 | installer les différents modules du serveur. 68 | 69 | 70 | 71 | ### Installation de virtualenv 72 | 73 | On va utiliser `virtualenv` pour créer un environnement virtuel. 74 | 75 | ``` 76 | sudo pip3 install virtualenv 77 | ``` 78 | 79 | Ensuite, dans le dossier du projet, créer un environement virtuel en tapant la commande suivante : 80 | 81 | ``` 82 | python3 -m venv env 83 | ``` 84 | 85 | 86 | ### Activation de l'environnement 87 | Enfin, on active l'environement virtuel 88 | 89 | ``` 90 | source env/bin/activate 91 | ``` 92 | 93 | Si tous va bien, on peut passer à l'installation des modules. 94 | 95 |
96 | 97 | 98 | ## Installation des modules 99 | Voici tous les modules dont on a besoin pour monter notre serveur socket. 100 | 101 | Contenu du fichier `requirements.txt` : 102 | 103 | ``` 104 | asgiref==3.4.1 105 | bidict==0.21.2 106 | Django==3.2.6 107 | dnspython==1.16.0 108 | enum-compat==0.0.3 109 | eventlet==0.30.0 110 | gevent==21.1.2 111 | gevent-websocket==0.10.1 112 | greenlet==0.4.17 113 | python-engineio==4.2.1 114 | python-socketio==5.4.0 115 | pytz==2021.1 116 | six==1.10.0 117 | sqlparse==0.4.1 118 | zope.event==4.5.0 119 | zope.interface==5.4.0 120 | 121 | ``` 122 | Utiliser la commande suivante pour installer tous les modules contenus dans le fichier. 123 | Vous pouvez aussi les installer un a un afin d'avoir leur dernière version. 124 | 125 | ``` 126 | pip install -r requirements.txt 127 | ``` 128 | 129 |
130 | 131 | ## Creation d'un projet Django 132 | On va maintenant créer un projet Django nommé `django_socketio` par exemple. 133 | 134 | ``` 135 | django-admin startproject django_socketio 136 | ``` 137 | 138 | Il faut créer ensuite une application. C'est dans cette dernière qu'on va implémenter un exemple de programme de chat pour tester notre serveur de `socket.io`. 139 | 140 | ``` 141 | django-admin startapp socketio_app 142 | ``` 143 | 144 |
145 | 146 | ## Configuration du projet 147 | On va placer les boûts de code qu'il faut dans certains fichiers de django. 148 | 149 | 150 | ### Configuration de l'URL 151 | 1. Dans le fichier `django_socketio/urls.py`, insérer la ligne suivante : 152 | 153 | ```python 154 | from django.conf.urls import url, include 155 | 156 | # ... 157 | ``` 158 | 159 | ensuite, 160 | 161 | ```python 162 | # ... 163 | 164 | urlpatterns = [ 165 | url(r'', include('socketio_app.urls')), 166 | path('admin/', admin.site.urls), 167 | ] 168 | ``` 169 | 170 |
171 | 172 | 2. Dans le dossier `django_socketio/socketio_app/`, créez le fichier `urls.py` et insérer s'y 173 | le code suivant : 174 | 175 | ```python 176 | from django.conf.urls import url 177 | 178 | from . import views 179 | 180 | urlpatterns = [ 181 | url(r'', views.index, name='index'), 182 | ]; 183 | 184 | ``` 185 | 186 | 187 | ### Configuration du serveur en socket.io 188 | On va maintenant mettre en place les fonctionnalités du serveur de socket.io. 189 | 190 | 1. Dans le fichier `django_socketio/socketio_app/views.py` insérer les lignes de code suivantes : 191 | 192 | ```python 193 | import socketio 194 | 195 | # mode d'asynchronisation 196 | async_mode = 'gevent'; 197 | 198 | # definition du serveur de socket.io 199 | sio = socketio.Server(async_mode=async_mode); 200 | 201 | 202 | ``` 203 | 204 |
205 | 206 | ![Stratégie de déploiement](https://www.botreetechnologies.com/blog/wp-content/uploads/2020/12/deployment-strategy.jpg, "Stratégie de déploiement") 207 | 208 | - Le déploiement est délicat, car les sockets ne sont pas basés sur le protocole HTTP. Le serveur d'applications alloue généralement un processus ou un fil distinct pour chaque demande. Par conséquent, nous devons utiliser `Gevent`, qui agit comme une boucle d'événements et chaque fois qu'il y a une demande de connexion, il génère un nouveau thread et attribue la connexion à ce thread. 209 | 210 | - Nous avons décidé de séparer l'application socket de l'application normale, car la prise en charge à la fois de la fonction Django normale et de l'application socket dans une seule `application Django` rendait la gestion des réponses aux requêtes lente. 211 | 212 | - Le déplacement du code socketio vers une autre application a également facilité la maintenance du code. 213 | 214 | Par [ici](https://www.botreetechnologies.com/blog/django-websocket-with-socketio/) pour en savoir plus. 215 | 216 |
217 | 218 | 219 | 2. Remplacez les lignes de code du fichier `django_socketio/wsgi.py` par les suivantes : 220 | 221 | ```python 222 | import os 223 | import socketio 224 | 225 | from django.core.wsgi import get_wsgi_application 226 | from socketio_app.views import sio 227 | 228 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_socketio.settings'); 229 | 230 | django_app = get_wsgi_application(); 231 | application = socketio.WSGIApp(sio, django_app); 232 | 233 | #################################################################################### 234 | 235 | from gevent import pywsgi 236 | from geventwebsocket.handler import WebSocketHandler 237 | 238 | server = pywsgi.WSGIServer(("", 8000), application, handler_class=WebSocketHandler); 239 | server.serve_forever(); 240 | 241 | ``` 242 | 243 |
244 | 245 | ## Implémentation des exemples de fonctionnalités avec socket.io 246 | On va mettre en place le programme serveur et un programme client. 247 | 248 | ### Programme serveur 249 | 250 | On va juste essayer d'implémenter un programme de chat. Remplacez donc tous le code contenu dans le fichier `django_socketio/socketio_app/views.py` par les lignes de code suivantes : 251 | 252 | ```python 253 | # définissez async_mode sur 'threading', 'eventlet', 'gevent' ou 'gevent_uwsgi' sur 254 | # forcer un autre mode, le meilleur mode est sélectionné automatiquement parmi ce qui est 255 | # installée 256 | sync_mode = 'gevent'; 257 | 258 | import os 259 | 260 | from django.http import HttpResponse 261 | import socketio 262 | 263 | basedir = os.path.dirname(os.path.realpath(__file__)); 264 | sio = socketio.Server(async_mode=async_mode); 265 | 266 | # thread = None 267 | users = {}; 268 | 269 | def index(request): 270 | """ 271 | Programme qui permet de renvoiyer la page web `index` 272 | """ 273 | 274 | # global thread; 275 | 276 | # if thread is None: 277 | # thread = sio.start_background_task(background_thread); 278 | 279 | return HttpResponse(open(os.path.join(basedir, 'static/index.html'))); 280 | 281 | 282 | # def background_thread(): 283 | # """ Exemple de programme d'execution de programme d'arriere plan """ 284 | 285 | # count = 0; 286 | 287 | # while True: 288 | # sio.sleep(10); 289 | # count += 1; 290 | # sio.emit('my_response', {'data': 'Server generated event'}, namespace='/test'); 291 | 292 | @sio.event 293 | def set_username(sid, message): 294 | """ Programme de modification du nom d'utilisateur """ 295 | users[sid] = message['data']; 296 | 297 | # on notifit que le username a ete correctement notifie 298 | sio.emit('my_response', {'data': f"Username is set to {users[sid]} !"}, to=sid); 299 | 300 | 301 | 302 | @sio.event 303 | def my_event(sid, message): 304 | # Programme qui permet d'envoyer le message a moi meme 305 | sio.emit('my_response', {'data': message['data']}, room=sid); 306 | 307 | 308 | 309 | @sio.event 310 | def my_broadcast_event(sid, message): 311 | # Programme qui permet d'envoyer le message a tous le monde 312 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}); 313 | 314 | 315 | 316 | @sio.event 317 | def join(sid, message): 318 | """ Programme de creation et d'adesion de canale """ 319 | 320 | # on cree le canale et on se join a ce canal 321 | sio.enter_room(sid, message['room']); 322 | 323 | # sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, room=sid); 324 | 325 | # on emet a tous ceux qui sont dans le canal qu'on de 326 | # rejoindre le canal 327 | sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, to=message['room']); 328 | 329 | 330 | @sio.event 331 | def leave(sid, message): 332 | """ Programme de deconnection d'un canal """ 333 | 334 | # on se deconnecte du canal 335 | sio.leave_room(sid, message['room']); 336 | 337 | # on informe tous ceux qui sont dans le canal, que celui-ci 338 | # a quitte le canal 339 | sio.emit('my_response', {'data': users[sid] + ' left room: ' + message['room']}, room=message['room']); 340 | 341 | 342 | @sio.event 343 | def close_room(sid, message): 344 | """ Programme de fermeture d'un canal """ 345 | 346 | # on ferme le canal 347 | sio.close_room(message['room']); 348 | sio.emit('my_response', {'data': 'Room ' + message['room'] + ' is closing.'}, room=message['room']); 349 | 350 | 351 | @sio.event 352 | def my_room_event(sid, message): 353 | """ Programme qui permet d'envoyer un message a tous les membres du canal """ 354 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}, room=message['room']); 355 | 356 | 357 | @sio.event 358 | def disconnect_request(sid): 359 | """ Programme qui declanche la deconnection de l'utilisateur """ 360 | sio.disconnect(sid); 361 | 362 | 363 | @sio.event 364 | def connect(sid, environ): 365 | """ Programme de connexion 366 | Au cour de la connexion, on enregistre tous les utilisateurs 367 | connectes 368 | """ 369 | 370 | print(f"{sid}\t connected"); 371 | 372 | # on ajoute le nouveau a la liste 373 | users[sid] = None; 374 | 375 | # on lui notifie qu'il s'est bien connecte 376 | sio.emit('my_response', {'data': 'Connected', 'count': len(users)}, room=sid); 377 | 378 | # on notifie a tous le monde le nombre de personnes actuellement connectes 379 | sio.emit('my_response', {'data': f'{len(users)} connected now!', 'count': len(users)}); 380 | 381 | 382 | @sio.event 383 | def disconnect(sid): 384 | """ Programme de deconnexion 385 | Lors de la deconnexion, on supprime l'utilisateur de la liste des 386 | connectes 387 | """ 388 | 389 | print(f"{sid}\t {users[sid]} disconnected"); 390 | 391 | # on notifie a tous le monde le nombre de personnes actuellement connectes 392 | sio.emit('my_response', {'data': f"{users[sid]} is disconnected", 'count': len(users)}); 393 | 394 | # on le supprime de la liste 395 | del users[sid]; 396 | 397 | # on notifie a tous le monde le nombre de personnes actuellement connectes 398 | sio.emit('my_response', {'data': f'{len(users)} connected', 'count': len(users)}); 399 | 400 | 401 | ``` 402 | 403 | ### Programme client 404 | En effet, il s'agit de mettre en place une interface WEB.
405 | Dans le dossier `django_socketio/socketio_app/`, créez un dossier nommé `static`, ensuite, dans ce dernier, créer un fichier nommé `index.html`. Dans ce fichier, insérez les lignes de code suivantes : 406 | 407 | ```html 408 | 409 | 410 | 411 | Django + SocketIO Test 412 | 413 | 414 | 466 | 467 | 468 |

Django + SocketIO Test

469 |

Send:

470 |
471 | 472 | 473 |
474 |
475 | 476 | 477 |
478 |
479 | 480 | 481 |
482 |
483 | 484 | 485 |
486 |
487 | 488 | 489 |
490 |
491 | 492 | 493 | 494 |
495 |
496 | 497 | 498 |
499 |
500 | 501 |
502 |

Receive:

503 |

504 | 505 | 506 | 507 | ``` 508 | 509 |
510 | 511 | ## Démarrage du serveur 512 | N'oublier pas de sauvegarder tous les fichiers sources. Il est temps de tester notre programme. 513 | On doit d'abord faire un migration dans une base de données avant de démarrer le serveur. 514 | Pour faire la migration, tappez la commande suivante : 515 | 516 | ``` 517 | ./manage.py migrate 518 | ``` 519 | 520 | On peut maintenant démarrer le serveur avec la commande suivante : 521 | 522 | ``` 523 | ./manage.py runserver 524 | ``` 525 | 526 | Si tout va bien, vous verrez le message suivant affichez dans votre terminal. 527 | 528 | ``` 529 | Watching for file changes with StatReloader 530 | Performing system checks... 531 | 532 | System check identified no issues (0 silenced). 533 | August 30, 2021 - 01:55:05 534 | Django version 3.2.6, using settings 'django_socketio.settings' 535 | Starting development server at http://127.0.0.1:8000/ 536 | Quit the server with CONTROL-C. 537 | ``` 538 | 539 |
540 | 541 | ## Test 542 | Voici le lien pour accéder à l'interface web : [http://127.0.0.1:8000/](http://127.0.0.1:8000/). 543 | Pour que d'autre ordinateur puisse accéder à votre application, il faut qu'ils soient dans le même réseau que votre ordinateur. Ensuite, ils doivent utiliser votre adresse IP (souvent sous la forme `192.168.xxx.xxx`). Pour connaitre votre adresse IP : 544 | 545 | - sous linux : 546 | 547 | ``` 548 | sudo ifconfig 549 | ``` 550 | 551 | - sous windows : 552 | 553 | ``` 554 | ipconfig 555 | ``` 556 | Ensuite rendez-vous dans le fichier `django_socketio/settings.py` pour permettre l'accès au serveur via cet adresse IP. Si par exemple mon adresse IP est `192.168.100.31`, alors vous devez modifier la ligne de code suivante dans le fichier `django_socketio/settings.py` 557 | 558 | ```python 559 | # ... 560 | 561 | ALLOWED_HOSTS = ['192.168.100.31']; 562 | 563 | # La ligne ci-dessus permettra au autre ordinateur qui sont sur le même réseau que moi 564 | # de pouvoir accéder à mon serveur socket.io via le lien http://192.168.100.31:8000/ 565 | 566 | # ... 567 | ``` 568 | 569 | 570 |
571 | 572 | ## Système d'exploitation 573 | 574 | ``` 575 | Kernel: 5.8.0-59-generic x86_64 bits: 64 compiler: N/A Desktop: Gnome 3.36.9 576 | Distro: Ubuntu 20.04.2 LTS (Focal Fossa) 577 | ``` 578 | 579 | 580 | -------------------------------------------------------------------------------- /django_socketio/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokira3d48/python-socket.io-django/c6495f692c7915c00163e3cb19c85d9a9a17bf3d/django_socketio/__init__.py -------------------------------------------------------------------------------- /django_socketio/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for django_socketio project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_socketio.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /django_socketio/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_socketio project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-ie=_%as-#k!0^%sxe)^jce#ikpu31z*o_k@-6f(@bh%@dcdsfz' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = []; 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'socketio_app', 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'django_socketio.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'django_socketio.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': BASE_DIR / 'db.sqlite3', 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | 123 | # Default primary key field type 124 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 125 | 126 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 127 | -------------------------------------------------------------------------------- /django_socketio/urls.py: -------------------------------------------------------------------------------- 1 | """django_socketio URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url, include 17 | from django.contrib import admin 18 | from django.urls import path 19 | 20 | 21 | urlpatterns = [ 22 | url(r'', include('socketio_app.urls')), 23 | path('admin/', admin.site.urls), 24 | ] 25 | -------------------------------------------------------------------------------- /django_socketio/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_socketio project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | import socketio 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | from socketio_app.views import sio 15 | 16 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_socketio.settings'); 17 | 18 | django_app = get_wsgi_application(); 19 | application = socketio.WSGIApp(sio, django_app); 20 | 21 | #################################################################################### 22 | 23 | from gevent import pywsgi 24 | from geventwebsocket.handler import WebSocketHandler 25 | 26 | server = pywsgi.WSGIServer(("", 8000), application, handler_class=WebSocketHandler); 27 | server.serve_forever(); 28 | 29 | -------------------------------------------------------------------------------- /fr/README.md: -------------------------------------------------------------------------------- 1 | # python-socket.io-django 2 | Exemple de programme `python` de `socket.io` avec le framework `Django`. 3 | 4 |
5 | 6 | 7 | ## Prérequis 8 | 9 | Vous devez installer `python version 3.8.10 ou plus` sur votre machine. 10 | Sous Ubuntu : 11 | 12 | ``` 13 | sudo apt install python3 14 | ``` 15 | Sous certaines distributions linux, 16 | ``` 17 | sudo apt install python 18 | ``` 19 | 20 |
21 | 22 | PS : Si vous verifiez la version et qu'elle n'est la bonne `3.8.10 ou plus`, alors cherchez comment installer ou mettre 23 | à jour ce qui a été installé. 24 | 25 |
26 | 27 | Ensuite, installer le gestionnaire de dépendance `pip3` pour python3. 28 | Sous Ubuntu : 29 | 30 | ``` 31 | sudo apt install python3-pip 32 | ``` 33 | 34 | 35 |
36 | 37 | 38 | ## Version de python 39 | Exécutez la ligne de commande suivante pour vérifier votre version de `python`. 40 | ``` 41 | python --version 42 | ``` 43 | PS : Pour ceux qui sont sur certain système `Linux` notamment `Ubuntu`, exécuter plutôt la ligne de commande suivante : 44 | ``` 45 | python3 --version 46 | ``` 47 | 48 | Chez moi en ce moment, ma version de python est `3.8.10`. 49 | ``` 50 | Python 3.8.10 51 | ``` 52 | Je vous recommande d'avoir cette version ou une version supérieur à celle-ci. 53 | 54 |
55 | 56 | 57 | ## Configuration d'un environnement virtuel 58 | 59 | Avant d'installer les modules, il faut créer un environnement virtuel. C'est dans ce dernier, qu'on va 60 | installer les différents modules du serveur. 61 | 62 | 63 | 64 | ### Installation de virtualenv 65 | 66 | On va utiliser `virtualenv` pour créer un environnement virtuel. 67 | 68 | ``` 69 | sudo pip3 install virtualenv 70 | ``` 71 | 72 | Ensuite, dans le dossier du projet, créer un environement virtuel en tapant la commande suivante : 73 | 74 | ``` 75 | python3 -m venv env 76 | ``` 77 | 78 | 79 | ### Activation de l'environnement 80 | Enfin, on active l'environement virtuel 81 | 82 | ``` 83 | source env/bin/activate 84 | ``` 85 | 86 | Si tous va bien, on peut passer à l'installation des modules. 87 | 88 |
89 | 90 | 91 | ## Installation des modules 92 | Voici tous les modules dont on a besoin pour monter notre serveur socket. 93 | 94 | Contenu du fichier `requirements.txt` : 95 | 96 | ``` 97 | asgiref==3.4.1 98 | bidict==0.21.2 99 | Django==3.2.6 100 | dnspython==1.16.0 101 | enum-compat==0.0.3 102 | eventlet==0.30.0 103 | gevent==21.1.2 104 | gevent-websocket==0.10.1 105 | greenlet==0.4.17 106 | python-engineio==4.2.1 107 | python-socketio==5.4.0 108 | pytz==2021.1 109 | six==1.10.0 110 | sqlparse==0.4.1 111 | zope.event==4.5.0 112 | zope.interface==5.4.0 113 | 114 | ``` 115 | Utiliser la commande suivante pour installer tous les modules contenus dans le fichier. 116 | Vous pouvez aussi les installer un a un afin d'avoir leur dernière version. 117 | 118 | ``` 119 | pip install -r requirements.txt 120 | ``` 121 | 122 |
123 | 124 | ## Creation d'un projet Django 125 | On va maintenant créer un projet Django nommé `django_socketio` par exemple. 126 | 127 | ``` 128 | django-admin startproject django_socketio 129 | ``` 130 | 131 | Il faut créer ensuite une application. C'est dans cette dernière qu'on va implémenter un exemple de programme de chat pour tester notre serveur de `socket.io`. 132 | 133 | ``` 134 | django-admin startapp socketio_app 135 | ``` 136 | 137 |
138 | 139 | ## Configuration du projet 140 | On va placer les boûts de code qu'il faut dans certains fichiers de django. 141 | 142 | 143 | ### Configuration de l'URL 144 | 1. Dans le fichier `django_socketio/urls.py`, insérer la ligne suivante : 145 | 146 | ```python 147 | from django.conf.urls import url, include 148 | 149 | # ... 150 | ``` 151 | 152 | ensuite, 153 | 154 | ```python 155 | # ... 156 | 157 | urlpatterns = [ 158 | url(r'', include('socketio_app.urls')), 159 | path('admin/', admin.site.urls), 160 | ] 161 | ``` 162 | 163 |
164 | 165 | 2. Dans le dossier `django_socketio/socketio_app/`, créez le fichier `urls.py` et insérer s'y 166 | le code suivant : 167 | 168 | ```python 169 | from django.conf.urls import url 170 | 171 | from . import views 172 | 173 | urlpatterns = [ 174 | url(r'', views.index, name='index'), 175 | ]; 176 | 177 | ``` 178 | 179 | 180 | ### Configuration du serveur en socket.io 181 | On va maintenant mettre en place les fonctionnalités du serveur de socket.io. 182 | 183 | 1. Dans le fichier `django_socketio/socketio_app/views.py` insérer les lignes de code suivantes : 184 | 185 | ```python 186 | import socketio 187 | 188 | # mode d'asynchronisation 189 | async_mode = 'gevent'; 190 | 191 | # definition du serveur de socket.io 192 | sio = socketio.Server(async_mode=async_mode); 193 | 194 | 195 | ``` 196 | 197 |
198 | 199 | ![Stratégie de déploiement](https://www.botreetechnologies.com/blog/wp-content/uploads/2020/12/deployment-strategy.jpg, "Stratégie de déploiement") 200 | 201 | - Le déploiement est délicat, car les sockets ne sont pas basés sur le protocole HTTP. Le serveur d'applications alloue généralement un processus ou un fil distinct pour chaque demande. Par conséquent, nous devons utiliser `Gevent`, qui agit comme une boucle d'événements et chaque fois qu'il y a une demande de connexion, il génère un nouveau thread et attribue la connexion à ce thread. 202 | 203 | - Nous avons décidé de séparer l'application socket de l'application normale, car la prise en charge à la fois de la fonction Django normale et de l'application socket dans une seule `application Django` rendait la gestion des réponses aux requêtes lente. 204 | 205 | - Le déplacement du code socketio vers une autre application a également facilité la maintenance du code. 206 | 207 | Par [ici](https://www.botreetechnologies.com/blog/django-websocket-with-socketio/) pour en savoir plus. 208 | 209 |
210 | 211 | 212 | 2. Remplacez les lignes de code du fichier `django_socketio/wsgi.py` par les suivantes : 213 | 214 | ```python 215 | import os 216 | import socketio 217 | 218 | from django.core.wsgi import get_wsgi_application 219 | from socketio_app.views import sio 220 | 221 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_socketio.settings'); 222 | 223 | django_app = get_wsgi_application(); 224 | application = socketio.WSGIApp(sio, django_app); 225 | 226 | #################################################################################### 227 | 228 | from gevent import pywsgi 229 | from geventwebsocket.handler import WebSocketHandler 230 | 231 | server = pywsgi.WSGIServer(("", 8000), application, handler_class=WebSocketHandler); 232 | server.serve_forever(); 233 | 234 | ``` 235 | 236 |
237 | 238 | ## Implémentation des exemples de fonctionnalités avec socket.io 239 | On va mettre en place le programme serveur et un programme client. 240 | 241 | ### Programme serveur 242 | 243 | On va juste essayer d'implémenter un programme de chat. Remplacez donc tous le code contenu dans le fichier `django_socketio/socketio_app/views.py` par les lignes de code suivantes : 244 | 245 | ```python 246 | # définissez async_mode sur 'threading', 'eventlet', 'gevent' ou 'gevent_uwsgi' sur 247 | # forcer un autre mode, le meilleur mode est sélectionné automatiquement parmi ce qui est 248 | # installée 249 | sync_mode = 'gevent'; 250 | 251 | import os 252 | 253 | from django.http import HttpResponse 254 | import socketio 255 | 256 | basedir = os.path.dirname(os.path.realpath(__file__)); 257 | sio = socketio.Server(async_mode=async_mode); 258 | 259 | # thread = None 260 | users = {}; 261 | 262 | def index(request): 263 | """ 264 | Programme qui permet de renvoiyer la page web `index` 265 | """ 266 | 267 | # global thread; 268 | 269 | # if thread is None: 270 | # thread = sio.start_background_task(background_thread); 271 | 272 | return HttpResponse(open(os.path.join(basedir, 'static/index.html'))); 273 | 274 | 275 | # def background_thread(): 276 | # """ Exemple de programme d'execution de programme d'arriere plan """ 277 | 278 | # count = 0; 279 | 280 | # while True: 281 | # sio.sleep(10); 282 | # count += 1; 283 | # sio.emit('my_response', {'data': 'Server generated event'}, namespace='/test'); 284 | 285 | @sio.event 286 | def set_username(sid, message): 287 | """ Programme de modification du nom d'utilisateur """ 288 | users[sid] = message['data']; 289 | 290 | # on notifit que le username a ete correctement notifie 291 | sio.emit('my_response', {'data': f"Username is set to {users[sid]} !"}, to=sid); 292 | 293 | 294 | 295 | @sio.event 296 | def my_event(sid, message): 297 | # Programme qui permet d'envoyer le message a moi meme 298 | sio.emit('my_response', {'data': message['data']}, room=sid); 299 | 300 | 301 | 302 | @sio.event 303 | def my_broadcast_event(sid, message): 304 | # Programme qui permet d'envoyer le message a tous le monde 305 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}); 306 | 307 | 308 | 309 | @sio.event 310 | def join(sid, message): 311 | """ Programme de creation et d'adesion de canale """ 312 | 313 | # on cree le canale et on se join a ce canal 314 | sio.enter_room(sid, message['room']); 315 | 316 | # sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, room=sid); 317 | 318 | # on emet a tous ceux qui sont dans le canal qu'on de 319 | # rejoindre le canal 320 | sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, to=message['room']); 321 | 322 | 323 | @sio.event 324 | def leave(sid, message): 325 | """ Programme de deconnection d'un canal """ 326 | 327 | # on se deconnecte du canal 328 | sio.leave_room(sid, message['room']); 329 | 330 | # on informe tous ceux qui sont dans le canal, que celui-ci 331 | # a quitte le canal 332 | sio.emit('my_response', {'data': users[sid] + ' left room: ' + message['room']}, room=message['room']); 333 | 334 | 335 | @sio.event 336 | def close_room(sid, message): 337 | """ Programme de fermeture d'un canal """ 338 | 339 | # on ferme le canal 340 | sio.close_room(message['room']); 341 | sio.emit('my_response', {'data': 'Room ' + message['room'] + ' is closing.'}, room=message['room']); 342 | 343 | 344 | @sio.event 345 | def my_room_event(sid, message): 346 | """ Programme qui permet d'envoyer un message a tous les membres du canal """ 347 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}, room=message['room']); 348 | 349 | 350 | @sio.event 351 | def disconnect_request(sid): 352 | """ Programme qui declanche la deconnection de l'utilisateur """ 353 | sio.disconnect(sid); 354 | 355 | 356 | @sio.event 357 | def connect(sid, environ): 358 | """ Programme de connexion 359 | Au cour de la connexion, on enregistre tous les utilisateurs 360 | connectes 361 | """ 362 | 363 | print(f"{sid}\t connected"); 364 | 365 | # on ajoute le nouveau a la liste 366 | users[sid] = None; 367 | 368 | # on lui notifie qu'il s'est bien connecte 369 | sio.emit('my_response', {'data': 'Connected', 'count': len(users)}, room=sid); 370 | 371 | # on notifie a tous le monde le nombre de personnes actuellement connectes 372 | sio.emit('my_response', {'data': f'{len(users)} connected now!', 'count': len(users)}); 373 | 374 | 375 | @sio.event 376 | def disconnect(sid): 377 | """ Programme de deconnexion 378 | Lors de la deconnexion, on supprime l'utilisateur de la liste des 379 | connectes 380 | """ 381 | 382 | print(f"{sid}\t {users[sid]} disconnected"); 383 | 384 | # on notifie a tous le monde le nombre de personnes actuellement connectes 385 | sio.emit('my_response', {'data': f"{users[sid]} is disconnected", 'count': len(users)}); 386 | 387 | # on le supprime de la liste 388 | del users[sid]; 389 | 390 | # on notifie a tous le monde le nombre de personnes actuellement connectes 391 | sio.emit('my_response', {'data': f'{len(users)} connected', 'count': len(users)}); 392 | 393 | 394 | 395 | 396 | ``` 397 | 398 | ### Programme client 399 | En effet, il s'agit de mettre en place une interface WEB.
400 | Dans le dossier `django_socketio/socketio_app/`, créez un dossier nommé `static`, ensuite, dans ce dernier, créer un fichier nommé `index.html`. Dans ce fichier, insérez les lignes de code suivantes : 401 | 402 | ```html 403 | 404 | 405 | 406 | Django + SocketIO Test 407 | 408 | 409 | 461 | 462 | 463 |

Django + SocketIO Test

464 |

Send:

465 |
466 | 467 | 468 |
469 |
470 | 471 | 472 |
473 |
474 | 475 | 476 |
477 |
478 | 479 | 480 |
481 |
482 | 483 | 484 |
485 |
486 | 487 | 488 | 489 |
490 |
491 | 492 | 493 |
494 |
495 | 496 |
497 |

Receive:

498 |

499 | 500 | 501 | 502 | ``` 503 | 504 |
505 | 506 | ## Démarrage du serveur 507 | N'oublier pas de sauvegarder tous les fichiers sources. Il est temps de tester notre programme. 508 | On doit d'abord faire un migration dans une base de données avant de démarrer le serveur. 509 | Pour faire la migration, tappez la commande suivante : 510 | 511 | ``` 512 | ./manage.py migrate 513 | ``` 514 | 515 | On peut maintenant démarrer le serveur avec la commande suivante : 516 | 517 | ``` 518 | ./manage.py runserver 519 | ``` 520 | 521 | Si tout va bien, vous verrez le message suivant affichez dans votre terminal. 522 | 523 | ``` 524 | Watching for file changes with StatReloader 525 | Performing system checks... 526 | 527 | System check identified no issues (0 silenced). 528 | August 30, 2021 - 01:55:05 529 | Django version 3.2.6, using settings 'django_socketio.settings' 530 | Starting development server at http://127.0.0.1:8000/ 531 | Quit the server with CONTROL-C. 532 | ``` 533 | 534 |
535 | 536 | ## Test 537 | Voici le lien pour accéder à l'interface web : [http://127.0.0.1:8000/](http://127.0.0.1:8000/). 538 | Pour que d'autre ordinateur puisse accéder à votre application, il faut qu'ils soient dans le même réseau que votre ordinateur. Ensuite, ils doivent utiliser votre adresse IP (souvent sous la forme `192.168.xxx.xxx`). Pour connaitre votre adresse IP : 539 | 540 | - sous linux : 541 | 542 | ``` 543 | sudo ifconfig 544 | ``` 545 | 546 | - sous windows : 547 | 548 | ``` 549 | ipconfig 550 | ``` 551 | Ensuite rendez-vous dans le fichier `django_socketio/settings.py` pour permettre l'accès au serveur via cet adresse IP. Si par exemple mon adresse IP est `192.168.100.31`, alors vous devez modifier la ligne de code suivante dans le fichier `django_socketio/settings.py` 552 | 553 | ```python 554 | # ... 555 | 556 | ALLOWED_HOSTS = ['192.168.100.31']; 557 | 558 | # La ligne ci-dessus permettra au autre ordinateur qui sont sur le même réseau que moi 559 | # de pouvoir accéder à mon serveur socket.io via le lien http://192.168.100.31:8000/ 560 | 561 | # ... 562 | ``` 563 | 564 | 565 |
566 | 567 | ## Système d'exploitation 568 | 569 | ``` 570 | Kernel: 5.8.0-59-generic x86_64 bits: 64 compiler: N/A Desktop: Gnome 3.36.9 571 | Distro: Ubuntu 20.04.2 LTS (Focal Fossa) 572 | ``` 573 | 574 | 575 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_socketio.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.4.1 2 | bidict==0.21.2 3 | Django==3.2.6 4 | dnspython==1.16.0 5 | enum-compat==0.0.3 6 | eventlet==0.30.0 7 | gevent==21.1.2 8 | gevent-websocket==0.10.1 9 | greenlet==0.4.17 10 | python-engineio==4.2.1 11 | python-socketio==5.4.0 12 | pytz==2021.1 13 | six==1.10.0 14 | sqlparse==0.4.1 15 | zope.event==4.5.0 16 | zope.interface==5.4.0 17 | -------------------------------------------------------------------------------- /socketio_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokira3d48/python-socket.io-django/c6495f692c7915c00163e3cb19c85d9a9a17bf3d/socketio_app/__init__.py -------------------------------------------------------------------------------- /socketio_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /socketio_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SocketioAppConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'socketio_app' 7 | -------------------------------------------------------------------------------- /socketio_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokira3d48/python-socket.io-django/c6495f692c7915c00163e3cb19c85d9a9a17bf3d/socketio_app/migrations/__init__.py -------------------------------------------------------------------------------- /socketio_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /socketio_app/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Django + SocketIO Test 5 | 6 | 7 | 59 | 60 | 61 |

Django + SocketIO Test

62 |

Send:

63 |
64 | 65 | 66 |
67 |
68 | 69 | 70 |
71 |
72 | 73 | 74 |
75 |
76 | 77 | 78 |
79 |
80 | 81 | 82 |
83 |
84 | 85 | 86 | 87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 |
95 |

Receive:

96 |

97 | 98 | -------------------------------------------------------------------------------- /socketio_app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /socketio_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r'', views.index, name='index'), 7 | ]; 8 | 9 | -------------------------------------------------------------------------------- /socketio_app/views.py: -------------------------------------------------------------------------------- 1 | # set async_mode to 'threading', 'eventlet', 'gevent' or 'gevent_uwsgi' to 2 | # force a mode else, the best mode is selected automatically from what's 3 | # installed 4 | async_mode = 'gevent'; 5 | 6 | import os 7 | 8 | from django.http import HttpResponse 9 | import socketio 10 | 11 | basedir = os.path.dirname(os.path.realpath(__file__)); 12 | sio = socketio.Server(async_mode=async_mode); 13 | 14 | # thread = None 15 | users = {}; 16 | 17 | def index(request): 18 | """ 19 | Programme qui permet de renvoiyer la page web `index` 20 | """ 21 | 22 | # global thread; 23 | 24 | # if thread is None: 25 | # thread = sio.start_background_task(background_thread); 26 | 27 | return HttpResponse(open(os.path.join(basedir, 'static/index.html'))); 28 | 29 | 30 | # def background_thread(): 31 | # """ Exemple de programme d'execution de programme d'arriere plan """ 32 | 33 | # count = 0; 34 | 35 | # while True: 36 | # sio.sleep(10); 37 | # count += 1; 38 | # sio.emit('my_response', {'data': 'Server generated event'}, namespace='/test'); 39 | 40 | @sio.event 41 | def set_username(sid, message): 42 | """ Programme de modification du nom d'utilisateur """ 43 | users[sid] = message['data']; 44 | 45 | # on notifit que le username a ete correctement notifie 46 | sio.emit('my_response', {'data': f"Username is set to {users[sid]} !"}, to=sid); 47 | 48 | 49 | 50 | @sio.event 51 | def my_event(sid, message): 52 | # Programme qui permet d'envoyer le message a moi meme 53 | sio.emit('my_response', {'data': message['data']}, room=sid); 54 | 55 | 56 | 57 | @sio.event 58 | def my_broadcast_event(sid, message): 59 | # Programme qui permet d'envoyer le message a tous le monde 60 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}); 61 | 62 | 63 | 64 | @sio.event 65 | def join(sid, message): 66 | """ Programme de creation et d'adesion de canale """ 67 | 68 | # on cree le canale et on se join a ce canal 69 | sio.enter_room(sid, message['room']); 70 | 71 | # sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, room=sid); 72 | 73 | # on emet a tous ceux qui sont dans le canal qu'on de 74 | # rejoindre le canal 75 | sio.emit('my_response', {'data': 'Entered room: ' + message['room']}, to=message['room']); 76 | 77 | 78 | @sio.event 79 | def leave(sid, message): 80 | """ Programme de deconnection d'un canal """ 81 | 82 | # on se deconnecte du canal 83 | sio.leave_room(sid, message['room']); 84 | 85 | # on informe tous ceux qui sont dans le canal, que celui-ci 86 | # a quitte le canal 87 | sio.emit('my_response', {'data': users[sid] + ' left room: ' + message['room']}, room=message['room']); 88 | 89 | 90 | @sio.event 91 | def close_room(sid, message): 92 | """ Programme de fermeture d'un canal """ 93 | 94 | # on ferme le canal 95 | sio.close_room(message['room']); 96 | sio.emit('my_response', {'data': 'Room ' + message['room'] + ' is closing.'}, room=message['room']); 97 | 98 | 99 | @sio.event 100 | def my_room_event(sid, message): 101 | """ Programme qui permet d'envoyer un message a tous les membres du canal """ 102 | sio.emit('my_response', {'data': f"[{users[sid]}] {message['data']}"}, room=message['room']); 103 | 104 | 105 | @sio.event 106 | def disconnect_request(sid): 107 | """ Programme qui declanche la deconnection de l'utilisateur """ 108 | sio.disconnect(sid); 109 | 110 | 111 | @sio.event 112 | def connect(sid, environ): 113 | """ Programme de connexion 114 | Au cour de la connexion, on enregistre tous les utilisateurs 115 | connectes 116 | """ 117 | 118 | print(f"{sid}\t connected"); 119 | 120 | # on ajoute le nouveau a la liste 121 | users[sid] = None; 122 | 123 | # on lui notifie qu'il s'est bien connecte 124 | sio.emit('my_response', {'data': 'Connected', 'count': len(users)}, room=sid); 125 | 126 | # on notifie a tous le monde le nombre de personnes actuellement connectes 127 | sio.emit('my_response', {'data': f'{len(users)} connected now!', 'count': len(users)}); 128 | 129 | 130 | @sio.event 131 | def disconnect(sid): 132 | """ Programme de deconnexion 133 | Lors de la deconnexion, on supprime l'utilisateur de la liste des 134 | connectes 135 | """ 136 | 137 | print(f"{sid}\t {users[sid]} disconnected"); 138 | 139 | # on notifie a tous le monde le nombre de personnes actuellement connectes 140 | sio.emit('my_response', {'data': f"{users[sid]} is disconnected", 'count': len(users)}); 141 | 142 | # on le supprime de la liste 143 | del users[sid]; 144 | 145 | # on notifie a tous le monde le nombre de personnes actuellement connectes 146 | sio.emit('my_response', {'data': f'{len(users)} connected', 'count': len(users)}); 147 | 148 | 149 | --------------------------------------------------------------------------------