目录

 

一、Netty介绍

1、基本介绍

2、主要应用

二、Netty服务端hello world

1、导入 pom

2、server 服务端代码

3、测试

4、分析


一、Netty介绍

1、基本介绍

  1. NIO是一个非阻塞的多线程的socket网络通信API

  2. 每次使用NIO进行网络通信的时候我们都需要自己编写网络交互的服务端、客户端,都要去编写数据的接收、解析、返回等逻辑方法,十分麻烦。基于此,JBOSS就推出了一个可以快速开发高性能、高可靠性的网络服务器和客户端程序的框架-----Netty。

  3. Netty说白了就是一个基于NIO的客户、服务器端编程框架,本来需要写很久的客户端和服务端的代码,这里只需要Netty就可以快速、简单的开发出一个实现某种协议的网络应用。

  4. Netty版本大致版本分为Netty3.x、Netty4.x和Netty5.x。

2、主要应用

(1)分布式进程通信

像Hadoop、dubbo、akka等具有分布式功能的框架,底层RPC通信都是基于Netty实现的,这些框架版本通常都还在使用Netty3.x。

(2)游戏服务器开发

众所周知,许多网络游戏需要在各个客户端传输网络信息,所以也需要快速搭建客户端与服务端之间的网络交互逻辑。

最新的游戏服务器有部分公司可能已经开始使用Netty4.x和Netty5.x。

二、Netty服务端hello world

主要针对Netty3.x进行讲解,后面逐步衍生到Netty4.x以上

1、导入 pom

    <properties>
        <!--<netty.version>4.1.41.Final</netty.version>-->
        <netty.version>3.10.5.Final</netty.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty</artifactId>
            <version>${netty.version}</version>
        </dependency>

    </dependencies>

2、server 服务端代码

Server.java

import org.jboss.netty.channel.Channels;

import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;


/**
 * \* Created with IntelliJ IDEA.
 * \* User: sunxianpeng
 * \* Date: 2019/9/29
 * \* Time: 18:32
 * \* To change this template use File | Settings | File Templates.
 * \* Description:based on netty 3.10.5.Final
 * \
 */
public class Server {
    public static void main(String[] args) {
        //1.创建一个Netty服务类
        /**
         * ServerBootstrap 是一个启动NIO服务的辅助启动类
         * 你可以在这个服务中直接使用Channel
         * */
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //2.创建两个线程池
        /**
         * 第一个经常被叫做‘boss’,用来接收进来的连接。
         * 第二个经常被叫做‘worker’,用来处理已经被接收的连接,
         * 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。
         * */
        ExecutorService boss = Executors.newCachedThreadPool();
        ExecutorService worker = Executors.newCachedThreadPool();
        //3.为服务类设置一个NioSocket工厂
        serverBootstrap.setFactory(new NioServerSocketChannelFactory(boss, worker));
        //4.设置管道的工厂(匿名内部类实现)
        serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                //设置一个处理服务端消息和各种消息事件的类(Handler)
                pipeline.addLast("hellohandler", new HelloHandler());
                return pipeline;
            }
        });

        //5.为服务端设置一个端口
        serverBootstrap.bind(new InetSocketAddress(10101));
        System.out.println("server start!");
    }
}
HelloHandler.java
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
 * \* Created with IntelliJ IDEA.
 * \* User: sunxianpeng
 * \* Date: 2019/9/29
 * \* Time: 18:43
 * \* To change this template use File | Settings | File Templates.
 * \* Description:based on netty 3.10.5.Final
 * \
 */
public class HelloHandler extends SimpleChannelHandler {
    /**
     * 连接关闭
     */
    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
            throws Exception {
        System.out.println("channelClosed");
        super.channelClosed(ctx, e);
    }
    /**
     * 新建连接
     * */
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
            throws Exception {
        System.out.println("channelConnected");
        super.channelConnected(ctx, e);
    }
    /**
     * 连接断开
     * */
    @Override
    public void channelDisconnected(ChannelHandlerContext ctx,
                                    ChannelStateEvent e) throws Exception {
        System.out.println("channelDisconnected");
        super.channelDisconnected(ctx, e);
    }
    /**
     * 捕获异常
     * */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception {
        System.out.println("exceptionCaught,error:"+e.toString());
        super.exceptionCaught(ctx, e);
    }
    /**
     * 消息接收
     * */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception {
        System.out.println("messageReceived");
        //获取到的信息是使用ChannelBuffer包装的字节流
        ChannelBuffer message = (ChannelBuffer)e.getMessage();
        String msg = new String(message.array());//将字节流转换为String
        System.out.println(msg);//打印收到的字符串
        super.messageReceived(ctx, e);
    }
}

3、测试

启动服务端,在window 控制台 使用 telnet  开启多个客户端 连接服务端

telnet 127.0.0.1 10101

按键 Ctrl + ]  后,使用如下方法,向服务端发送信息

send hello world

可以同时处理过个连接。

4、分析

(1)关闭cmd,可以看到控制台先打印了“channelDisconnected”,然后打印了“channelClosed”

原因是,对于channelDisconnected方法,是必须有连接建立之后,关闭通道的时候才会触发此方法,而对于channelClosed就是无论连接建立与否,在channel关闭的时候就会触发。(例如客户端连接失败后,不会触发channelDisconnected,只会触发channelClosed)。

(2)我们的消息接收时总是要把字节流转换为String字符串,可不可以省去这一步,直接拿到字符串呢?

是可以的,我们只需要在服务端的管道的工厂中设置decoder解码类为“StringDecoder”类即可:

//4.设置管道的工厂(匿名内部类实现)
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
	
	@Override
	public ChannelPipeline getPipeline() throws Exception {
		ChannelPipeline pipeline = Channels.pipeline();
		//设置解码方式为String
		pipeline.addLast("decoder", new StringDecoder());
		//设置一个处理服务端消息和各种消息事件的类(Handler)  
		pipeline.addLast("hellohandler", new HelloHandler());
		return pipeline;
	}
});
/**
 * 消息接收
 * */
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
		throws Exception {
	System.out.println("messageReceived");
	String msg = (String)e.getMessage();//将字节流转换为String
	System.out.println(msg);//打印收到的字符串
	super.messageReceived(ctx, e);
}

(3)Server服务端接收到信息后,可以回写给客户端信息,我们在messageReceived中编写回复逻辑

/**
 * 消息接收
 * */
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
		throws Exception {
	System.out.println("messageReceived");
	String msg = (String)e.getMessage();//将字节流转换为String
	System.out.println(msg);//打印收到的字符串
	
	//回写数据(需要用ChannelBuffer包装要回复的字节流信息)
	ChannelBuffer channelBuffer = ChannelBuffers.copiedBuffer("hi".getBytes());
	ctx.getChannel().write(channelBuffer);
	super.messageReceived(ctx, e);
}

重新启动服务端,然后使用telnet进去后(不用ctrl+])随便输一个字符(这里输入a),就拿到了服务端的回复信息

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐