在中国程序界。我们都是学着Hello World !慢慢成长起来的。逐渐从一无所知到熟悉精通的。

  第二章就从Hello World 开始讲述Netty的中文教程。

  首先创建一个Java项目。引入一个Netty 框架的包。这个步骤我在本系列教程的后面就不在重复了。

  先上一张我示例的项目工程图给大家看一下:

1.下载并为项目添加Netty框架

  Netty的包大家可以从Netty官网:http://netty.io/downloads.html 下载

  

如图所示: Netty提供了三个主要版本的框架包给大家下载。

3.9版本Final 说明这个版本是3.x版本中最新的版本。final意味着功能不再继续添加更新。仅为修改bug等提供继续的更新。

5.x版本由于是开始。不能排除是否稳定运行等问题。加上5.x在4.x的版本上略微修改的。在5.x稳定之前。不推荐大家学习使用。

本教程是基于Netty4.x版本的。

  笔者也是从3.6版本,经过了相当痛苦的一段时间才算是真正的过度到4.x版本。

  下载之后解压缩。大家可以看到这样一个目录结构。非常的清晰。

  第一个文件夹jar是jar包的文件夹。第二个javadoc是API文档。第三个license文件夹是开源的授权文件(可以直接无视)。

  javadoc文件夹下面是一个jar包。可以直接解压缩出来。解压缩之后的文件夹就是api文档(以网页的形势展现)。

  jar文件夹里面有很多的jar包和一个all-in-one文件夹。都是Netty框架的组成部分。all-in-one里面有两个文件一个是jar包,另一个是对应的source源代码包。这样做的目的是为了给程序员有选择的添加自己所需要的包。

  假如读者是初学者的话。推荐直接套用all-in-one里面的jar包。假如你熟悉Netty的话可以根据自己的项目需求添加不同的jar包。

2.创建Server 服务端

  Netty创建全部都是实现自AbstractBootstrap。客户端的是Bootstrap,服务端的则是ServerBootstrap。

  

2.1创建一个 HelloServer

package org.example.hello;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; public class HelloServer { /**
* 服务端监听的端口地址
*/
private static final int portNumber = 7878; public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new HelloServerInitializer()); // 服务器绑定端口监听
ChannelFuture f = b.bind(portNumber).sync();
// 监听服务器关闭监听
f.channel().closeFuture().sync(); // 可以简写为
/* b.bind(portNumber).sync().channel().closeFuture().sync(); */
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

  EventLoopGroup 是在4.x版本中提出来的一个新概念。用于channel的管理。服务端需要两个。和3.x版本一样,一个是boss线程一个是worker线程。

  b.childHandler(new HelloServerInitializer());    //用于添加相关的Handler

  服务端简单的代码,真的没有办法在精简了感觉。就是一个绑定端口操作。

2.2创建和实现HelloServerInitializer

  在HelloServer中的HelloServerInitializer在这里实现。

  首先我们需要明确我们到底是要做什么的。很简单。HelloWorld!。我们希望实现一个能够像服务端发送文字的功能。服务端假如可以最好还能返回点消息给客户端,然客户端去显示。

  需求简单。那我们下面就准备开始实现。

  DelimiterBasedFrameDecoder Netty在官方网站上提供的示例显示 有这么一个解码器可以简单的消息分割。

  其次 在decoder里面我们找到了String解码编码器。着都是官网提供给我们的。

 1 package org.example.hello;
2
3 import io.netty.channel.ChannelInitializer;
4 import io.netty.channel.ChannelPipeline;
5 import io.netty.channel.socket.SocketChannel;
6 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
7 import io.netty.handler.codec.Delimiters;
8 import io.netty.handler.codec.string.StringDecoder;
9 import io.netty.handler.codec.string.StringEncoder;
10
11 public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {
12
13 @Override
14 protected void initChannel(SocketChannel ch) throws Exception {
15 ChannelPipeline pipeline = ch.pipeline();
16
17 // 以("\n")为结尾分割的 解码器
18 pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
19
20 // 字符串解码 和 编码
21 pipeline.addLast("decoder", new StringDecoder());
22 pipeline.addLast("encoder", new StringEncoder());
23
24 // 自己的逻辑Handler
25 pipeline.addLast("handler", new HelloServerHandler());
26 }
27 }

  上面的三个解码和编码都是系统。

  另外我们自己的Handler怎么办呢。在最后我们添加一个自己的Handler用于写自己的处理逻辑。

2.3 增加自己的逻辑HelloServerHandler

  自己的Handler我们这里先去继承extends官网推荐的SimpleChannelInboundHandler<C> 。在这里C,由于我们需求里面发送的是字符串。这里的C改写为String。

  

 1 package org.example.hello;
2
3 import java.net.InetAddress;
4
5 import io.netty.channel.ChannelHandlerContext;
6 import io.netty.channel.SimpleChannelInboundHandler;
7
8 public class HelloServerHandler extends SimpleChannelInboundHandler<String> {
9
10 @Override
11 protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
12 // 收到消息直接打印输出
13 System.out.println(ctx.channel().remoteAddress() + " Say : " + msg);
14
15 // 返回客户端消息 - 我已经接收到了你的消息
16 ctx.writeAndFlush("Received your message !\n");
17 }
18
19 /*
20 *
21 * 覆盖 channelActive 方法 在channel被启用的时候触发 (在建立连接的时候)
22 *
23 * channelActive 和 channelInActive 在后面的内容中讲述,这里先不做详细的描述
24 * */
25 @Override
26 public void channelActive(ChannelHandlerContext ctx) throws Exception {
27
28 System.out.println("RamoteAddress : " + ctx.channel().remoteAddress() + " active !");
29
30 ctx.writeAndFlush( "Welcome to " + InetAddress.getLocalHost().getHostName() + " service!\n");
31
32 super.channelActive(ctx);
33 }
34 }

  在channelHandlerContent自带一个writeAndFlush方法。方法的作用是写入Buffer并刷入。

  注意:在3.x版本中此处有很大区别。在3.x版本中write()方法是自动flush的。在4.x版本的前面几个版本也是一样的。但是在4.0.9之后修改为WriteAndFlush。普通的write方法将不会发送消息。需要手动在write之后flush()一次

  这里channeActive的意思是当连接活跃(建立)的时候触发.输出消息源的远程地址。并返回欢迎消息。

  channelRead0 在这里的作用是类似于3.x版本的messageReceived()。可以当做是每一次收到消息是触发。

  我们在这里的代码是返回客户端一个字符串"Received your message !".

  注意:字符串最后面的"\n"是必须的。因为我们在前面的解码器DelimiterBasedFrameDecoder是一个根据字符串结尾为“\n”来结尾的。假如没有这个字符的话。解码会出现问题。

2.Client客户端

  类似于服务端的代码。我们不做特别详细的解释。

  直接上示例代码:

  

 1 package org.example.hello;
2
3 import io.netty.bootstrap.Bootstrap;
4 import io.netty.channel.Channel;
5 import io.netty.channel.EventLoopGroup;
6 import io.netty.channel.nio.NioEventLoopGroup;
7 import io.netty.channel.socket.nio.NioSocketChannel;
8
9 import java.io.BufferedReader;
10 import java.io.IOException;
11 import java.io.InputStreamReader;
12
13 public class HelloClient {
14
15 public static String host = "127.0.0.1";
16 public static int port = 7878;
17
18 /**
19 * @param args
20 * @throws InterruptedException
21 * @throws IOException
22 */
23 public static void main(String[] args) throws InterruptedException, IOException {
24 EventLoopGroup group = new NioEventLoopGroup();
25 try {
26 Bootstrap b = new Bootstrap();
27 b.group(group)
28 .channel(NioSocketChannel.class)
29 .handler(new HelloClientInitializer());
30
31 // 连接服务端
32 Channel ch = b.connect(host, port).sync().channel();
33
34 // 控制台输入
35 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
36 for (;;) {
37 String line = in.readLine();
38 if (line == null) {
39 continue;
40 }
41 /*
42 * 向服务端发送在控制台输入的文本 并用"\r\n"结尾
43 * 之所以用\r\n结尾 是因为我们在handler中添加了 DelimiterBasedFrameDecoder 帧解码。
44 * 这个解码器是一个根据\n符号位分隔符的解码器。所以每条消息的最后必须加上\n否则无法识别和解码
45 * */
46 ch.writeAndFlush(line + "\r\n");
47 }
48 } finally {
49 // The connection is closed automatically on shutdown.
50 group.shutdownGracefully();
51 }
52 }
53 }

  下面的是HelloClientInitializer代码貌似是和服务端的完全一样。我没注意看。其实编码和解码是相对的。多以服务端和客户端都是解码和编码。才能通信。

  

 1 package org.example.hello;
2
3 import io.netty.channel.ChannelInitializer;
4 import io.netty.channel.ChannelPipeline;
5 import io.netty.channel.socket.SocketChannel;
6 import io.netty.handler.codec.DelimiterBasedFrameDecoder;
7 import io.netty.handler.codec.Delimiters;
8 import io.netty.handler.codec.string.StringDecoder;
9 import io.netty.handler.codec.string.StringEncoder;
10
11 public class HelloClientInitializer extends ChannelInitializer<SocketChannel> {
12
13 @Override
14 protected void initChannel(SocketChannel ch) throws Exception {
15 ChannelPipeline pipeline = ch.pipeline();
16
17 /*
18 * 这个地方的 必须和服务端对应上。否则无法正常解码和编码
19 *
20 * 解码和编码 我将会在下一张为大家详细的讲解。再次暂时不做详细的描述
21 *
22 * */
23 pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
24 pipeline.addLast("decoder", new StringDecoder());
25 pipeline.addLast("encoder", new StringEncoder());
26
27 // 客户端的逻辑
28 pipeline.addLast("handler", new HelloClientHandler());
29 }
30 }

  HellClientHandler:

  

 1 package org.example.hello;
2
3 import io.netty.channel.ChannelHandlerContext;
4 import io.netty.channel.SimpleChannelInboundHandler;
5
6 public class HelloClientHandler extends SimpleChannelInboundHandler<String> {
7
8 @Override
9 protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
10
11 System.out.println("Server say : " + msg);
12 }
13
14 @Override
15 public void channelActive(ChannelHandlerContext ctx) throws Exception {
16 System.out.println("Client active ");
17 super.channelActive(ctx);
18 }
19
20 @Override
21 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
22 System.out.println("Client close ");
23 super.channelInactive(ctx);
24 }
25 }

本教程的示例源代码:http://pan.baidu.com/s/1hABzK#dir

大家可以再我的百度云盘里面找到。

下面上几张成果图:

  客户端在连接建立是输出了Client active 信息,并收到服务端返回的Welcome消息。

  输入Hello World ! 回车发送消息。服务端响应返回消息已接受。

1.客户端控制台截图

2.服务端控制台截图

作者:TinyZ
出处:http://www.cnblogs.com/zou90512/

Netty4.x中文教程系列(二) Hello World !<转>的更多相关文章

  1. Netty4.x中文教程系列(二) Hello World !

    在中国程序界.我们都是学着Hello World !慢慢成长起来的.逐渐从一无所知到熟悉精通的. 第二章就从Hello World 开始讲述Netty的中文教程. 首先创建一个Java项目.引入一个N ...

  2. Netty4.x中文教程系列(一) 目录及概述

    Netty4.x中文教程系列(一)目录及概述 Netty 提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. Netty是一个NIO客户端 服务端框架 ...

  3. Netty4.x中文教程系列(六) 从头开始Bootstrap

    Netty4.x中文教程系列(六) 从头开始Bootstrap 其实自从中文教程系列(五)一直不知道自己到底想些什么.加上忙着工作上出现了一些问题.本来想就这么放弃维护了.没想到有朋友和我说百度搜索推 ...

  4. Netty4.x中文教程系列(五)编解码器Codec

    Netty4.x中文教程系列(五)编解码器Codec 上一篇文章详细解释了ChannelHandler的相关构架设计,版本和设计逻辑变更等等. 这篇文章主要在于讲述Handler里面的Codec,也就 ...

  5. Netty4.x中文教程系列(四) 对象传输

    Netty4.x中文教程系列(四)  对象传输 我们在使用netty的过程中肯定会遇到传输对象的情况,Netty4通过ObjectEncoder和ObjectDecoder来支持. 首先我们定义一个U ...

  6. Netty4.x中文教程系列(三) ChannelHandler

    Netty4.x中文教程系列(四)  ChannelHandler 上一篇文章详细解释了Hello World示例的代码.里面涉及了一些Netty框架的基础. 这篇文章用以解释ChannelHandl ...

  7. Netty4.x中文教程系列(三) Hello World !详解

    Netty 中文教程 (二) Hello World !详解 上一篇文章,笔者提供了一个Hello World 的Netty示例. 时间过去了这么久,准备解释一下示例代码. 1.HelloServer ...

  8. struts2官方 中文教程 系列二:Hello World项目

    先贴个本帖的地址,免得其它网站被爬去了struts2入门系列二之Hello World  即 http://www.cnblogs.com/linghaoxinpian/p/6898779.html ...

  9. Netty4.x中文教程系列(二) – 白话概念

    "Hello World"的代码固然简单,不过其中的几个重要概念(类)和 Netty的工作原理还是需要简单明确一下,至少知道其是负责什.方便自己以后更灵活的使用和扩展.   声明, ...

随机推荐

  1. selenium实战脚本集(2)——简单的知乎爬虫

    背景 很多同学在工作中是没有selenium的实战环境的,因此自学的同学会感到有力无处使,想学习但又不知道怎么练习.其实学习新东西的道理都是想通的,那就是反复练习.这里乙醇会给出一些有用的,也富有挑战 ...

  2. SharePoint 2013 Backup Farm Automatically With a Powershell and Windows Task Schedule

    In this post,I will show you SharePoint 2013 How to Backup Farm Automatically with a PowerShell and ...

  3. java播放wav文件

    import java.io.File; import java.io.IOException; import javax.sound.sampled.AudioFormat; import java ...

  4. jlink下载不进去程序

  5. JPA的多表复杂查询

    转 JPA的多表复杂查询:详细篇 原文链接: https://mp.weixin.qq.com/s/7J6ANppuiZJccIVN-h0T3Q 2017-11-10 从小爱喝AD钙  最近工作中由于 ...

  6. 微信小程序JS导出和导入

    1. 导出 1.1 方法和变量导出(写在被导出方法和变量的js文件) module.exports = { variable: value, method : methodName } 1.2 cla ...

  7. shell 知识点

        Q:1 Shell脚本是什么.它是必需的吗? 答:一个Shell脚本是一个文本文件,包含一个或多个命令.作为系统管理员,我们经常需要使用多个命令来完成一项任务,我们可以添加这些所有命令在一个文 ...

  8. ASP.NET MVC中的cshtml页面中的下拉框的使用

    ASP.NET MVC中的cshtml页面中的下拉框的使用 用上@Html.DropDownList 先记下来..以做备忘...

  9. PostgreSQL PL/Python 和 PL/Postgres 函数互相调用

    create or replace function hello(name text) returns text as $$ # str = name+',你吃饭了吗?'; # return 'hel ...

  10. 【Linux技术】linux库文件编写·入门

    一.为什么要使用库文件 我们在实际编程中肯定会遇到这种情况:有几个项目里有一些函数模块的功能相同,实现代码也相同,也是我们所说的重复代码.比如,很多项目里都有一个用户验证的功能. 代码段如下: //U ...