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

一、服务器实现

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

 package com.coder.client;

 import java.util.Date;

 /**
* 自定义时间数据类
* @author Coder
*
*/
public class Time {
private final long value; public Time() {
// 除以1000是为了使时间精确到秒
this(System.currentTimeMillis() / 1000L);
} public Time(long value) {
this.value = value;
} public long value() {
return value;
} @Override
public String toString() {
return new Date((value()) * 1000L).toString();
}
}

2.  然后我们需要自定义服务器数据编码类

 package com.coder.server;

 import com.coder.client.Time;

 import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; /**
* 服务器数据编码类
* @author Coder
*
*/
public class TimeEncoderPOJO extends MessageToByteEncoder<Time> { // 发送数据时调用
@Override
protected void encode(ChannelHandlerContext ctx, Time msg, ByteBuf out) throws Exception {
// 只传输当前时间,精确到秒
out.writeInt((int)msg.value());
} }

3. 也需要自定义服务器的业务逻辑类,如下:

 package com.coder.server;

 import com.coder.client.Time;

 import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; /**
* 服务器解码器
* 连接建立时发送当前时间
* @author Coder
*
*/
public class TimeServerHandlerPOJO extends ChannelInboundHandlerAdapter {
/**
* 连接建立的时候并且准备进行通信时被调用
*/
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
// 发送当前时间信息
ChannelFuture f = ctx.writeAndFlush(new Time());
// 发送完毕之后关闭 Channel
f.addListener(ChannelFutureListener.CLOSE);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

4. 有了上面的代码,我们就可以实现服务器程序了,如下:

 package com.coder.server;

 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; public class TimeServerPOJO {
private int port; public TimeServerPOJO(int port) {
this.port = port;
} public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用来接收进来的连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 用来处理已经被接收的连接
System.out.println("准备运行端口:" + port); try {
ServerBootstrap b = new ServerBootstrap(); // 启动NIO服务的辅助启动类
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 这里告诉Channel如何接收新的连接
.childHandler( new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 自定义处理类
// 注意添加顺序
ch.pipeline().addLast(new TimeEncoderPOJO(),new TimeServerHandlerPOJO());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); // 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // 等待服务器socket关闭
f.channel().closeFuture().sync();
} catch (Exception e) {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
} public static void main(String[] args) throws Exception {
int port = 8080;
new TimeServer(port).run();
}
}

  执行代码后如下:

  

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

二、客户端实现

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

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

 package com.coder.client;

 import java.util.List;

 import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; public class TimeDecoderPOJO extends ByteToMessageDecoder {
/**
* 有新数据接收时调用
* 为防止分包现象,先将数据存入内部缓存,到达满足条件之后再进行解码
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if(in.readableBytes() < 4) {
return;
} // out添加对象则表示解码成功
out.add(new Time(in.readUnsignedInt()));
}
}

2. 自定义客户端业务逻辑类

 package com.coder.client;

 import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; /**
* 客户端数据处理类
* @author Coder
*
*/
public class TimeClientHandlerPOJO extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 直接将信息转换成Time类型输出即可
Time time = (Time)msg;
System.out.println(time);
ctx.close();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

3. 客户端程序实现

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

  1. 创建一个 NIO 线程组,用于处理服务器与客户端的连接,客户端不需要用到 boss worker。
  2. 创建一个 Bootstrap 对象,配置 Netty 的一系列参数,由于客户端 SocketChannel 没有父亲,所以不需要使用 childoption。
  3. 创建一个用于实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式以及实际处理数据的接口。
  4. 配置服务器 IP 和端口号,建立与服务器的连接。
 package com.coder.client;

 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; public class TimeClientPOJO {
public static void main(String[] args) throws Exception{
String host = "127.0.0.1"; // ip
int port = 8080; // 端口
EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
Bootstrap b = new Bootstrap(); // 与ServerBootstrap类似
b.group(workerGroup); // 客户端不需要boss worker
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true); // 客户端的socketChannel没有父亲
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// POJO
ch.pipeline().addLast(new TimeDecoderPOJO() ,new TimeClientHandlerPOJO());
}
}); // 启动客户端,客户端用connect连接
ChannelFuture f = b.connect(host, port).sync(); // 等待连接关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}

三、测试

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

  

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

  

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

  

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

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

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

    Netty入门二:开发第一个Netty应用程序 时间 2014-05-07 18:25:43  CSDN博客 原文  http://blog.csdn.net/suifeng3051/article/ ...

  2. 【卷二】网络二—TCP服务器与客户端

    经过上回简单地介绍,大家对服务器多少应该清楚一些了吧!还记得TCP: (Transmission Control Protocol) 传输控制协议? 还记得IP: (Internet Protocol ...

  3. netty 入门二 (传输bytebuf 或者pojo)

    基于流的数据传输:在基于流的传输(如TCP / IP)中,接收的数据被存储到套接字接收缓冲器中. 不幸的是,基于流的传输的缓冲区不是数据包的队列,而是字节队列. 这意味着,即使您将两个消息作为两个独立 ...

  4. 搭建NTP时间服务器~使用NTP同步时间~构建主机间时间自动同步关系

    NTP是一个时间服务器,同时它也是一个时间客户端. 我们可以使用它构建主机与主机之间的时间自动同步环境,保证所有服务器时间一致性. 常用的公共NTP时间服务器有: cn.ntp.org.cn 中国 n ...

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

    Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...

  6. Cloudera Manager安装之时间服务器和时间客户端(二)

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  7. Cloudera Manager安装之时间服务器和时间客户端(Ubuntu14.04)(二)

    第二步: Cloudera Manager安装之时间服务器和时间客户端(二) 找一台机器作为时间服务器 我这里,放到ubuntucmbigdata1这台机器! 注意,之前是已经做了集群时间同步了. 在 ...

  8. Netty入门(二)之PC聊天室

    参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...

  9. Netty入门(三)之web服务器

    Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ...

随机推荐

  1. jQuery选择器遇上一些特殊字符

    学习jQuery过程中,发现一些特殊字符,如“.”,“#”,"(","]"等.它在选择器应用时,按照普通处理就会出错.解决办法,就是使用转义字符来处理,这有点象 ...

  2. 使用eclipse上Tomcat插件配置域名、端口号、启动时间详解

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7856284.html 邮箱:moyi@moyib ...

  3. SpringMVC+Spring+MyBatis 整合与图片上传简单示例

    一.思路: (一) Dao层: 1. SqlMapConfig.xml,空文件即可.需要文件头.2. applicationContext_dao.xml. a) 数据库连接池b) SqlSessio ...

  4. JS 为什么在涉及到模块开发this的时候使用类似 self = this 的形式 p7

    JS 动态作用域(调用栈)实际上也没有准确说明的,大多数我们使用对多和认知上大多是词法作用域,但是this的机制跟动态作用域很像. var a = 2; function fn(){ console. ...

  5. js实现分享到QQ

    js代码 <script src="http://connect.qq.com/widget/loader/loader.js" widget="shareqq&q ...

  6. equals 与 == 的区别

    equals 是 用来比较二个对象内容是否相等. == 是用来比较二个对象的内存是否相等. public void testString(){ String s1="123"; S ...

  7. pm2以windows服务运行

    借助于pm2-windows-service 可以把pm2以windows服务运行.已服务运行的好处就是,即时用户注销也,pm2也会在后台运行 npm i pm2 -g npm i pm2-windo ...

  8. 解释JS变量作用域的范例

    JS的变量作用域只有两种:全局作用域与函数作用域. 用var声明的变量不能简单的说是属于函数作用域,应该是说属于其最近的作用域. var a = 10; function test(){ var a; ...

  9. .gitignore文件规则不起效的解决办法

    在一个项目里面,多少会有一些文件是不需要上传到git上面的,比如node的依赖模块node_modules,这个文件夹超过10000个文件,大小也超过80M.所以,一个.gitignore文件省不了, ...

  10. .NET中低版本程序调用高版本DLL

    在.NET项目开发中,有时需要对旧的程序进行二次开发,但是有些DLL是高版本的,如果对旧程序升级高版本,则需要改动的地方比较多,在项目比较急,开发时间短的情况下,可以通过下面方法让低版本程序调用高版本 ...