Netty In Action中国版 – 第二章:第一Netty程序

Netty In Action中国版 – 第二章:第一Netty程序

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

本章介绍

  • 获得Netty4最新的版本号
  • 设置执行环境,以构建和执行netty程序
  • 创建一个基于Netty的server和client
  • 拦截和处理异常
  • 编制和执行Nettyserver和client

        本章将简介Netty的核心概念,这个狠心概念就是学习Netty是怎样拦截和处理异常。对于刚開始学习netty的读者。利用netty的异常拦截机制来调试程序问题非常有帮助。本章还会介绍其它一些核心概念。如server和client的启动以及分离通道的处理程序。本章学习一些基础以便后面章节的深入学习。

本章中将编写一个基于netty的server和client来互相通信,我们首先来设置netty的开发环境。

2.1 设置开发环境

        设置开发环境的过程例如以下:
  • 安装JDK7,下载地址http://www.oracle.com/technetwork/java/javase/archive-139210.html
  • 下载netty包,下载地址http://netty.io/
  • 安装Eclipse

        《Netty In Action》中描写叙述的比較多,没啥用。这里就不多说了。
本系列博客将使用Netty4。须要JDK1.7+

2.2 Nettyclient和server概述

        本节将引导你构建一个完整的Nettyserver和client。普通情况下。你可能仅仅关心编写server,如一个httpserver的client是浏览器。然后在这个样例中,你若同一时候实现了server和client。你将会对他们的原理更加清晰。
        一个Netty程序的工作图例如以下
Netty In Action中国版 - 第二章:第一Netty程序

  1. client连接到server
  2. 建立连接后,发送或接收数据
  3. server处理全部的client连接

        从上图中能够看出,server会写数据到client而且处理多个client的并发连接。从理论上来说,限制程序性能的因素仅仅有系统资源和JVM。为了方便理解,这里举了个生活样例。在山谷或高山上大声喊,你会听见回声。回声是山返回的;在这个样例中,你是client。山是server。喊的行为就类似于一个Nettyclient将数据发送到server,听到回声就类似于server将同样的数据返回给你,你离开山谷就断开了连接。可是你能够返回进行重连server而且能够发送很多其它的数据。

        尽管将同样的数据返回给client不是一个典型的样例。可是client和server之间数据的来来回回的传输和这个样例是一样的。本章的样例会证明这一点,它们会越来越复杂。

        接下来的几节将带着你完毕基于Netty的client和server的应答程序。

2.3 编写一个应答server

        写一个Nettyserver主要由两部分组成:

  • 配置server功能,如线程、port
  • 实现server处理程序,它包括业务逻辑,决定当有一个请求连接或接收数据时该做什么

2.3.1 启动server

        通过创建ServerBootstrap对象来启动server。然后配置这个对象的相关选项。如port、线程模式、事件循环,而且加入逻辑处理程序用来处理业务逻辑(以下是个简单的应答server样例)

package netty.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class EchoServer {

	private final int port;

	public EchoServer(int port) {
		this.port = port;
	}

	public void start() throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			//create ServerBootstrap instance
			ServerBootstrap b = new ServerBootstrap();
			//Specifies NIO transport, local socket address
			//Adds handler to channel pipeline
			b.group(group).channel(NioServerSocketChannel.class).localAddress(port)
					.childHandler(new ChannelInitializer<Channel>() {
						@Override
						protected void initChannel(Channel ch) throws Exception {
							ch.pipeline().addLast(new EchoServerHandler());
						}
					});
			//Binds server, waits for server to close, and releases resources
			ChannelFuture f = b.bind().sync();
			System.out.println(EchoServer.class.getName() + "started and listen on “" + f.channel().localAddress());
			f.channel().closeFuture().sync();
		} finally {
			group.shutdownGracefully().sync();
		}
	}
	
	public static void main(String[] args) throws Exception {
		new EchoServer(65535).start();
	}

}

        从上面这个简单的server样例能够看出,启动server应先创建一个ServerBootstrap对象。由于使用NIO,所以指定NioEventLoopGroup来接受和处理新连接。指定通道类型为NioServerSocketChannel,设置InetSocketAddress让server监听某个端口已等待client连接。

        接下来,调用childHandler放来指定连接后调用的ChannelHandler。这种方法传ChannelInitializer类型的參数。ChannelInitializer是个抽象类,所以须要实现initChannel方法,这种方法就是用来设置ChannelHandler。

        最后绑定server等待直到绑定完毕。调用sync()方法会堵塞直到server完毕绑定,然后server等待通道关闭。由于使用sync(),所以关闭操作也会被堵塞。如今你能够关闭EventLoopGroup和释放全部资源,包含创建的线程。

        这个样例中使用NIO,由于它是眼下最经常使用的传输方式。你可能会使用NIO非常长时间。可是你能够选择不同的传输实现。比如。这个样例使用OIO方式传输,你须要指定OioServerSocketChannel。Netty框架中实现了多重传输方式,将再后面讲述。
        本小节重点内容:

  • 创建ServerBootstrap实例来引导绑定和启动server
  • 创建NioEventLoopGroup对象来处理事件,如接受新连接、接收数据、写数据等等
  • 指定InetSocketAddress。server监听此port
  • 设置childHandler运行全部的连接请求
  • 都设置完成了,最后调用ServerBootstrap.bind() 方法来绑定server

2.3.2 实现server业务逻辑

        Netty使用futures和回调概念。它的设计同意你处理不同的事件类型。更具体的介绍将再后面章节讲述。可是我们能够接收数据。

你的channel handler必须继承ChannelInboundHandlerAdapter而且重写channelRead方法,这种方法在不论什么时候都会被调用来接收数据,在这个样例中接收的是字节。

        以下是handler的实现,事实上现的功能是将client发给server的数据返回给client:

package netty.example;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		System.out.println("Server received: " + msg);
		ctx.write(msg);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}

}

        Netty使用多个Channel Handler来达到对事件处理的分离,由于能够非常容的加入、更新、删除业务逻辑处理handler。

Handler非常easy,它的每一个方法都能够被重写,它的全部的方法中仅仅有channelRead方法是必需要重写的。

2.3.3 捕获异常

        重写ChannelHandler的exceptionCaught方法能够捕获server的异常,比方client连接server后强制关闭,server会抛出”client主机强制关闭错误”,通过重写exceptionCaught方法就能够处理异常,比方发生异常后关闭ChannelHandlerContext。

2.4 编写应答程序的client

        server写好了。如今来写一个client连接server。应答程序的client包含下面几步:
  • 连接server
  • 写数据到server
  • 等待接受server返回同样的数据
  • 关闭连接

2.4.1 引导client

        引导client启动和引导server非常类似,client需同一时候指定host和port来告诉client连接哪个server。看以下代码:
package netty.example;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.example.echo.EchoClientHandler;

import java.net.InetSocketAddress;

public class EchoClient {

	private final String host;
	private final int port;

	public EchoClient(String host, int port) {
		this.host = host;
		this.port = port;
	}

	public void start() throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new EchoClientHandler());
						}
					});
			ChannelFuture f = b.connect().sync();
			f.channel().closeFuture().sync();
		} finally {
			group.shutdownGracefully().sync();
		}
	}

	public static void main(String[] args) throws Exception {
		new EchoClient("localhost", 20000).start();
	}
}

创建启动一个client包括以下几步:

  • 创建Bootstrap对象用来引导启动client
  • 创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup能够理解为是一个线程池。这个线程池用来处理连接、接受数据、发送数据
  • 创建InetSocketAddress并设置到Bootstrap中。InetSocketAddress是指定连接的server地址
  • 加入一个ChannelHandler,client成功连接server后就会被运行
  • 调用Bootstrap.connect()来连接server
  • 最后关闭EventLoopGroup来释放资源

2.4.2 实现client的业务逻辑

        client的业务逻辑的实现依旧非常easy,更复杂的使用方法将在后面章节具体介绍。

和编写server的ChannelHandler一样,在这里将自己定义一个继承SimpleChannelInboundHandler的ChannelHandler来处理业务;通过重写父类的三个方法来处理感兴趣的事件:

  • channelActive():client连接server后被调用
  • channelRead0():从server接收到数据后调用
  • exceptionCaught():发生异常时被调用

        实现代码例如以下

package netty.example;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
	
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		ctx.write(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
		System.out.println("Client received: " + ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())));
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		ctx.close();
	}
}

        可能你会问为什么在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter?主要原因是ChannelInboundHandlerAdapter在处理完消息后须要负责释放资源。

在这里将调用ByteBuf.release()来释放资源。SimpleChannelInboundHandler会在完毕channelRead0后释放消息,这是通过Netty处理全部消息的ChannelHandler实现了ReferenceCounted接口达到的。

        为什么在server中不使用SimpleChannelInboundHandler呢?由于server要返回同样的消息给client,在server运行完毕写操作之前不能释放调用读取到的消息,由于写操作是异步的,一旦写操作完毕后,Netty中会自己主动释放消息。
        client的编写完了,以下让我们来測试一下

2.5 编译和执行echo(应答)程序client和server

        注意,netty4须要jdk1.7+。

        本人測试,能够正通常执行。

2.6 总结一下

        本章介绍如何编写基于一个简单的Netty的server和client和发送数据通信。它描述了如何创建server和client以及Netty异常处理机制。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116691.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • win10企业版永久激活方法「建议收藏」

    win10企业版永久激活方法「建议收藏」win10企业版永久激活方法?win10企业版是针对企业用户推出的版本,随着win10系统的不断完善,现在越来越多的人选择升级win10,升级完系统就需要激活它。那么今天就为大家分享一下怎么永久激活w

  • 【2022年更新】手把手教你去除 WinRAR 的弹窗广告

    【2022年更新】手把手教你去除 WinRAR 的弹窗广告WinRAR作为知名的老牌压缩软件,二十余年来始终风靡全球,经久不衰。但对于中国用户,其简体中文的个人免费版安装后会有“评估版本”的标记,而且每次启动时会有代理商的弹窗广告。本文教你如何去除标记和弹窗广告,让你的WinRAR恢复纯净体验。

  • java 取余和取模运算之间的区别「建议收藏」

    java 取余和取模运算之间的区别「建议收藏」转自lee371042https://blog.csdn.net/lee371042/article/details/102553342packageOperator;importjava.math.BigInteger;/***假如有两个数:*amod(b)与a%b,b为正整数,*一种叫a对b取模,另一个叫a对b取余,两种叫法有什么区别呢?*通常情况下,取模运算也叫取余运算,*它们返回的结果都是一个数对另一个数的余数,**区别在于当a是一

  • android sdk manager安装,Android SDK Manager安装过程

    android sdk manager安装,Android SDK Manager安装过程1、首先要下载安装JavaJDK,下载地址:http://pan.baidu.com/share/link?shareid=7368&uk=2148507123然后在下载最新的AndroidSDK4.1文件,下载地址:http://dl.google.com/android/android-sdk_r20-windows.zip或:http://pan.baidu.com/share…

  • 数据分析,主成分分析例题

    数据分析,主成分分析例题已知协方差矩阵求X的各主成分以及主成分的贡献率主成分分析原理:找出几个综合变量来代替原来众多的变量,使这些综合变量能尽可能地代表原来变量的信息量,且彼此之间互不相关统计方法:主成分分析(主分量分析)主成分分析步骤1.根据已知协方差矩阵,求出相应的特征值(特征根)令|kE-A|=0(其中k是特征值),求出的k就是所需要的特征值2.求出对应特征值的特征向量解方程|kE-A|X=0,求X的所有情况(参考高等代数的第三章解线性方程组)求出基本解系,设定自由未知量的值(X是向量)3.对所求出来

    2022年10月27日
  • Linux——./configure && make && make install命令解析

    Linux——./configure && make && make install命令解析当我们在Linux上安装相关的软件源码包时,一般会涉及到以下三个步骤,分别是 ./configure、make、make install————————————————————————————————————————————————————第一步./configure 该步骤主要用来检测系统的配置、环境以及相关依赖,如果缺少相关依赖,该脚本会中止执行,软件安装失败如:它会检…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号