Netty5入门学习笔记001

服务器端:

TimeServer 时间服务器 服务端接收客户端的连接请求和查询当前时间的指令,判断指令正确后响应返回当前服务器的校准时间。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package  c1;
 
import  io.netty.bootstrap.ServerBootstrap;
import  io.netty.channel.ChannelFuture;
import  io.netty.channel.ChannelInitializer;
import  io.netty.channel.ChannelOption;
import  io.netty.channel.EventLoopGroup;
import  io.netty.channel.nio.NioEventLoopGroup;
import  io.netty.channel.socket.SocketChannel;
import  io.netty.channel.socket.nio.NioServerSocketChannel;
 
/**
  * server 有粘包问题
  * @author xwalker
  */
public  class  TimeServer {
     public  void  bind( int  port)  throws  Exception {
         // 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接
         // 另一个线程组用于处理SocketChannel的网络读写
         EventLoopGroup bossGroup =  new  NioEventLoopGroup();
         EventLoopGroup workerGroup =  new  NioEventLoopGroup();
         try  {
             // NIO服务器端的辅助启动类 降低服务器开发难度
             ServerBootstrap serverBootstrap =  new  ServerBootstrap();
             serverBootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel. class ) // 类似NIO中serverSocketChannel
                     .option(ChannelOption.SO_BACKLOG,  1024 ) // 配置TCP参数
                     .childHandler( new  ChildChannelHandler()); // 最后绑定I/O事件的处理类
                                                                 // 处理网络IO事件
 
             // 服务器启动后 绑定监听端口 同步等待成功 主要用于异步操作的通知回调 回调处理用的ChildChannelHandler
             ChannelFuture f = serverBootstrap.bind(port).sync();
             System.out.println( "timeServer启动" );
             // 等待服务端监听端口关闭
             f.channel().closeFuture().sync();
 
         finally  {
             // 优雅退出 释放线程池资源
             bossGroup.shutdownGracefully();
             workerGroup.shutdownGracefully();
             System.out.println( "服务器优雅的释放了线程资源..." );
         }
 
     }
 
     /**
      * 网络事件处理器
      */
     private  class  ChildChannelHandler  extends  ChannelInitializer {
         @Override
         protected  void  initChannel(SocketChannel ch)  throws  Exception {
             ch.pipeline().addLast( new  TimeServerHandler());
         }
 
     }
 
     public  static  void  main(String[] args)  throws  Exception {
         int  port =  8000 ;
         new  TimeServer().bind(port);
     }
 
}

TimerServer接收到客户端的连接和读写请求后交给处理器handler进行事件的响应处理,服务器定义两组线程组,一组用来处理客户端连接,一组用来处理网络IO事件(SocketChannel)的响应,NioEventLoopGroup是Netty提供的NIO线程组,实际上就是Java NIO中的Reactor线程组。

ServerBootstrap是Netty提供的用于NIO服务端辅助启动类,降低了NIO服务端的开发复杂度。

ServerBootstrap需要绑定服务器网络IO事件的处理类ChildChannelHandler ,用于实际处理具体的IO事件,例如记录日志,对消息编解码等。

TimeServerHandler需要继承Netty提供的适配器ChannelhandlerAdapter重写channelRead等方法完成消息的读写。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package  c1;
 
import  io.netty.buffer.ByteBuf;
import  io.netty.buffer.Unpooled;
import  io.netty.channel.ChannelHandlerAdapter;
import  io.netty.channel.ChannelHandlerContext;
 
import  java.util.Date;
/**
  * server端网络IO事件处理
  * @author xwalker
  *
  */
public  class  TimeServerHandler  extends  ChannelHandlerAdapter {
     @Override
     public  void  channelRead(ChannelHandlerContext ctx, Object msg)
             throws  Exception {
         System.out.println( "服务器读取到客户端请求..." );
         ByteBuf buf=(ByteBuf) msg;
         byte [] req= new  byte [buf.readableBytes()];
         buf.readBytes(req);
         String body= new  String(req, "UTF-8" );
         System.out.println( "the time server receive order:" +body);
         String curentTime= "QUERY TIME ORDER" .equalsIgnoreCase(body)? new  Date(System.currentTimeMillis()).toString(): "BAD ORDER" ;
         ByteBuf resp=Unpooled.copiedBuffer(curentTime.getBytes());
         ctx.write(resp);
         System.out.println( "服务器做出了响应" );
     }
     
     @Override
     public  void  channelReadComplete(ChannelHandlerContext ctx)  throws  Exception {
         ctx.flush();
         System.out.println( "服务器readComplete 响应完成" );
     }
     
     @Override
     public  void  exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
             throws  Exception {
         ctx.close();
         System.out.println( "服务器异常退出" +cause.getMessage());
     }
}

服务器通过handler接收和处理消息请求,channelRead中的msg就是客户端请求的消息,通过解码获取具体信息后根据消息格式和定义完成后续的响应。

ByteBuf是netty封装和扩展的java NIO中的ByteBuffer类,功能更完善。通过ByteBuf接收和解码msg 转成String类型 然后判断命令是都准确,根据结果做出响应。


客户端:

客户端的处理比较简单,启动客户端,链接服务器成功后发送时间查询的指令,等待服务器响应。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package  c1;
 
import  io.netty.bootstrap.Bootstrap;
import  io.netty.channel.ChannelFuture;
import  io.netty.channel.ChannelInitializer;
import  io.netty.channel.ChannelOption;
import  io.netty.channel.EventLoopGroup;
import  io.netty.channel.nio.NioEventLoopGroup;
import  io.netty.channel.socket.SocketChannel;
import  io.netty.channel.socket.nio.NioSocketChannel;
/**
  * client 存在TCP粘包问题
  * @author xwlaker
  *
  */
public  class  TimeClient {
     /**
      * 连接服务器
      * @param port
      * @param host
      * @throws Exception
      */
     public  void  connect( int  port, String host)  throws  Exception {
         //配置客户端NIO线程组
         EventLoopGroup group =  new  NioEventLoopGroup();
         try  {
             //客户端辅助启动类 对客户端配置
             Bootstrap b =  new  Bootstrap();
             b.group(group).channel(NioSocketChannel. class )
                     .option(ChannelOption.TCP_NODELAY,  true )
                     .handler( new  ChannelInitializer() {
                         @Override
                         protected  void  initChannel(SocketChannel ch)
                                 throws  Exception {
                             ch.pipeline().addLast( new  TimeClientHandler());
                         }
                     });
             //异步链接服务器 同步等待链接成功
             ChannelFuture f = b.connect(host, port).sync();
             //等待链接关闭
             f.channel().closeFuture().sync();
 
         finally  {
             group.shutdownGracefully();
             System.out.println( "客户端优雅的释放了线程资源..." );
         }
 
     }
 
     public  static  void  main(String[] args)  throws  Exception {
         new  TimeClient().connect( 8000 "127.0.0.1" );
     }
 
}

客户端定义一组线程组用于处理与服务器的网络IO事件。通过客户端辅助启动类 Bootstrap来配置线程组、TCP参数以及IO事件处理的Handler。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package  c1;
 
import  io.netty.buffer.ByteBuf;
import  io.netty.buffer.Unpooled;
import  io.netty.channel.ChannelHandlerAdapter;
import  io.netty.channel.ChannelHandlerContext;
 
import  java.util.logging.Logger;
/**
  * Client 网络IO事件处理
  * @author xwalker
  *
  */
public  class  TimeClientHandler  extends  ChannelHandlerAdapter {
     private  static  final  Logger logger=Logger.getLogger(TimeClientHandler. class .getName());
     private   ByteBuf firstMessage;
     public  TimeClientHandler(){
         byte [] req = "QUERY TIME ORDER" .getBytes();
         firstMessage=Unpooled.buffer(req.length);
         firstMessage.writeBytes(req);
     }
     @Override
     public  void  channelActive(ChannelHandlerContext ctx)  throws  Exception {
         ctx.writeAndFlush(firstMessage);
         System.out.println( "客户端active" );
     }
     
     @Override
     public  void  channelRead(ChannelHandlerContext ctx, Object msg)
             throws  Exception {
         System.out.println( "客户端收到服务器响应数据" );
         ByteBuf buf=(ByteBuf) msg;
         byte [] req= new  byte [buf.readableBytes()];
         buf.readBytes(req);
         String body= new  String(req, "UTF-8" );
         System.out.println( "Now is:" +body);
         
     }
     
     @Override
     public  void  channelReadComplete(ChannelHandlerContext ctx)  throws  Exception {
         ctx.flush();
         System.out.println( "客户端收到服务器响应数据处理完成" );
     }
     
     @Override
     public  void  exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
             throws  Exception {
         logger.warning( "Unexpected exception from downstream:" +cause.getMessage());
         ctx.close();
         System.out.println( "客户端异常退出" );
     }
}

TimeClienthandler继承Netty提供的Handler适配器,重写channelActive和channelRead方法 前者通道打开active状态时 发送查询指令,后者接收服务器响应的消息并解码输出。


你可能感兴趣的:(开发经验)