Netty入门(二)时间服务器及客户端
生活随笔
收集整理的這篇文章主要介紹了
Netty入门(二)时间服务器及客户端
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在這個(gè)例子中,我在服務(wù)器和客戶端連接被創(chuàng)立時(shí)發(fā)送一個(gè)消息,然后在客戶端解析收到的消息并輸出。并且,在這個(gè)項(xiàng)目中我使用 POJO 代替 ByteBuf 來(lái)作為傳輸對(duì)象。
一、服務(wù)器實(shí)現(xiàn)
1.? 首先我們自定義傳輸數(shù)據(jù)對(duì)象
1 package com.coder.client;2 3 import java.util.Date;4 5 /**6 * 自定義時(shí)間數(shù)據(jù)類7 * @author Coder8 *9 */ 10 public class Time { 11 private final long value; 12 13 public Time() { 14 // 除以1000是為了使時(shí)間精確到秒 15 this(System.currentTimeMillis() / 1000L); 16 } 17 18 public Time(long value) { 19 this.value = value; 20 } 21 22 public long value() { 23 return value; 24 } 25 26 @Override 27 public String toString() { 28 return new Date((value()) * 1000L).toString(); 29 } 30 }?
?
2.? 然后我們需要自定義服務(wù)器數(shù)據(jù)編碼類
1 package com.coder.server;2 3 import com.coder.client.Time;4 5 import io.netty.buffer.ByteBuf;6 import io.netty.channel.ChannelHandlerContext;7 import io.netty.handler.codec.MessageToByteEncoder;8 9 /** 10 * 服務(wù)器數(shù)據(jù)編碼類 11 * @author Coder 12 * 13 */ 14 public class TimeEncoderPOJO extends MessageToByteEncoder<Time> { 15 16 // 發(fā)送數(shù)據(jù)時(shí)調(diào)用 17 @Override 18 protected void encode(ChannelHandlerContext ctx, Time msg, ByteBuf out) throws Exception { 19 // 只傳輸當(dāng)前時(shí)間,精確到秒 20 out.writeInt((int)msg.value()); 21 } 22 23 }?
?
3. 也需要自定義服務(wù)器的業(yè)務(wù)邏輯類,如下:
1 package com.coder.server;2 3 import com.coder.client.Time;4 5 import io.netty.channel.ChannelFuture;6 import io.netty.channel.ChannelFutureListener;7 import io.netty.channel.ChannelHandlerContext;8 import io.netty.channel.ChannelInboundHandlerAdapter;9 10 /** 11 * 服務(wù)器解碼器 12 * 連接建立時(shí)發(fā)送當(dāng)前時(shí)間 13 * @author Coder 14 * 15 */ 16 public class TimeServerHandlerPOJO extends ChannelInboundHandlerAdapter { 17 /** 18 * 連接建立的時(shí)候并且準(zhǔn)備進(jìn)行通信時(shí)被調(diào)用 19 */ 20 @Override 21 public void channelActive(final ChannelHandlerContext ctx) throws Exception { 22 // 發(fā)送當(dāng)前時(shí)間信息 23 ChannelFuture f = ctx.writeAndFlush(new Time()); 24 // 發(fā)送完畢之后關(guān)閉 Channel 25 f.addListener(ChannelFutureListener.CLOSE); 26 } 27 28 @Override 29 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 30 cause.printStackTrace(); 31 ctx.close(); 32 } 33 }?
?
4. 有了上面的代碼,我們就可以實(shí)現(xiàn)服務(wù)器程序了,如下:
1 package com.coder.server;2 3 import io.netty.bootstrap.ServerBootstrap;4 import io.netty.channel.ChannelFuture;5 import io.netty.channel.ChannelInitializer;6 import io.netty.channel.ChannelOption;7 import io.netty.channel.EventLoopGroup;8 import io.netty.channel.nio.NioEventLoopGroup;9 import io.netty.channel.socket.SocketChannel; 10 import io.netty.channel.socket.nio.NioServerSocketChannel; 11 12 public class TimeServerPOJO { 13 private int port; 14 15 public TimeServerPOJO(int port) { 16 this.port = port; 17 } 18 19 public void run() throws Exception { 20 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用來(lái)接收進(jìn)來(lái)的連接 21 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 用來(lái)處理已經(jīng)被接收的連接 22 System.out.println("準(zhǔn)備運(yùn)行端口:" + port); 23 24 try { 25 ServerBootstrap b = new ServerBootstrap(); // 啟動(dòng)NIO服務(wù)的輔助啟動(dòng)類 26 b.group(bossGroup, workerGroup) 27 .channel(NioServerSocketChannel.class) // 這里告訴Channel如何接收新的連接 28 .childHandler( new ChannelInitializer<SocketChannel>() { 29 @Override 30 protected void initChannel(SocketChannel ch) throws Exception { 31 // 自定義處理類 32 // 注意添加順序 33 ch.pipeline().addLast(new TimeEncoderPOJO(),new TimeServerHandlerPOJO()); 34 } 35 }) 36 .option(ChannelOption.SO_BACKLOG, 128) 37 .childOption(ChannelOption.SO_KEEPALIVE, true); 38 39 // 綁定端口,開(kāi)始接收進(jìn)來(lái)的連接 40 ChannelFuture f = b.bind(port).sync(); 41 42 // 等待服務(wù)器socket關(guān)閉 43 f.channel().closeFuture().sync(); 44 } catch (Exception e) { 45 workerGroup.shutdownGracefully(); 46 bossGroup.shutdownGracefully(); 47 } 48 } 49 50 public static void main(String[] args) throws Exception { 51 int port = 8080; 52 new TimeServer(port).run(); 53 } 54 }執(zhí)行代碼后如下:
這時(shí)候服務(wù)器在等待客戶端的連接(非阻塞)。
?
二、客戶端實(shí)現(xiàn)
? 客戶端的實(shí)現(xiàn)與服務(wù)器類似。
1. 自定義客戶端數(shù)據(jù)解碼類
1 package com.coder.client;2 3 import java.util.List;4 5 import io.netty.buffer.ByteBuf;6 import io.netty.channel.ChannelHandlerContext;7 import io.netty.handler.codec.ByteToMessageDecoder;8 9 public class TimeDecoderPOJO extends ByteToMessageDecoder { 10 /** 11 * 有新數(shù)據(jù)接收時(shí)調(diào)用 12 * 為防止分包現(xiàn)象,先將數(shù)據(jù)存入內(nèi)部緩存,到達(dá)滿足條件之后再進(jìn)行解碼 13 */ 14 @Override 15 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 16 if(in.readableBytes() < 4) { 17 return; 18 } 19 20 // out添加對(duì)象則表示解碼成功 21 out.add(new Time(in.readUnsignedInt())); 22 } 23 }?
?
2. 自定義客戶端業(yè)務(wù)邏輯類
1 package com.coder.client;2 3 import io.netty.channel.ChannelHandlerContext;4 import io.netty.channel.ChannelInboundHandlerAdapter;5 6 /**7 * 客戶端數(shù)據(jù)處理類8 * @author Coder9 * 10 */ 11 public class TimeClientHandlerPOJO extends ChannelInboundHandlerAdapter { 12 @Override 13 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 14 // 直接將信息轉(zhuǎn)換成Time類型輸出即可 15 Time time = (Time)msg; 16 System.out.println(time); 17 ctx.close(); 18 } 19 20 @Override 21 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 22 cause.printStackTrace(); 23 ctx.close(); 24 } 25 }?
?
3. 客戶端程序?qū)崿F(xiàn)
? Netty 客戶端的通信步驟大致為:
?
?
?
?
?三、測(cè)試
? 先運(yùn)行服務(wù)器程序,運(yùn)行結(jié)果如下圖:
然后運(yùn)行客戶端程序,運(yùn)行結(jié)果如下圖:
需要注意的是,Eclipse 是可以同時(shí)運(yùn)行多個(gè) Java 程序的,可以通過(guò)點(diǎn)擊
來(lái)切換不同程序的控制臺(tái)輸出窗口。
總結(jié)
以上是生活随笔為你收集整理的Netty入门(二)时间服务器及客户端的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: UNIX再学习 -- 记录锁
- 下一篇: UNIX再学习 -- 高级 I/O