Partage
  • Partager sur Facebook
  • Partager sur Twitter

Chat en temps réels avec websocket + symfony 7

Chat en temps réels avec websocket + symfony 7

    1 janvier 2025 à 10:13:55

    Bonjour et bonne année à tous !

    Je crée actuellement un site web sous Symfony 7 en utilisant WebSocket pour un chat en temps réel. (Mon souci vient de WebSocket, voilà pourquoi je poste dans la section JS.)

    Je suis sous Windows et XAMPP.

    Quand je suis connecté avec 1 utilisateur courant sur un onglet, le message dans le chat s'envoie en 1 seconde. Cependant, quand je suis connecté avec 6 utilisateurs différents sur 6 onglets différents, le message met plus de 40 secondes à s'envoyer. Je me que quand je vais mettre le site en ligne j'aurais le même soucis mais pire comme il y aura plusieurs personnes connecter. 

    Je souhaiterais que le message s'envoie en 1 seconde :

    Mon fichier server.js : 

    const { Server } = require('socket.io');
    const http = require('http');
    const cors = require('cors');
    const express = require('express');
    const app = express();
    
    app.use(cors());
    app.use(express.json());
    
    const server = http.createServer(app);
    const io = new Server(server, {
      cors: {
        origin: "https://127.0.0.1:8000", 
        methods: ["GET", "POST"]
      }
    });
    
    io.on('connection', (socket) => {
      console.log('a user connected');
      socket.on('disconnect', () => {
        console.log('user disconnected');
      });
    });
    
    app.post('/', (req, res) => {
      const message = req.body;
      io.emit('chat message', message);
      res.sendStatus(200);
    });
    
    server.listen(3000, () => {
      console.log('listening on *:3000');
    });

    Ma vue twig  (websocket + chat ) :

     {# DEBUT CHAT MESSAGE #}
    	{% if game.getParticipants.contains(app.user) or game.creator == app.user %}
    		<h2>Chat de la partie</h2>
    		<div id="chat-messages">
    			{% for chatMessage in game.getChatMessages %}
    				<div>
    					<strong>
    						{% if chatMessage.getParticipant().fortniteGame is not null %}
    							{{ chatMessage.getParticipant().fortniteGame.pseudo }}
    						{% else %}
    							{{ chatMessage.getParticipant().username }}
    						{% endif %}
    					</strong>:
    					<p>{{ chatMessage.getMessage() }}</p>
    					<small>{{ chatMessage.getTimestamp().format('d/m/Y H:i') }}</small>
    				</div>
    			{% endfor %}
    		</div>
    
    		<form id="chat-form" action="{{ path('game_chat', { 'id': game.id }) }}" method="post">
    			<textarea name="message" rows="3"></textarea>
    			<button type="submit">Envoyer</button>
    		</form>
    	{% endif %}
    	{# FIN CHAT MESSAGE #}
    
    
    {# DEBUT Importation WEB SOCKET #}      
        <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
        {# FIN Importation WEB SOCKET #}      
        {# DEBUT Script pour le chat avec websocket #}      
        <script>
            const socket = io('http://localhost:3000');
    
            socket.on('connect', function () {
                console.log('Connected to Socket.io server');
            });
    
            socket.on('disconnect', function () {
                console.log('Disconnected from Socket.io server');
            });
    
            document.getElementById('chat-form').addEventListener('submit', function (e) {
                e.preventDefault();
    
                const message = this.message.value;
    
                fetch(this.action, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: new URLSearchParams(new FormData(this))
                })
                .then(response => {
                    if (!response.ok) {
                        throw new Error('Network response was not ok ' + response.statusText);
                    }
                    return response.text();
                })
                .then(data => {
                    console.log('Message sent:', data);
                })
                .catch(error => {
                    console.error('There has been a problem with your fetch operation:', error);
                });
    
                this.message.value = '';
            });
    
            socket.on('chat message', function (msg) {
                console.log('New message received:', msg);
                const chatMessages = document.getElementById('chat-messages');
                const messageDiv = document.createElement('div');
                messageDiv.innerHTML = `<strong>${msg.username}</strong>:<p>${msg.message}</p><small>${msg.timestamp}</small>`;
                chatMessages.appendChild(messageDiv);
            });
        </script>
        {# FIN Script pour le chat avec websocket #}  

    Mon contrôleur : 

    #[Route('/game/{id}/chat', name: 'game_chat', methods: ['POST'])]
        public function chat(Game $game, Request $request, EntityManagerInterface $em, HttpClientInterface $httpClient, LoggerInterface $logger, ValidatorInterface $validator): Response
        {
            $messageContent = $request->request->get('message');
    
            if (empty($messageContent)) {
                $logger->error('Message content is empty.');
                return new Response('Message content is required.', Response::HTTP_BAD_REQUEST);
            }
    
            $user = $this->getUser();
            if (!$user) {
                $logger->error('User is not authenticated.');
                return new Response('User is not authenticated.', Response::HTTP_UNAUTHORIZED);
            }
    
            if (!$user instanceof User) {
                $logger->error('User object is not an instance of App\Entity\User. Class: ' . get_class($user));
                return new Response('User object is not an instance of App\Entity\User.', Response::HTTP_INTERNAL_SERVER_ERROR);
            }
    
            $logger->info('User class: ' . get_class($user));
            $logger->info('Username: ' . $user->getUsername());
    
            if (!$game->getParticipants()->contains($user) && $game->getCreator() !== $user) {
                $logger->error('User is not a participant of the game.');
                return new Response('You are not authorized to participate in this chat.', Response::HTTP_FORBIDDEN);
            }
    
            $fortnitePseudo = $user->getFortniteGame() ? $user->getFortniteGame()->getPseudo() : $user->getUsername();
    
            $chatMessage = new Chat();
            $chatMessage->setParticipant($user);
            $chatMessage->setMessage($messageContent);
            $chatMessage->setGame($game);
            $chatMessage->setTimestamp(new \DateTime());
    
            // Validation
            $errors = $validator->validate($chatMessage);
            if (count($errors) > 0) {
                $errorsString = (string) $errors;
                $logger->error('Validation errors: ' . $errorsString);
                return new Response($errorsString, Response::HTTP_BAD_REQUEST);
            }
    
            $em->persist($chatMessage);
            $em->flush();
    
            try {
                $response = $httpClient->request('POST', 'http://localhost:3000', [
                    'json' => [
                        'username' => $fortnitePseudo,  
                        'message' => $messageContent,
                        'timestamp' => $chatMessage->getTimestamp()->format('d/m/Y H:i'),
                    ],
                ]);
    
                if ($response->getStatusCode() !== 200) {
                    $logger->error('Failed to send message to Socket.io server: ' . $response->getContent(false));
                    throw new \Exception('Failed to send message to Socket.io server');
                }
            } catch (\Exception $e) {
                $logger->error('Error sending message to Socket.io server: ' . $e->getMessage());
                return new Response('Error sending message to Socket.io server: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
            }
    
            return new Response(null, Response::HTTP_NO_CONTENT);
        }
    • Partager sur Facebook
    • Partager sur Twitter
      4 janvier 2025 à 10:02:39

      Hello, je vais regarder en détail ton problème, mais je me demande s'il n'aurait pas été mieux de passer par du SSE avec Mercure par exemple.

      PS: https://www.youtube.com/watch?v=pV4jXfDECjU

      Et aussi https://www.youtube.com/watch?v=Q4LRN2wXuIc 

      -
      Edité par quenti77 4 janvier 2025 à 17:33:05

      • Partager sur Facebook
      • Partager sur Twitter
        5 janvier 2025 à 13:36:13

        Bonjour , merci je vais regarder !
        • Partager sur Facebook
        • Partager sur Twitter

        Chat en temps réels avec websocket + symfony 7

        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
        • Editeur
        • Markdown