首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

手機網遊服務端請求處理:併發架構與協議設計實戰

     編輯:Java 相關知識

手機網遊服務端需支撐大量客戶端併發連接,本文以《瘋狂的炸彈》實戰項目為例,剖析請求處理線程模型、協議設計、用戶匹配邏輯與同步控制。

架構總覽


客戶端 ←HTTP/TCP→ 服務端
                    ├── Acceptor線程 (ServerSocket.accept)
                    ├── 處理線程池 (每連接一線程/線程池)
                    ├── 邏輯引擎 (遊戲狀態機)
                    └── 數據存儲 (內存/Redis/MySQL)

請求處理流程

1. 連接建立與線程分配


public class GameServer {
    private final ServerSocket serverSocket;
    private final ExecutorService threadPool = Executors.newCachedThreadPool();
    
    public void start() {
        while (running) {
            Socket client = serverSocket.accept();
            threadPool.submit(new ClientHandler(client));
        }
    }
}

class ClientHandler implements Runnable {
    private final Socket socket;
    
    public void run() {
        try (InputStream is = socket.getInputStream();
             OutputStream os = socket.getOutputStream()) {
            
            // 讀取請求
            byte[] request = readRequest(is);
            if (request.length == 0) return;
            
            // 邏輯處理
            byte[] response = LogicEngine.process(request, this);
            
            // 發送響應
            if (response != null) {
                sendResponse(os, response);
            }
        } catch (IOException e) {
            log.error("連接異常", e);
        } finally {
            closeConnection();
        }
    }
}

2. 協議設計:指令碼+參數


// 客戶端→服務端指令
interface RequestCmd {
    byte READY     = 0x00;  // 就緒/註冊
    byte START     = 0x01;  // 開始匹配
    byte MOVE      = 0x02;  // 移動
    byte LAY_BOMB  = 0x03;  // 放炸彈
    byte GET_DATA  = 0x04;  // 請求遊戲數據
    byte QUIT      = 0x05;  // 退出
}

// 服務端→客戶端響應
interface ResponseCmd {
    byte READY_OK       = 0x00;  // 就緒成功,返回userId
    byte CONTINUE_WAIT  = 0x01;  // 繼續等待匹配
    byte INIT_DATA      = 0x02;  // 初始化數據(房間/地圖/角色)
    byte GAME_DATA      = 0x03;  // 幀同步數據
}

3. 數據包編解碼


// 請求編碼:[指令1字節][參數長度2字節][參數體]
public byte[] encodeMove(short x, short y, byte direction) {
    ByteBuffer buf = ByteBuffer.allocate(1 + 2 + 5);
    buf.put(RequestCmd.MOVE);
    buf.putShort(5); // 參數長度
    buf.putShort(x);
    buf.putShort(y);
    buf.put(direction);
    return buf.array();
}

// 響應解碼
public GameData decodeGameData(byte[] data) {
    ByteBuffer buf = ByteBuffer.wrap(data);
    byte cmd = buf.get();
    if (cmd != ResponseCmd.GAME_DATA) throw new ProtocolException();
    
    GameData gd = new GameData();
    gd.roomId = buf.getInt();
    gd.players = decodePlayers(buf);
    gd.map = decodeMap(buf);
    gd.bombs = decodeBombs(buf);
    return gd;
}

核心邏輯:用戶匹配與同步控制

用戶ID分配(線程安全)


public class UserManager {
    private final AtomicLong idGenerator = new AtomicLong(10000);
    private final ConcurrentMap sessions = new ConcurrentHashMap<>();
    
    public long allocateUserId() {
        return idGenerator.getAndIncrement(); // 原子操作,無鎖
    }
    
    public void registerSession(long userId, UserSession session) {
        sessions.put(userId, session);
    }
}

房間匹配邏輯


public class MatchMaker {
    private final Queue waitingQueue = new ConcurrentLinkedQueue<>();
    private final Map rooms = new ConcurrentHashMap<>();
    
    public synchronized Room tryMatch(long userId) {
        waitingQueue.offer(userId);
        if (waitingQueue.size() >= 2) {
            Long u1 = waitingQueue.poll();
            Long u2 = waitingQueue.poll();
            if (u1 != null && u2 != null) {
                Room room = new Room(u1, u2);
                rooms.put(room.id, room);
                return room;
            }
        }
        return null; // 需繼續等待
    }
}

遊戲幀同步


// 客戶端每秒請求一次
public byte[] handleGetData(Request req, ClientHandler handler) {
    long userId = req.getUserId();
    Room room = roomManager.getRoomByUser(userId);
    if (room == null) return encodeError("不在房間中");
    
    // 返回當前幀數據
    return room.getCurrentFrame().encode();
}

// 服務端定時推送(可選)
@Scheduled(fixedRate = 100) // 10 FPS
public void broadcastFrames() {
    for (Room room : rooms.values()) {
        Frame frame = room.calculateNextFrame();
        room.broadcast(frame.encode());
    }
}

性能優化關鍵點

優化點方案效果
線程模型NIO選擇器+線程池/Netty事件循環支撐萬級併發,降低線程開銷
協議壓縮Protobuf/MessagePack替代自定義二進制減少50%+頻寬,解碼加速
狀態存儲熱數據內存+異步落地Redis毫秒級讀寫,支持集群
幀同步增量更新/死重播/客戶端預測弱網下流暢體驗
防作弊服務端權威校驗/重放檢測保證公平性

避坑指南

  • TCP粘包/拆包:必須實現完整幀解碼器(長度前綴/分隔符)
  • 阻塞操作:DB查詢/HTTP調用必須異步化,不可阻塞遊戲線程
  • 內存洩漏:斷線未清理Session/Room,定期掃描清理
  • 單點故障:無狀態服務層+分佈式Session/房間管理

從「每連接一線程」起步,逐步演進至NIO/Netty架構,是手遊服務端成長的必經之路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved