长轮询之websocket

官方文档

背景

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它是为了解决 HTTP 协议存在的一些问题而产生的。WebSocket 的产生背景主要包括以下几点:

HTTP 协议的局限性

HTTP 协议是一种请求-响应模型,客户端发起请求,服务端返回响应。这种模型存在一些问题,如无法主动推送数据给客户端,只能通过客户端轮询的方式获取数据。
HTTP 协议是无状态的,每次请求都需要重新建立连接,给服务器和客户端带来了不必要的开销。

HTTP 协议的局限性

HTTP 协议是一种请求-响应模型,客户端发起请求,服务端返回响应。这种模型存在一些问题:

1、无法主动推送数据给客户端,只能通过客户端轮询的方式获取数据,这会造成网络流量的浪费。
2、HTTP 协议是无状态的,每次请求都需要重新建立连接,给服务器和客户端带来了不必要的开销。
3、HTTP 协议头部信息较大,在移动网络环境下会造成较大的流量开销。
这些局限性使得 HTTP 协议难以满足实时交互、即时通信等应用的需求。

实时通信需求的增加

随着 Web 应用程序的复杂度提高,对实时交互、即时通讯等需求越来越强烈,例如:

聊天应用

需要即时的消息推送和响应。

多人游戏

需要客户端和服务端之间的实时数据交互。

股票行情

需要服务端能够实时推送最新的行情数据。
这些应用场景都需要一种更加合适的通信协议来满足实时性和双向通信的需求。

技术演进的驱动

HTML5 的发展为 WebSocket 的应用奠定了基础

HTML5 标准中正式定义了 WebSocket API,使浏览器能够原生支持 WebSocket 协议。
这为 WebSocket 在 Web 应用中的广泛应用奠定了技术基础。

服务端也开始支持 WebSocket 协议

如 Node.js 的 Socket.IO、Java 的 Spring WebSocket 等,使得 WebSocket 的应用变得更加广泛和成熟。
这些技术的进步为 WebSocket 协议的应用提供了必要的支持。

移动互联网时代的需求

移动互联网时代,用户对即时性和响应速度的要求越来越高,HTTP 协议的局限性越来越明显:

移动网络环境下,HTTP 协议的大量头部信息会造成较大的流量开销。
移动应用对实时交互的需求更加迫切,HTTP 协议难以满足。

WebSocket 协议可以提供更好的实时交互体验,因此在移动应用中得到了广泛应用。

websocket的特点

基于tcp实现的的长连接

理论上可以永久地维持连接,支持比传统轮询更长的轮询间隔,30、60秒甚至更长。websocket在没有消息的时候,会通过挂起请求的方式保持连接,到达时限的时候象征性地回个消息,以保持连接。

全双工

实现了服务端与客户端的互相通信。也因此,一旦有了新消息,服务端可以主动发起消息通知,解决了传统轮询只能服务端被动询问是否有新消息的难题。

单次握手

你可以理解为门票。持有门票的client才能与服务端通信。而单次握手的过程,便是服务端给客户端发门票的过程。通信的过程中,采取认票不认人的逻辑。

实时性

WebSocket 协议能够提供近乎实时的数据传输,延迟低于 100ms。
这使得 WebSocket 非常适合实时应用,如聊天应用、多人游戏等。

轻量化

WebSocket 协议的数据帧相比 HTTP 的请求/响应头部要小得多。
这在移动网络环境下可以大大减少流量开销,提高传输效率。

跨域支持

WebSocket 协议天生支持跨域通信,不受同源策略的限制。
这为构建分布式的实时应用提供了便利。

协议兼容

WebSocket 协议能够与 HTTP 协议很好地兼容,可以复用现有的 HTTP 基础设施。
这使得 WebSocket 的部署和集成更加简单。

常见应用场景

实时聊天应用

WebSocket 协议非常适合实时聊天应用,如即时通讯软件、在线客服系统等。
它可以提供低延迟的双向消息传递,支持即时的消息推送和接收。

多人游戏

WebSocket 协议能够支持多人游戏中的实时交互和状态同步。
如在线多人游戏、实时对战游戏等都可以很好地利用 WebSocket 技术。

实时数据推送

WebSocket 可以用于各种需要实时数据推送的应用场景,如股票行情、体育赛事直播等。
相比轮询 HTTP 请求,WebSocket 可以更高效地推送实时数据更新。

物联网(IoT)

WebSocket 协议适用于物联网设备之间的实时通信和控制。
通过 WebSocket 连接,物联网设备可以即时上报数据、接收命令和控制信号。

协同编辑

WebSocket 可以支持多人实时协作编辑文档、代码等内容的应用场景。
编辑者之间可以实时看到彼此的操作,协同编辑体验更好。

实时监控和报警

WebSocket 协议适用于需要实时监控和快速报警的场景,如工业设备监控、网站性能监控等。
监控数据可以通过 WebSocket 实时推送到客户端,快速响应异常情况。

简单示例

Node.js 和 Socket.IO实现的多人在线棋牌游戏

首先,我们需要一个服务端代码:

// server.js
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIO(server);

// 游戏房间管理
const rooms = {};

// 客户端连接事件处理
io.on('connection', (socket) => {
  console.log('用户连接:', socket.id);

  // 创建房间
  socket.on('create_room', (roomName) => {
    rooms[roomName] = { players: [], state: 'waiting' };
    socket.join(roomName);
    socket.emit('room_created', roomName);
  });

  // 加入房间
  socket.on('join_room', (roomName) => {
    if (rooms[roomName]) {
      rooms[roomName].players.push(socket.id);
      socket.join(roomName);
      io.to(roomName).emit('player_joined', rooms[roomName].players);

      if (rooms[roomName].players.length === 2) {
        rooms[roomName].state = 'playing';
        io.to(roomName).emit('game_started');
      }
    } else {
      socket.emit('room_not_found');
    }
  });

  // 下棋
  socket.on('make_move', (roomName, position) => {
    if (rooms[roomName]) {
      io.to(roomName).emit('move_made', socket.id, position);
    }
  });

  // 断开连接
  socket.on('disconnect', () => {
    console.log('用户断开:', socket.id);
    // 处理用户断开连接的逻辑
  });
});

server.listen(3000, () => {
  console.log('Server started on port 3000');
});

这个服务端代码实现了基本的房间创建、玩家加入、游戏开始和下棋等逻辑。

接下来,我们需要一个客户端代码:

<!-- client.html -->
<!DOCTYPE html>
<html>
<head>
  <title>在线多人游戏</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.1/socket.io.js"></script>
</head>
<body>
  <h1>在线多人游戏</h1>
  <button id="createRoom">创建房间</button>
  <input type="text" id="roomName" placeholder="输入房间名" />
  <button id="joinRoom">加入房间</button>
  <div id="gameBoard"></div>

  <script>
    const socket = io('http://localhost:3000');

    // 创建房间
    document.getElementById('createRoom').addEventListener('click', () => {
      const roomName = document.getElementById('roomName').value;
      socket.emit('create_room', roomName);
    });

    // 加入房间
    document.getElementById('joinRoom').addEventListener('click', () => {
      const roomName = document.getElementById('roomName').value;
      socket.emit('join_room', roomName);
    });

    // 处理游戏事件
    socket.on('room_created', (roomName) => {
      console.log('房间创建成功:', roomName);
    });

    socket.on('room_not_found', () => {
      console.log('房间不存在');
    });

    socket.on('player_joined', (players) => {
      console.log('有玩家加入:', players);
    });

    socket.on('game_started', () => {
      console.log('游戏开始');
    });

    socket.on('move_made', (playerId, position) => {
      console.log('玩家', playerId, '下了一步', position);
    });
  </script>
</body>
</html>

这个客户端代码实现了基本的房间创建、加入房间和接收游戏事件的逻辑。

通过这个示例,你可以看到 WebSocket 协议在实现在线多人游戏中的应用。服务端负责管理房间和游戏逻辑,客户端负责与服务端交互和显示游戏状态。这种基于 WebSocket 的实时双向通信方式非常适合实现这类游戏应用。

基于 Python 和 React 实现的 WebSocket 多人实时对战

服务端 (Python + WebSocket)

我们将使用 Python 的 websocket-server 库来实现服务端的 WebSocket 处理逻辑。

# server.py
import json
from websocket_server import WebsocketServer

# 游戏房间管理
rooms = {}

# 新客户端连接
def new_client(client, server):
    print(f"新客户端连接: {client['address']}")

# 客户端断开连接
def client_left(client, server):
    print(f"客户端断开: {client['address']}")
    # 处理用户断开连接的逻辑

# 接收客户端消息
def message_received(client, server, message):
    data = json.loads(message)
    action = data['action']

    if action == 'create_room':
        room_name = data['room_name']
        rooms[room_name] = {'players': [client['id']], 'state': 'waiting'}
        server.send_message(client, json.dumps({'action': 'room_created', 'room_name': room_name}))
    elif action == 'join_room':
        room_name = data['room_name']
        if room_name in rooms:
            rooms[room_name]['players'].append(client['id'])
            server.send_message_to_all(json.dumps({'action': 'player_joined', 'room_name': room_name, 'players': rooms[room_name]['players']}))

            if len(rooms[room_name]['players']) == 2:
                rooms[room_name]['state'] = 'playing'
                server.send_message_to_all(json.dumps({'action': 'game_started', 'room_name': room_name}))
        else:
            server.send_message(client, json.dumps({'action': 'room_not_found'}))
    elif action == 'make_move':
        room_name = data['room_name']
        position = data['position']
        server.send_message_to_all(json.dumps({'action': 'move_made', 'room_name': room_name, 'player_id': client['id'], 'position': position}))

server = WebsocketServer(host='localhost', port=8000, on_new_client=new_client, on_client_left=client_left, on_message=message_received)
server.run_forever()

客户端 (React + WebSocket)

我们将使用 React 和 socket.io-client 库来实现客户端的 WebSocket 交互。

// App.js
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';

function App() {
  const [socket, setSocket] = useState(null);
  const [roomName, setRoomName] = useState('');
  const [players, setPlayers] = useState([]);
  const [gameStarted, setGameStarted] = useState(false);

  useEffect(() => {
    const newSocket = io('ws://localhost:8000');
    setSocket(newSocket);

    return () => newSocket.disconnect();
  }, []);

  const createRoom = () => {
    socket.emit('create_room', roomName);
  };

  const joinRoom = () => {
    socket.emit('join_room', roomName);
  };

  const makeMove = (position) => {
    socket.emit('make_move', { room_name: roomName, position });
  };

  useEffect(() => {
    if (socket) {
      socket.on('room_created', (data) => {
        console.log('Room created:', data.room_name);
      });

      socket.on('room_not_found', () => {
        console.log('Room not found');
      });

      socket.on('player_joined', (data) => {
        setPlayers(data.players);
      });

      socket.on('game_started', () => {
        setGameStarted(true);
      });

      socket.on('move_made', (data) => {
        console.log(`Player ${data.player_id} made a move at ${data.position}`);
      });
    }
  }, [socket]);

  return (
    <div>
      <h1>WebSocket 多人实时对战</h1>
      <input
        type="text"
        placeholder="Room Name"
        value={roomName}
        onChange={(e) => setRoomName(e.target.value)}
      />
      <button onClick={createRoom}>Create Room</button>
      <button onClick={joinRoom}>Join Room</button>
      {gameStarted && (
        <div>
          <h2>Game Started!</h2>
          <p>Players: {players.join(', ')}</p>
          <button onClick={() => makeMove({ x: 0, y: 0 })}>Move</button>
        </div>
      )}
    </div>
  );
}

export default App;

在这个示例中,服务端使用 Python 的 websocket-server 库来管理 WebSocket 连接和游戏逻辑。客户端则使用 React 和 socket.io-client 库来与服务端进行 WebSocket 通信,实现房间创建、加入房间和下棋等功能。

值得注意的是,这只是一个基本的示例,在实际开发中,你需要考虑更多的功能和优化,如游戏规则实现、错误处理、性能优化等。同时,你还需要处理客户端断开连接的情况,以及如何在客户端渲染游戏画面等。

相关推荐

  1. websocket

    2024-06-08 10:18:02       23 阅读
  2. --一起学习吧架构

    2024-06-08 10:18:02       39 阅读
  3. STM32模式串口收发不定字符串

    2024-06-08 10:18:02       38 阅读
  4. 浅谈架构方法时间片

    2024-06-08 10:18:02       32 阅读
  5. Promise和事件

    2024-06-08 10:18:02       39 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-08 10:18:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 10:18:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 10:18:02       87 阅读
  4. Python语言-面向对象

    2024-06-08 10:18:02       96 阅读

热门阅读

  1. langchian_aws模块学习

    2024-06-08 10:18:02       32 阅读
  2. npm发布自己的插件包

    2024-06-08 10:18:02       36 阅读
  3. npm发布自己的插件包

    2024-06-08 10:18:02       28 阅读
  4. ubantu安装第三库到指定目录

    2024-06-08 10:18:02       31 阅读
  5. C#面:AJAX的底层实现原理

    2024-06-08 10:18:02       31 阅读
  6. 类的定义和对象的引用

    2024-06-08 10:18:02       30 阅读
  7. c++ 左右值与引用折叠

    2024-06-08 10:18:02       29 阅读
  8. 【例0808】create daxis using face 使用面创建基准轴

    2024-06-08 10:18:02       31 阅读