欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Netty教程系列(一)——netty入门应答程序

程序员文章站 2022-07-14 18:15:34
...

Netty简介

Netty是一个异步通信、事件驱动基于NIO编写的高性能高并发的java网络编程框架。下面通过一个简单的服务器应答程序来完成Netty的初步学习。

代码地址:https://code.csdn.net/luo4105/study_netty/

Netty的编程例子——应答程序

设置开发环境

Jdk1.8

Mvn配置

<dependencies>
    <dependency>
       <groupId>io.netty</groupId>
       <artifactId>netty-all</artifactId>
       <version>4.1.12.Final</version>
    </dependency>
</dependencies>

应答服务器

一个Netty服务器主要有三个部分组成

l  服务器配置,配置端口、操作线程池等。

l  通道初始程序,传输的编解码格式、粘包处理、通道处理程序的调用。

l  实现通道处理程序,它包含业务逻辑,即实现服务器通道发生连接、读取信息等事件时的处理。

启动服务器

通过创建SerserBootstrap对象来启动服务器,然后配置对象的相关属性,如端口、线程模式、处理程序等。

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 classHelloServer {
 
    private static final int protNumner= 7878;
 
    public static void main(String[] args) throws InterruptedException {
       // 事件循环组
       /*
        * Main函数开始的位置定义了两个工作线程,一个命名为WorkerGroup,另一个命名为BossGroup。
        * 都是实例化NioEventLoopGroup。这一点和3.x版本中基本思路是一致的。Worker线程用于管理线程为Boss线程服务。
        * EventLoopGroup,它是4.x版本提出来的一个新概念。类似于3.x版本中的线程。用于管理Channel连接的。
        * 在main函数的结尾就用到了EventLoopGroup提供的便捷的方法,shutdownGraceFully(),
        * 翻译为中文就是优雅的全部关闭。
        */
       EventLoopGroupbossGroup= newNioEventLoopGroup();
       EventLoopGroupworkerGroup= newNioEventLoopGroup();
 
       /* ServerBootstrap负责初始化netty服务器,并且开始监听端口的socket请求 */
       ServerBootstrapb = new ServerBootstrap();
       b.group(bossGroup, workerGroup);
       b.channel(NioServerSocketChannel.class);
       b.childHandler(newHelloServerInitializer());
 
       // 服务器绑定端口监听
       ChannelFuturef = b.bind(protNumner).sync();
       // 监听服务器关闭监听
       f.channel().closeFuture().sync();
    }
}

通道的初始化程序

初始化程序可以加上编解码、消息分割(粘包)、业务处理程序等。这些设置都是通过ChannelPipeline.addLast()来实现的。

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
 
public classHelloServerInitializer extends ChannelInitializer<SocketChannel> {
 
    @Override
    protected voidinitChannel(SocketChannel ch) throwsException {
       ChannelPipelinepipeline= ch.pipeline();
 
       // 以("\n")为结尾分割的解码器,最大帧长度为8192
       // Delimiter Based Frame Decoder 基于定界符的帧解码器
       // Delimiters.lineDelimiter()行分隔符
       // 这里使用时,会把换行符作为一个消息的分隔界限使用,即接收时,换行符前为一条消息
       pipeline.addLast("framer",newDelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
 
       // 字符串解码和编码
       // encoder 编码器, decoder 解码器
       pipeline.addLast("decoder",newStringDecoder());
       pipeline.addLast("encoder",newStringEncoder());
 
       // 自己的逻辑Handler
       pipeline.addLast("handler",newHelloServerHandler());
    }
}

通道的业务处理程序

处理程序是事件驱动的,用于监控服务器事件的产生,自定义事件的处理。

import java.net.InetAddress;
 
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
 
public classHelloServerHandler extends SimpleChannelInboundHandler<String> {
 
    @Override
    protected voidchannelRead0(ChannelHandlerContext ctx, String msg)throwsException {
       // 收到消息直接打印输出
       System.out.println(ctx.channel().remoteAddress()+ " Say : "+ msg);
 
       // 返回客户端信息 - 我已经接收到了你的消息
       // writeAndFlush写消息并发送
       ctx.writeAndFlush("received your message !\n");
    }
 
    // 当Channel变成活跃状态时被调用
    public voidchannelActive(ChannelHandlerContext ctx) throwsException {
       System.out.println("RamoteAddress : "+ ctx.channel().remoteAddress()+ " active !");
 
       ctx.writeAndFlush("welcome to "+ InetAddress.getLocalHost().getHostName() + " server.\n");
 
       super.channelActive(ctx);
    }
}

SimpleChannelInboundHandler这里实现事件处理,ChannelRead0是读取数据事件,channelActive是通道连接事件。

除了以上事件,还有如下事件

void channelInactive(ChannelHandlerContextctx)  连接中断时,调用它

void userEventTriggered(ChannelHandlerContext ctx, Object evt)  如果触发了用户事件,将调用它。

void channelWritabilityChanged(ChannelHandlerContextctx)  一旦更改了{连接信道}的可写状态,就调用。您可以检查状态

void exceptionCaught(ChannelHandlerContextctx, Throwable cause)  如果抛出异常,将调用它。

在writeAndFlush后要接”\n”,pipeline设置的是通过换行符来分割信息。

编写应答程序客户端

一个客户端程序主要有三部分组成,与服务器类似

l  连接服务器,配置服务器的ip、端口,创建服务器连接通道对象,向服务器发送数据

l  通道初始程序,传输的解编码格式、粘包处理、通道处理程序的调用。

l  实现通道处理程序,它包含业务逻辑,即实现客户端通道发生连接、读取信息等事件时的处理。

连接服务器

Netty4通过Bootstrap来创建通道连接对象Channel,通过Channel的writeAndFlush来向服务器发送数据。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
 
/**
 * netty客户端
 *
 * @author逝兮诚
 * @date 2017年6月26日下午5:37:23
 *
 */
public classHelloClient {
 
    public static String host = "127.0.0.1";
    public static int port = 7878;
 
    public static void main(String[] args) throws IOException,InterruptedException {
       EventLoopGroupgroup= newNioEventLoopGroup();
       Bootstrapb = new Bootstrap();
       b.group(group).channel(NioSocketChannel.class).handler(newHelloClientInitializer());
 
       // 连接服务端
       Channelch = b.connect(host, port).sync().channel();
 
       // 控制台输入
       BufferedReaderin = new BufferedReader(newInputStreamReader(System.in));
       for (;;) {
           Stringline= in.readLine();
           if (line == null)
              continue;
           ch.writeAndFlush(line + "\r\n");
       }
    }
}

通道的初始化设置程序

初始化设置程序同服务器一样,便不再过多累述。

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
 
public classHelloClientInitializer extends ChannelInitializer<SocketChannel> {
 
    @Override
    protected voidinitChannel(SocketChannel ch) throwsException {
       ChannelPipelinepipeline= ch.pipeline();
 
       pipeline.addLast("framer",newDelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
       pipeline.addLast("decoder",newStringDecoder());
       pipeline.addLast("encoder",newStringEncoder());
 
       // 客户端的逻辑
       pipeline.addLast("handler",newHelloClientHandler());
    }
}

通道的业务处理程序

客户端的连接通道的业务处理程序与服务器类似,就不再过多累述。

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
 
public classHelloClientHandler extends SimpleChannelInboundHandler<String> {
 
    @Override
    protected voidchannelRead0(ChannelHandlerContext ctx, String msg)throwsException {
       System.out.println("Server say : "+ msg);
    }
   
    @Override
    public voidchannelActive(ChannelHandlerContext ctx) throwsException {
       System.out.println("Client active ");
       super.channelActive(ctx);
    }
   
    @Override
    public voidchannelInactive(ChannelHandlerContext ctx) throwsException {
       System.out.println("Client close ");
       super.channelInactive(ctx);
    }
}

运行结果

客户端

Netty教程系列(一)——netty入门应答程序

服务端

Netty教程系列(一)——netty入门应答程序