随笔 - 1357  文章 - 0  评论 - 1104  阅读 - 1941万

Netty入门——客户端与服务端通信

Netty简介
Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发。Netty 已逐渐成为 Java NIO 编程的首选框架。

什么是物联网?
nio通信框架

物联网主要运用到netty哪些特性
a、TCP长连接
b、能够和各种序列化框架完美整合

为什么要使用netty,相对于其他通信框架mina有哪些优点
a、API使用简单,开发门槛低
b、功能强大,预置了多种编解码功能,支持多种主流协议
c、社区活跃,版本更新快
d、技术稳定可靠,如:elasticsearch、spark、dubbo、motan等开源框架底层通信采用的是netty

1、Netty服务端编写
首先,Netty通过ServerBootstrap启动服务端代码,需要四步:
第一步,定义两个线程组,用来处理客户端通道的accept和读写事件
第二步,绑定服务端通道NioServerSocketChannel
第三步,给读写事件的线程通道绑定handle,处理具体的业务逻辑
第四步,绑定监听
1.1、NettyServer——Netty服务端编写

复制代码
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 io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;

/**
 *  Netty服务端编写
 * 
 * @author Administrator
 *
 */
public class NettyServer {
    
    public static void main(String[] args) throws InterruptedException {
        // 首先,netty通过ServerBootstrap启动服务端
        ServerBootstrap server = new ServerBootstrap();
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup =new NioEventLoopGroup();
        //第1步定义两个线程组,用来处理客户端通道的accept和读写事件
        //parentGroup用来处理accept事件,childgroup用来处理通道的读写事件
        //parentGroup获取客户端连接,连接接收到之后再将连接转发给childgroup去处理
        server.group(parentGroup, childGroup);
        
        //用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。
        //用来初始化服务端可连接队列
        //服务端处理客户端连接请求是按顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。
        server.option(ChannelOption.SO_BACKLOG, 128);
        
        //第2步绑定服务端通道
        server.channel(NioServerSocketChannel.class);
        
        //第3步绑定handler,处理读写事件,ChannelInitializer是给通道初始化
        server.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                 //解码器,接收的数据进行解码,一定要加在SimpleServerHandler 的上面
                //maxFrameLength表示这一贞最大的大小 
                //delimiter表示分隔符,我们需要先将分割符写入到ByteBuf中,然后当做参数传入;
                //需要注意的是,netty并没有提供一个DelimiterBasedFrameDecoder对应的编码器实现(笔者没有找到),因此在发送端需要自行编码添加分隔符,如 \r \n分隔符
                ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()[0]));
                 //把传过来的数据 转换成byteBuf
                ch.pipeline().addLast(new SimpleServerHandler());
            }
        });
        
        //第4步绑定8080端口
        ChannelFuture future = server.bind(8080).sync();
        //当通道关闭了,就继续往下走
        future.channel().closeFuture().sync();
    }

}
复制代码


1.2、SimpleServerHandler——处理客户端返回的数据

复制代码
import java.nio.charset.Charset;

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

/**
 * 处理客户端返回的数据
 * 
 * @author Administrator
 *
 */
public class SimpleServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 读取客户端通道的数据
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //可以在这里面写一套类似SpringMVC的框架
        //让SimpleServerHandler不跟任何业务有关,可以封装一套框架
        if(msg instanceof ByteBuf){
            System.out.println(((ByteBuf)msg).toString(Charset.defaultCharset()));
        }
        
        //业务逻辑代码处理框架。。。
        
        //返回给客户端的数据,告诉我已经读到你的数据了
        String result = "hello client ";
        ByteBuf buf = Unpooled.buffer();
        buf.writeBytes(result.getBytes());
        ctx.channel().writeAndFlush(buf);
        
        ByteBuf buf2 = Unpooled.buffer();
        buf2.writeBytes("\r\n".getBytes());
        ctx.channel().writeAndFlush(buf2);
        System.out.println("==========");
    }
 
}
复制代码

 

//使用下面命令测试上面代码。
1.开启上面main方法。
2.执行一下命令
c:>telnet localhost 8080
随便输入,会发现eclipse控制台会监听到输入的内容,有个问题接收时一个字一个字接受,可以让他一行一行接收

2、netty客户端编写
第一步,连接到服务端
第二步,向服务端发送数据
第三步,处理服务端返回的数据
第四步,关闭连接
2.1、NettyClient——Netty客户端编写

复制代码
import com.alibaba.fastjson.JSONObject;
import com.dxfx.user.model.User;

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.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.AttributeKey;

/**
 * Netty客户端编写
 * @author Administrator
 *
 */
public class NettyClient {
    
    public static void main(String[] args) throws InterruptedException {
        // 首先,netty通过ServerBootstrap启动服务端
        Bootstrap client = new Bootstrap();
        
        //第1步 定义线程组,处理读写和链接事件,没有了accept事件
        EventLoopGroup group = new NioEventLoopGroup();
        client.group(group );
        
        //第2步 绑定客户端通道
        client.channel(NioSocketChannel.class);
        
        //第3步 给NIoSocketChannel初始化handler, 处理读写事件
        client.handler(new ChannelInitializer<NioSocketChannel>() {  //通道是NioSocketChannel
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                //字符串编码器,一定要加在SimpleClientHandler 的上面
                ch.pipeline().addLast(new StringEncoder());
                ch.pipeline().addLast(new DelimiterBasedFrameDecoder(   
                        Integer.MAX_VALUE, Delimiters.lineDelimiter()[0])); 
                //找到他的管道 增加他的handler
                ch.pipeline().addLast(new SimpleClientHandler());
            }
        });
        
        //连接服务器
        ChannelFuture future = client.connect("localhost", 8080).sync();
        
        //发送数据给服务器
        User user = new User();
        user.setAge(12);
        user.setId(1);
        user.setName("sssss");
        future.channel().writeAndFlush(JSONObject.toJSONString(user)+"\r\n");
        
        for(int i=0;i<5;i++){
            String msg = "ssss"+i+"\r\n";
            future.channel().writeAndFlush(msg);
        }
        
        //当通道关闭了,就继续往下走
        future.channel().closeFuture().sync();
        
        //接收服务端返回的数据
        AttributeKey<String> key = AttributeKey.valueOf("ServerData");
        Object result = future.channel().attr(key).get();
        System.out.println(result.toString());
    }

}
复制代码

 

2.2、SimpleClientHandler——处理服务端返回的数据

复制代码
import java.nio.charset.Charset;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;

/**
 * 处理服务端返回的数据
 * 
 * @author Administrator
 *
 */
public class SimpleClientHandler extends ChannelInboundHandlerAdapter {
 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            String value = ((ByteBuf) msg).toString(Charset.defaultCharset());
            System.out.println("服务器端返回的数据:" + value);
        }
         
        AttributeKey<String> key = AttributeKey.valueOf("ServerData");
        ctx.channel().attr(key).set("客户端处理完毕");

        //把客户端的通道关闭
        ctx.channel().close();
    }

}
复制代码

 

3、netty服务端输出的信息

复制代码
{"age":12,"id":1,"name":"sssss"}
==========
ssss0
==========
ssss1
==========
ssss2
==========
ssss3
==========
ssss4
==========
复制代码

 

4、netty客户端输出的信息

复制代码
服务器端返回的数据:hello client 
客户端处理完毕
服务器端返回的数据:hello client 
服务器端返回的数据:hello client 
服务器端返回的数据:hello client 
服务器端返回的数据:hello client 
服务器端返回的数据:hello client 
复制代码

 

posted on   Ruthless  阅读(32781)  评论(1编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
历史上的今天:
2017-12-14 Git 的4个阶段的撤销更改
2015-12-14 eclipse配置maven
2011-12-14 四十一、Android Notification通知详解
2011-12-14 四十、Android home键和back键区别
2011-12-14 三十九、Android原理 不需要关闭后台运行程序
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示