netty做服务端监听多个端口推送数据并区分接收每个端口响应回来的数据

netty 做服务端监听多个端口推送数据并区分接收每个端口响应回来的数据:

@Sharable:被注解的ChannelHandle对应的同意实例可以被加入到一个或者多个ChannelPipelines一次或者多次,而不存在竞争条件。
* 如果在创建服务端后 clien重连服务端 报错 is not a@Sharable hanler   解决方法: 往Handler类上加注解即可。 
	参考:https://blog.csdn.net/haoziwlh/article/details/77684938

笔记分了三种:
1.0监听单端口,并实现在网页上动态开启或关闭(给线程测试连接)
2.0监听多端口,由于站点较多、采集时间也在三秒内,加了闭锁无法保证效率
3.0使用广播模式,统一向连接上的端口发信息,接收并处理每个端口对应站点的值

1.0:
在网页上改端口,netty服务端只监听输入的端口。
在有客户端连接上时,就将与客户端连接的ChannelHandlerContext 存到缓存集合中,当在网页上输入要更改的端口时,
控制层收到传送过来的值后,遍历缓存集合,逐个关闭channel 与两个WordGroup 。
注意: channelHandlerContext.channel().close(); //关闭端口
channelHandlerContext.close()则是关闭当前的端口的管道,但是端口还在监听。

 System.out.println("修改的端口:" + port);
            Map<String, ChannelHandlerContext> channelMap = TCPServer.channelMap;
            for (String key : channelMap.keySet()) {
                ChannelHandlerContext channelHandlerContext = channelMap.get(key);
                channelHandlerContext.channel().close(); //关闭
                //也要关闭两个wordGroup !!! 这里没贴  关一个都没用
            }
            try {
                new TCPServer().bind(Integer.parseInt(port));
                return Result.Success("绑定端口"+port+"成功*****************");
            } catch (Exception e) {
                e.printStackTrace();
                return new Result("绑定端口失败");
            }

2.0:
开启多个服务端口,监听端口,当有客户端连接上时,定时向这些连接上的管道发送数据,并等待回应(一发一回)
思路1. 使用BootStrap 绑定多个端口,当有客户端连接就将连接的ChannelHandlerContext存到缓存集合
但是这样存在两个问题 1).1无法判断发送回来的信息是通过哪个端口发送的。
2).要在几秒内发送信息并等待回应,需要一一遍历缓存集合,逐个发送并等待回应。 而客户端有三十来个 一次读完大概要30秒左右。
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;

/**
 * 测试多端口监听服务端
 * @author peng
 */
@Component
public class Server {

    private ChannelFuture [] ChannelFutures = null;
    private int end;
    private int start;

    public Server(){}
    public Server(int start,int end){
        this.start = start;
        this.end = end;
    }
    public static void main(String[] args){
        Server sever = new Server(20001,20032);
        sever.start();
    }

    private void start() {

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        serverBootstrap.group(bossGroup,workerGroup);
        serverBootstrap.channel(NioServerSocketChannel.class);
        serverBootstrap.childOption(ChannelOption.SO_REUSEADDR,true);

        serverBootstrap.childHandler(new ChannelInitializer() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new CountHandler());
            }
        });

        if (ChannelFutures == null){
            ChannelFutures = new ChannelFuture[end - start +1];
        }
        //多端口绑定
        for (int i = start;i <= end ;i++){
            final int port = i;
            ChannelFuture channelFuture = serverBootstrap.bind(port);
            ChannelFutures[i-start] = channelFuture;
            channelFuture.addListener(future -> {
                if (future.isSuccess()){
                    System.out.println("start success");
                }else{
                    System.out.println("start failed");
                }
            });
        }
    }



    @ChannelHandler.Sharable
    class CountHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            InetSocketAddress inteSocket = (InetSocketAddress) ctx.channel().remoteAddress();
            System.out.println("端口 已有客户端连接***"+inteSocket.getPort());
            super.channelActive(ctx);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            super.channelRead(ctx, msg);
            System.out.println("接受到.."+msg);
        }
    }
}

3.0 使用广播模式:
ChannelGroup可以管理当前的channel,当有连接上时就将连接上的channel组添加到ChannelGroup里:
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

 /**
     * 客户端连接上
     * @param ctx
     * @throws Exception
     */
 
  @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        FxmelsecApplication.channelGroup.add(ctx.channel());  // 将这个Channel添加到ChannelGroup 
        String s = analyLocalAddress(ctx.channel().localAddress());
        Global.channelMap.put(s, ctx);

        super.channelActive(ctx);
//连接上信息存数据库
        RecordEntity re = new RecordEntity();
        re.setState(1);
        re.setContent("连接成功");
        re.setSitesNum(s);
        re.setDate(DateUtils.getDate());
        writeSQLInConnectionOperation(re);
        System.out.println(recordServiceImpl);
        ctx.fireChannelActive(); 
    }
连接断开:
	    /***
     * 客户端离线
     * 判断管道id  删除
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Map<String, ChannelHandlerContext> channelMap = Global.channelMap;
        for (String key : channelMap.keySet()
        ) {
            if (channelMap.get(key) == ctx){
                Global.channelMap.remove(key);
                //将断开连接的写入数据库 在页面开一个框记录连接跟断开
                RecordEntity re = new RecordEntity();
                re.setState(0);
                re.setContent("断开连接");
                re.setSitesNum(key);
                re.setDate(DateUtils.getDate());
                super.channelInactive(ctx);
                writeSQLInConnectionOperation(re);
                return;
            }
        }
    }


Server:
	    public void bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        // 服务器辅助启动类配置
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                //设置nio的双向通道
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ServerChannelInitializer())
                // 设置tcp缓冲区
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        // 绑定端口 同步等待绑定成功
        ChannelFuture f = b.bind(port);
        f.addListener(future -> {
            if (future.isSuccess()) {
                System.out.printf("开启端口%s成功", port);
            } else {
                System.out.printf("开启端口%s失败", port);
            }
        });

    }

    /**
     * 网络事件处理器
     */
    private class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) {
            // 添加自定义协议的编解码工具
            ch.pipeline().addLast("decoder", new StringDecoder());
            ch.pipeline().addLast("encoder", new StringEncoder());
            // 处理网络IO
            ch.pipeline().addLast(new ServerHandler());
        }
    }
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Netty 可以在一个服务端开启多个端口,可以通过创建多个 `ServerBootstrap` 对象来实现。具体步骤如下: 1. 创建两个 `EventLoopGroup` 对象,一个用于接收客户端连接,一个用于处理客户端请求。 ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); // 接收客户端连接 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理客户端请求 ``` 2. 创建两个 `ServerBootstrap` 对象,分别绑定不同的端口,并设置相应的 `ChannelInitializer`。 ```java // 创建第一个 ServerBootstrap 对象 ServerBootstrap bootstrap1 = new ServerBootstrap(); bootstrap1.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(8080)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加 ChannelHandler 处理器 ch.pipeline().addLast(new MyChannelHandler1()); } }); // 创建第二个 ServerBootstrap 对象 ServerBootstrap bootstrap2 = new ServerBootstrap(); bootstrap2.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(8081)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加 ChannelHandler 处理器 ch.pipeline().addLast(new MyChannelHandler2()); } }); ``` 在这里,我们分别创建了两个 `ServerBootstrap` 对象,并分别绑定了 8080 和 8081 端口。同时,我们也设置了相应的 `ChannelInitializer`,来对客户端的请求进行处理。 3. 启动两个 `ServerBootstrap` 对象。 ```java ChannelFuture f1 = bootstrap1.bind().sync(); ChannelFuture f2 = bootstrap2.bind().sync(); ``` 在这里,我们分别启动了两个 `ServerBootstrap` 对象,这时候,这两个服务端就可以分别监听 8080 和 8081 端口了。 4. 关闭服务端。 ```java f1.channel().closeFuture().sync(); f2.channel().closeFuture().sync(); ``` 在这里,我们通过等待两个 `ChannelFuture` 对象的关闭来关闭服务端。完整的示例代码如下: ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); // 接收客户端连接 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理客户端请求 // 创建第一个 ServerBootstrap 对象 ServerBootstrap bootstrap1 = new ServerBootstrap(); bootstrap1.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(8080)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加 ChannelHandler 处理器 ch.pipeline().addLast(new MyChannelHandler1()); } }); // 创建第二个 ServerBootstrap 对象 ServerBootstrap bootstrap2 = new ServerBootstrap(); bootstrap2.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(8081)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加 ChannelHandler 处理器 ch.pipeline().addLast(new MyChannelHandler2()); } }); // 启动两个 ServerBootstrap 对象 ChannelFuture f1 = bootstrap1.bind().sync(); ChannelFuture f2 = bootstrap2.bind().sync(); // 关闭服务端 f1.channel().closeFuture().sync(); f2.channel().closeFuture().sync(); // 关闭 EventLoopGroup bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值