Netty入门(二)时间服务器及客户端

  在这个例子中,我在服务器和客户端连接被创立时发送一个消息,然后在客户端解析收到的消息并输出。并且,在这个项目中我使用 POJO 代替 ByteBuf 来作为传输对象。

一、服务器实现

1.  首先我们自定义传输数据对象

 1 package com.coder.client;
 2
 3 import java.util.Date;
 4
 5 /**
 6  * 自定义时间数据类
 7  * @author Coder
 8  *
 9  */
10 public class Time {
11     private final long value;
12
13     public Time() {
14         // 除以1000是为了使时间精确到秒
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.  然后我们需要自定义服务器数据编码类

 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  * 服务器数据编码类
11  * @author Coder
12  *
13  */
14 public class TimeEncoderPOJO extends MessageToByteEncoder<Time> {
15
16     // 发送数据时调用
17     @Override
18     protected void encode(ChannelHandlerContext ctx, Time msg, ByteBuf out) throws Exception {
19         // 只传输当前时间,精确到秒
20         out.writeInt((int)msg.value());
21     }
22
23 }

3. 也需要自定义服务器的数据处理类,如下:

 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  * 服务器解码器
12  * 连接建立时发送当前时间
13  * @author Coder
14  *
15  */
16 public class TimeServerHandlerPOJO extends ChannelInboundHandlerAdapter {
17     /**
18      * 连接建立的时候并且准备进行通信时被调用
19      */
20     @Override
21     public void channelActive(final ChannelHandlerContext ctx) throws Exception {
22         // 发送当前时间信息
23         ChannelFuture f = ctx.writeAndFlush(new Time());
24         // 发送完毕之后关闭 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. 有了上面的代码,我们就可以实现服务器程序了,如下:

 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();        // 用来接收进来的连接
21         EventLoopGroup workerGroup = new NioEventLoopGroup();    // 用来处理已经被接收的连接
22         System.out.println("准备运行端口:" + port);
23
24         try {
25             ServerBootstrap b = new ServerBootstrap();        // 启动NIO服务的辅助启动类
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             // 绑定端口,开始接收进来的连接
40             ChannelFuture f = b.bind(port).sync();
41
42             // 等待服务器socket关闭
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 }

  执行代码后如下:

  

  这时候服务器在等待客户端的连接(非阻塞)。

二、客户端实现

  客户端的实现与服务器类似。

1. 自定义客户端数据解码类

 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      * 有新数据接收时调用
12      * 为防止分包现象,先将数据存入内部缓存,到达满足条件之后再进行解码
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添加对象则表示解码成功
21         out.add(new Time(in.readUnsignedInt()));
22     }
23 }

2. 自定义客户端数据处理类

 1 package com.coder.client;
 2
 3 import io.netty.channel.ChannelHandlerContext;
 4 import io.netty.channel.ChannelInboundHandlerAdapter;
 5
 6 /**
 7  * 客户端数据处理类
 8  * @author Coder
 9  *
10  */
11 public class TimeClientHandlerPOJO extends ChannelInboundHandlerAdapter {
12     @Override
13     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
14         // 直接将信息转换成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. 客户端程序实现

  Netty 客户端的通信步骤大致为:

  1. 创建一个 NIO 线程组,用于处理服务器与客户端的连接,客户端不需要用到 boss worker。
  2. 创建一个 Bootstrap 对象,配置 Netty 的一系列参数,由于客户端 SocketChannel 没有父亲,所以不需要使用 childoption。
  3. 创建一个用于实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式以及实际处理数据的接口。
  4. 配置服务器 IP 和端口号,建立与服务器的连接。
 1 package com.coder.client;
 2
 3 import io.netty.bootstrap.Bootstrap;
 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.NioSocketChannel;
11
12 public class TimeClientPOJO {
13     public static void main(String[] args) throws Exception{
14         String host = "127.0.0.1";            // ip
15         int port = 8080;                    // 端口
16         EventLoopGroup workerGroup = new NioEventLoopGroup();
17
18         try {
19             Bootstrap b = new Bootstrap();            // 与ServerBootstrap类似
20             b.group(workerGroup);                    // 客户端不需要boss worker
21             b.channel(NioSocketChannel.class);
22             b.option(ChannelOption.SO_KEEPALIVE, true);    // 客户端的socketChannel没有父亲
23             b.handler(new ChannelInitializer<SocketChannel>() {
24                 @Override
25                 protected void initChannel(SocketChannel ch) throws Exception {
26                     // POJO
27                     ch.pipeline().addLast(new TimeDecoderPOJO() ,new TimeClientHandlerPOJO());
28                 }
29             });
30
31             // 启动客户端,客户端用connect连接
32             ChannelFuture f = b.connect(host, port).sync();
33
34             // 等待连接关闭
35             f.channel().closeFuture().sync();
36         } finally {
37             workerGroup.shutdownGracefully();
38         }
39     }
40 }

三、测试

  先运行服务器程序,运行结果如下图:

  

  然后运行客户端程序,运行结果如下图:

  

  需要注意的是,Eclipse 是可以同时运行多个 Java 程序的,可以通过点击

  

  来切换不同程序的控制台输出窗口。

原文地址:https://www.cnblogs.com/coderJiebao/p/Netty02.html

时间: 2025-01-14 15:29:51

Netty入门(二)时间服务器及客户端的相关文章

Netty入门二:开发第一个Netty应用程序

    既然是入门,那我们就在这里写一个简单的Demo,客户端发送一个字符串到服务器端,服务器端接收字符串后再发送回客户端. 2.1.配置开发环境 1.安装JDK 2.去官网下载jar包 (或者通过pom构建) 2.2.认识下Netty的Client和Server 一个Netty应用模型,如下图所示,但需要明白一点的是,我们写的Server会自动处理多客户端请求,理论上讲,处理并发的能力决定于我们的系统配置及JDK的极限. Client连接到Server端 建立链接发送/接收数据 Server端

windows server 2008搭建NTP时间服务器和客户端

背景:处于内网内的多台服务器(我们称之为NTP客户端)时间经过长时间运行,系统时间相差5分钟,有一台服务器连接外网(我们称为NTP服务器). 由于外网服务器可以和internet时间同步,故选做NTP服务器. 部署步骤: 一:服务端 默认情况下,独立服务器WINDOWS SERVER 2008是作为NTP客户端工作的 ,所以必须通过修改注册表,以使系统作为NTP服务器运行.工作之前请先备份注册表文件.     1.修改以下选项的键值HKEY_LOCAL_MACHINE\SYSTEM\Curren

Netty服务器自动识别客户端已断开

最近开发了使用的Netty的长连接服务器,客户端支持使用socket和websocket的js连接,也可以使用flash连接.但是遇到一个很麻烦的问题,当客户端的网络断开或因为异常断开的时候,服务器并不能立即自动知道客户端已断开.幸好遇到一个同事,比较熟悉网络,因此帮忙解决了这个问题,特此写在这,希望能帮助使用netty的朋友们. 之前尝试解决问题的时候,看了源码,发现了一个方法: public class TextWebSocketFrameHandler extends SimpleChan

Netty入门之客户端与服务端通信(二)

Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码吧. 二.客户端与服务端的通信 2.1 服务端启动程序 public class MyServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = ne

netty 3.9.2 UDP协议服务器和客户端DEMO

说明:基于netty 3.9.2的udp协议实现的(如果你使用的版本是4.X或5.X,请参考其他方法):程序的逻辑结构是,客户端发送给服务端一串数据,服务器端返回给客户端“A”.在进行游戏开发时需要对udp的丢包进行处理,可以利用服务器端的返回值进行相关处理,以确定是否重发,这方面具体没有实现. 文章结构: 一.服务器端 1.UDPServer 2.UdpChannelPipelineFactory 3.UDPServerHandler 二.客户端 1.UDPClient 2.UDPClient

CentOS7搭建NTP服务器及客户端同步时间

一.服务器配置 1.查看服务器.客户端操作系统版本 [[email protected] ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) 2.查看服务器是否安装ntp,系统默认安装ntpdate: [[email protected] ~]# rpm -qa | grep ntp fontpackages-filesystem-1.44-8.el7.noarch ntpdate-4.2.6p5-28.el7.cen

Muduo网络库实战(二):实现服务器与客户端的连接

1. 方案的确定 1)基本需求 用户1000+, IO压力不大: 多个客户端打开网站,输入查询字符串strclient,发送给服务器=>服务器接收客户端发过来的数据并处理,将结果返回给客户端: 2)并发网络服务程序设计方案 详见:<Muduo_网络库使用手册>的1.6节-<详解Muduo多线程模型> @ muduo中TcpServer模式的选择:多线程模式 模式一:单线程,accept与TcpConnection用同一个线程做IO; 模式二:多线程,accept与EventL

单点登录CAS使用记(二):部署CAS服务器以及客户端

CAS-Server下载地址:https://www.apereo.org/projects/cas/download-cas CAS-Client下载地址:http://developer.jasig.org/cas-clients/ CAS官方教程: https://wiki.jasig.org/display/CASUM/CAS+on+Windows+Quick+Setup+Guide 版本: CAS Server版本:cas-server-3.4.11 CAS Client版本:cas-

2016windows(10) wamp 最简单30分钟thrift入门使用讲解,实现php作为服务器和客户端的hello world

2016最简单windows(10) wamp 30分钟thrift入门使用讲解,实现php作为服务器和客户端的hello world thrift是什么 最简单解释 thrift是用来帮助各个编程语言之间进行通信,交换信息的一个框架(可以理解成一个工具,或者假象成一个软件).因为正常情况下php与java等语言无法进行数据的传递,当然平时我们运用接口传递jeson数据实现.但是使用thrift会有它的优势,我看到是说他的数据传输方式使得数据传输量很小,这方面可以自己搜索了解. 例如: 有人写了