Netty入門與實戰——Netty實現客戶端登陸

Ps:此係列文章來源於Netty 入門與實戰:仿寫微信 IM 即時通訊系統,知識付費的時代,有能力請支持正版,一頓飯錢而已~

*登陸流程

客戶端連接上服務端之後

  1. 客戶端會構建一個登錄請求對象,然後通過編碼把請求對象編碼爲 ByteBuf,寫到服務端。
  2. 服務端接受到 ByteBuf 之後,首先通過解碼把 ByteBuf 解碼爲登錄請求響應,然後進行校驗。
  3. 服務端校驗通過之後,構造一個登錄響應對象,依然經過編碼,然後再寫回到客戶端。
  4. 客戶端接收到服務端的之後,解碼 ByteBuf,拿到登錄響應響應,判斷是否登陸成功

*邏輯處理器

客戶端啓動的時候,給客戶端配置的邏輯處理器叫做 ClientHandler。這樣,在客戶端側,Netty 中 IO 事件相關的回調就能夠回調到我們的 ClientHandler

bootstrap.handler(new ChannelInitializer<SocketChannel>() {
    @Override
    public void initChannel(SocketChannel ch) {
        ch.pipeline().addLast(new ClientHandler());
    }
});

同理,給服務端引導類 ServerBootstrap 也配置一個邏輯處理器 ServerHandler。在服務端側,Netty 中 IO 事件相關的回調就能夠回調到我們的 ServerHandler


serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
    protected void initChannel(NioSocketChannel ch) {
        ch.pipeline().addLast(new ServerHandler());
    }
}

*客戶端發送登錄請求

客戶端處理登錄請求

ClientHandler.java

public void channelActive(ChannelHandlerContext ctx) {
    System.out.println(new Date() + ": 客戶端開始登錄");

    // 創建登錄對象
    LoginRequestPacket loginRequestPacket = new LoginRequestPacket();
    loginRequestPacket.setUserId(UUID.randomUUID().toString());
    loginRequestPacket.setUsername("flash");
    loginRequestPacket.setPassword("pwd");

    // 編碼
    ByteBuf buffer = PacketCodeC.INSTANCE.encode(ctx.alloc(), loginRequestPacket);

    // 寫數據
    ctx.channel().writeAndFlush(buffer);
}

在編碼的環節,我們把 PacketCodeC 變成單例模式,然後把 ByteBuf 分配器抽取出一個參數,這裏第一個實參 ctx.alloc() 獲取的就是與當前連接相關的 ByteBuf 分配器,建議這樣來使用。

寫數據的時候,我們通過 ctx.channel() 獲取到當前連接(Netty 對連接的抽象爲 Channel,後面小節會分析),然後調用 writeAndFlush() 就能把二進制數據寫到服務端。

服務端處理登錄請求

ServerHandler.java

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf requestByteBuf = (ByteBuf) msg;

    // 解碼
    Packet packet = PacketCodeC.INSTANCE.decode(requestByteBuf);

    // 判斷是否是登錄請求數據包
    if (packet instanceof LoginRequestPacket) {
        LoginRequestPacket loginRequestPacket = (LoginRequestPacket) packet;

        // 登錄校驗
        if (valid(loginRequestPacket)) {
            // 校驗成功
        } else {
            // 校驗失敗
        }
    }
}

private boolean valid(LoginRequestPacket loginRequestPacket) {
    return true;
}

拿到 ByteBuf 之後,首先要做的事情就是解碼,解碼出 java 數據包對象,然後判斷如果是登錄請求數據包 LoginRequestPacket,就進行登錄邏輯的處理,這裏,我們假設所有的登錄都是成功的,valid() 方法返回 true。

*服務端發送登錄響應

 服務端處理登錄響應

ServerHandler.java

LoginResponsePacket loginResponsePacket = new LoginResponsePacket();
loginResponsePacket.setVersion(packet.getVersion());
if (valid(loginRequestPacket)) {
    loginResponsePacket.setSuccess(true);
} else {
    loginResponsePacket.setReason("賬號密碼校驗失敗");
    loginResponsePacket.setSuccess(false);
}
// 編碼
ByteBuf responseByteBuf = PacketCodeC.INSTANCE.encode(ctx.alloc(), loginResponsePacket);
ctx.channel().writeAndFlush(responseByteBuf);

這段邏輯仍然是在服務端邏輯處理器 ServerHandlerchannelRead() 方法裏,我們構造一個登錄響應包 LoginResponsePacket,然後在校驗成功和失敗的時候分別設置標誌位,接下來,調用編碼器把 Java 對象編碼成 ByteBuf,調用 writeAndFlush() 寫到客戶端。

客戶端處理登錄響應

ClientHandler.java

客戶端接收服務端數據的處理邏輯也是在 ClientHandlerchannelRead() 方法

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf byteBuf = (ByteBuf) msg;

    Packet packet = PacketCodeC.INSTANCE.decode(byteBuf);

    if (packet instanceof LoginResponsePacket) {
        LoginResponsePacket loginResponsePacket = (LoginResponsePacket) packet;

        if (loginResponsePacket.isSuccess()) {
            System.out.println(new Date() + ": 客戶端登錄成功");
        } else {
            System.out.println(new Date() + ": 客戶端登錄失敗,原因:" + loginResponsePacket.getReason());
        }
    }
}

 客戶端拿到數據之後,調用 PacketCodeC 進行解碼操作,如果類型是登錄響應數據包,我們這裏邏輯比較簡單,在控制檯打印出一條消息。

*登陸請求與響應類

@Data
public class LoginRequestPacket extends Packet {
    private String userId;

    private String username;

    private String password;

    @Override
    public Byte getCommand() {
        return LOGIN_REQUEST;
    }
}

@Data
public class LoginResponsePacket extends Packet {
    private boolean success;

    private String reason;


    @Override
    public Byte getCommand() {
        return LOGIN_RESPONSE;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章