1. 程式人生 > >使用最新Netty實現一個簡單的聊天程式

使用最新Netty實現一個簡單的聊天程式

1、概述

    Netty是由JBOSS提供的一個java開源框架。Netty提供非同步的、事件驅動的網路應用程式框架和工具,用以快速開發高效能、高可靠性的網路伺服器和客戶端程式。

    目前最新的是版本是4.1.4(5.x版本官網已經棄用,不推薦使用)。

    Netty4.x同時也是原生支援Android的,所以後面的程式,放到android上也是可以正常執行的(親測)。

    專案如果使用Maven開發,直接新增以下依賴即可:

  1. <dependency>
  2. <groupId>io.netty</groupId>
  3. <artifactId>
    netty-all</artifactId>
  4. <version>4.1.4.Final</version>
  5. </dependency>

2、實現服務端

    首先是訊息處理類ServerHandler

  1. package com.anxpp.im.server.handler;
  2. import io.netty.channel.ChannelHandler;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.channel.ChannelInboundHandlerAdapter
    ;
  5. import java.io.IOException;
  6. import com.anxpp.im.common.IMMessage;
  7. import com.anxpp.im.common.OnlineUser;
  8. import com.anxpp.im.server.config.BaseConfig;
  9. @ChannelHandler.Sharable
  10. publicclassServerHandlerextendsChannelInboundHandlerAdapterimplementsBaseConfig{
  11. privateChannelHandlerContext ctx;
  12. @Override
  13. public
    void handlerAdded(ChannelHandlerContext ctx)throwsException{
  14. System.err.println("服務端Handler建立...");
  15. super.handlerAdded(ctx);
  16. }
  17. @Override
  18. publicvoid channelInactive(ChannelHandlerContext ctx)throwsException{
  19. System.err.println("channelInactive");
  20. super.channelInactive(ctx);
  21. }
  22. /**
  23. * tcp鏈路建立成功後呼叫
  24. */
  25. @Override
  26. publicvoid channelActive(ChannelHandlerContext ctx)throwsException{
  27. this.ctx = ctx;
  28. System.err.println("有客戶端連線:"+ctx.channel().remoteAddress().toString());
  29. }
  30. /**
  31. * 傳送訊息
  32. */
  33. publicboolean sendMsg(IMMessage msg)throwsIOException{
  34. System.err.println("伺服器推送訊息:"+msg);
  35. ctx.writeAndFlush(msg);
  36. return msg.getMsg().equals("q")?false:true;
  37. }
  38. @Override
  39. publicvoid channelRead(ChannelHandlerContext ctx,Object msg)throwsIOException{
  40. System.err.println("伺服器接收到訊息:"+msg);
  41. IMMessage message =(IMMessage)msg;
  42. if(OnlineUser.get(message.getReceiveId())==null){
  43. OnlineUser.put(message.getUid(), ctx);
  44. }
  45. ChannelHandlerContext c =OnlineUser.get(message.getReceiveId());
  46. if(c==null){
  47. message.setMsg("對方不線上!");
  48. OnlineUser.get(message.getUid()).writeAndFlush(message);
  49. }
  50. else
  51. c.writeAndFlush(message);
  52. }
  53. /**
  54. * 異常處理
  55. */
  56. @Override
  57. publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
  58. System.err.println("與客戶端斷開連線:"+cause.getMessage());
  59. cause.printStackTrace();
  60. ctx.close();
  61. }
  62. }

    接收客戶端連線,如果客戶端沒有連線過,就儲存ChannelHandlerContext到Map中用於傳送訊息。然後判斷訊息接收方是否線上,若線上就直接傳送訊息,沒有線上就返回“對方不線上”。當然,可以將訊息傳送者和訊息接收者ID設定為相同就能僅開一個客戶端自己跟自己聊天(類似Echo Server)。

    伺服器程式Server

  1. package com.anxpp.im.server;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
  11. import io.netty.handler.codec.LengthFieldPrepender;
  12. import java.io.IOException;
  13. import java.util.Scanner;
  14. import com.anxpp.im.common.IMMessage;
  15. import com.anxpp.im.common.MsgPackDecode;
  16. import com.anxpp.im.common.MsgPackEncode;
  17. import com.anxpp.im.server.config.BaseConfig;
  18. import com.anxpp.im.server.handler.ServerHandler;
  19. publicclassServerimplementsRunnable,BaseConfig{
  20. ServerHandler serverHandler =newServerHandler();
  21. publicstaticvoid main(String[] args)throwsIOException{
  22. newServer().start();
  23. }
  24. publicvoid start()throwsIOException{
  25. newThread(this).start();
  26. runServerCMD();
  27. }
  28. /**啟動服務端控制檯
  29. * @throws IOException */
  30. privatevoid runServerCMD()throwsIOException{
  31. //服務端主動推送訊息
  32. int toID =1;
  33. IMMessage message =newIMMessage(
  34. APP_IM,
  35. CLIENT_VERSION,
  36. SERVER_ID,
  37. TYPE_MSG_TEXT,
  38. toID,
  39. MSG_EMPTY);
  40. @SuppressWarnings("resource")
  41. Scanner scanner =newScanner(System.in);
  42. do{
  43. message.setMsg(scanner.nextLine());
  44. }
  45. while(serverHandler.sendMsg(message));
  46. }
  47. publicvoid run(){
  48. EventLoopGroup bossGroup =newNioEventLoopGroup();
  49. EventLoopGroup workerGroup =newNioEventLoopGroup();
  50. try{
  51. ServerBootstrap b =newServerBootstrap();
  52. b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024)
  53. // .childOption(ChannelOption.SO_KEEPALIVE, true)
  54. .childHandler(