Netty框架(一)组件介绍及实例实现长连接

一、Netty组件介绍

1. Channel (Socket)、EventLoop(控制流、多线程、并发)、ChannelFuture(异步通知)

(1)Channel 接口
基本的I/O操作(bind()、connect()、read()、write())依赖于底层网络传输所提供的原语,在基于Java的网络编程中其基本的构造函数是Socket。

(2)EventLoop接口
EventLoop定义了Netty的核心抽象,用于处理连接生命周期中所发生的事件。

(3)ChannelFuture接口
Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,一边在某个操作完成时得到异步通知。

2. ChannelHandler(逻辑事件处理容器)、ChannelPipeline(容器链)

(1)ChannelHandler接口
ChannelHannder是Netty的主要组件,它是处理入站到出站数据的应用程序逻辑的容器(相对服务器,客户端数据进入服务器称为入站,服务器处理完后返回数据给客户端称为出站),ChannelInboundHandlerAdapter是他其中的一个重要的实现方法。该方法可以处理信息的读写,客户端连接状态监听等功能。

(2)ChannelPipeline 接口
ChannelPipeline 提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的API。当Channel被创建时,它会被自动分配到它专属的ChannelPipeline。

3.Bootstrap(引导)

(1)Netty的引导类Bootstrap为应用程序的网络层配置提供了容器,这涉及将一个进程绑定到某个指定端口,或者将一个进程连接到另一个运行在指定主机的指定端口上的进程。引导分为两种,服务器端(ServerBootstrap)和客服端(Bootstrap)。

二、Netty实战实现长连接

添加maven依赖

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

1.服务器端代码实现

(1)EchoServerHandler

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component;

import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

@Component
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    //以客户端ip+端口为键,对应的通道为值,存在map中。便于主动向客户端发信息
    public static Map<String, Channel> map=new HashMap<>();


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //当有客户端连上时调用该方法
        String client= String.valueOf(ctx.channel().remoteAddress());
        Channel channel=ctx.channel();
        map.put(client,channel);
        System.out.println("client:"+client);
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //当服务器端收到客户端消息时调用该方法
        ByteBuf in=(ByteBuf)msg;
        String message=in.toString(Charset.forName("UTF-8"));
        System.out.println("服务器收到消息:"+message);



    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //当客户端发生异常时调用该方法
        map.remove(ctx.channel().remoteAddress());
        System.out.println("host:"+ctx.channel().remoteAddress()+"链接断开");
    }

}

(2)EchoServer

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;

import java.net.InetSocketAddress;


public class EchoServer {
    private final int port;


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

    public void start() throws InterruptedException {
        EchoServerHandler serverHandler=new EchoServerHandler();
        EventLoopGroup group=new NioEventLoopGroup();
        try{
            ServerBootstrap bootstrap=new ServerBootstrap();
            bootstrap.group(group).channel(NioServerSocketChannel.class)
            		//设置长连接
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture future=bootstrap.bind().sync();

            future.channel().closeFuture().sync();

        }
        finally {
            group.shutdownGracefully().sync();
        }
    }

}

(3)EchoController

import com.zzx.netty_server.core.EchoServerHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.Charset;

@RestController
public class EchoController {

    @Autowired
    EchoServerHandler handler;
    @GetMapping("/send")
    public String send(String host,String message){
        ByteBuf byteBuffer= Unpooled.copiedBuffer(message, Charset.forName("UTF-8"));
        EchoServerHandler.map.get(host).writeAndFlush(byteBuffer.duplicate());
        return "发送成功!";
    }
}

2.客户端代码实现

(1)EchoClientHandler

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;


@Component
public class EchoClientHandler extends SimpleChannelInboundHandler {
    public static List<Channel> list=new ArrayList<>();


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        ByteBuf in=(ByteBuf)o;
        String message=in.toString(Charset.forName("UTF-8"));
        System.out.println("客户端收到消息:"+message);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        list.add(ctx.channel());
        String s="你好!";
        ByteBuf byteBuffer= Unpooled.copiedBuffer(s, Charset.forName("UTF-8"));
        ctx.writeAndFlush(byteBuffer.duplicate());
        System.out.println("adrss:"+ctx.channel().remoteAddress());

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("服务器:"+ctx.channel().remoteAddress()+"断开链接");
    }
}

(2)EvhoClient

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;

import java.util.concurrent.Future;

public class EvhoClient {

    private final String host;
    private final int port;

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

    public void start() throws InterruptedException {
        EchoClientHandler handler=new EchoClientHandler();
        EventLoopGroup group=new NioEventLoopGroup();
        try {
        Bootstrap bootstrap=new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(host,port)
                .handler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(handler);
                    }
                });
        ChannelFuture future=bootstrap.connect().sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully().sync();
        }

    }

}

(3)ClientController

import com.zzx.netty_client.core.EchoClientHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.Charset;

@RestController
public class ClientController {

    @Autowired
    EchoClientHandler handler;

    @GetMapping("/send")
    public String send(String message){
        ByteBuf byteBuffer= Unpooled.copiedBuffer(message, Charset.forName("UTF-8"));
        EchoClientHandler.list.get(0).writeAndFlush(byteBuffer.duplicate());

        return "success";
    }
}

三、测试

1.启动服务器端和客服端

分别在8123和8234端口启动应用
在这里插入图片描述

2.测试客服端向服务器端发信息

浏览器输入:http://localhost:8123/send?message=啦啦啦啦啦啦
在这里插入图片描述

3.测试服务器端向客户端发信息

localhost:8234/send?host=/192.168.0.103:59280&message=啊啊啊啊啊啊
在这里插入图片描述

发布了55 篇原创文章 · 获赞 23 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Z_Vivian/article/details/95061859
今日推荐