# Socket.io Example

Real-time communication

## Code Example

```javascript
// Socket.io Real-time Communication Example
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
    methods: ['GET', 'POST']
  }
});

app.use(cors());
app.use(express.json());

// Store connected users
const connectedUsers = new Map();
const rooms = new Map();

// Socket.io connection handling
io.on('connection', (socket) => {
  console.log(`User connected: ${socket.id}`);

  // User joins with authentication
  socket.on('join', async (userData) => {
    try {
      // Validate user data
      const { userId, username, roomId } = userData;
      
      if (!userId || !username) {
        socket.emit('error', { message: 'Invalid user data' });
        return;
      }

      // Store user information
      connectedUsers.set(socket.id, {
        userId,
        username,
        roomId: roomId || 'general',
        socketId: socket.id,
        joinedAt: new Date()
      });

      // Join room
      socket.join(roomId || 'general');
      
      // Notify room about new user
      socket.to(roomId || 'general').emit('user_joined', {
        userId,
        username,
        timestamp: new Date()
      });

      // Send current room users to the new user
      const roomUsers = Array.from(connectedUsers.values())
        .filter(user => user.roomId === (roomId || 'general'))
        .map(user => ({
          userId: user.userId,
          username: user.username
        }));

      socket.emit('room_users', roomUsers);

      console.log(`User ${username} joined room ${roomId || 'general'}`);
    } catch (error) {
      socket.emit('error', { message: 'Failed to join room' });
    }
  });

  // Handle chat messages
  socket.on('send_message', (messageData) => {
    const user = connectedUsers.get(socket.id);
    
    if (!user) {
      socket.emit('error', { message: 'User not authenticated' });
      return;
    }

    const message = {
      id: Date.now() + Math.random(),
      userId: user.userId,
      username: user.username,
      content: messageData.content,
      timestamp: new Date(),
      roomId: user.roomId,
      type: messageData.type || 'text'
    };

    // Broadcast message to room
    io.to(user.roomId).emit('new_message', message);
    
    // Store message in room history
    if (!rooms.has(user.roomId)) {
      rooms.set(user.roomId, []);
    }
    
    const roomMessages = rooms.get(user.roomId);
    roomMessages.push(message);
    
    // Keep only last 100 messages per room
    if (roomMessages.length > 100) {
      roomMessages.shift();
    }
  });

  // Handle typing indicators
  socket.on('typing_start', () => {
    const user = connectedUsers.get(socket.id);
    if (user) {
      socket.to(user.roomId).emit('user_typing', {
        userId: user.userId,
        username: user.username
      });
    }
  });

  socket.on('typing_stop', () => {
    const user = connectedUsers.get(socket.id);
    if (user) {
      socket.to(user.roomId).emit('user_stopped_typing', {
        userId: user.userId,
        username: user.username
      });
    }
  });

  // Handle room switching
  socket.on('switch_room', (newRoomId) => {
    const user = connectedUsers.get(socket.id);
    
    if (user) {
      // Leave current room
      socket.leave(user.roomId);
      socket.to(user.roomId).emit('user_left', {
        userId: user.userId,
        username: user.username,
        timestamp: new Date()
      });

      // Join new room
      user.roomId = newRoomId;
      socket.join(newRoomId);
      
      // Notify new room
      socket.to(newRoomId).emit('user_joined', {
        userId: user.userId,
        username: user.username,
        timestamp: new Date()
      });

      // Send room history
      const roomHistory = rooms.get(newRoomId) || [];
      socket.emit('room_history', roomHistory);

      // Send new room users
      const roomUsers = Array.from(connectedUsers.values())
        .filter(u => u.roomId === newRoomId)
        .map(u => ({
          userId: u.userId,
          username: u.username
        }));

      socket.emit('room_users', roomUsers);
    }
  });

  // Handle disconnection
  socket.on('disconnect', () => {
    const user = connectedUsers.get(socket.id);
    
    if (user) {
      // Notify room about user leaving
      socket.to(user.roomId).emit('user_left', {
        userId: user.userId,
        username: user.username,
        timestamp: new Date()
      });

      // Remove user from connected users
      connectedUsers.delete(socket.id);
      
      console.log(`User ${user.username} disconnected`);
    }
  });

  // Handle errors
  socket.on('error', (error) => {
    console.error(`Socket error: ${error}`);
    socket.emit('error', { message: 'An error occurred' });
  });
});

// REST API endpoints
app.get('/api/rooms/:roomId/messages', (req, res) => {
  const { roomId } = req.params;
  const messages = rooms.get(roomId) || [];
  res.json(messages);
});

app.get('/api/rooms/:roomId/users', (req, res) => {
  const { roomId } = req.params;
  const roomUsers = Array.from(connectedUsers.values())
    .filter(user => user.roomId === roomId)
    .map(user => ({
      userId: user.userId,
      username: user.username,
      joinedAt: user.joinedAt
    }));
  
  res.json(roomUsers);
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
```

## Files

- server.js
- client.js
- package.json
- public/index.html

## Usage

```bash
# Install dependencies
npm install

# Run the example
npm start
```
