通过入门demo简单了解netty使用方法

这篇文章主要介绍了通过入门demo简单了解netty使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

最近做一个项目:

大概需求: 多个温度传感器不断向java服务发送温度数据,该传感器采用socket发送数据;该数据以$符号开头和结尾,最后将处理的数据存入数据库;

我想到的处理方式:采用netty来接收和处理数据,然后用mybatis将处理后的数据存入数据库;

我在这之前从来没使用过netty,在网上倒是看到不少关于netty的文章,如今就趁着这个项目写一下我所学到的东西和遇到的问题,又是怎么去解决的;

接下来的几篇文章都是围绕着这个项目来写的;本篇主要写netty的入门demo;

正文

代码部分

新建一个maven项目

首先在pom.xml中导入:

 <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>5.0.0.Alpha1</version>
    </dependency>

服务端
1. DiscardServer类,netty的服务端

public class DiscardServer {
  public void run(int port) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    System.out.println("准备运行端口:" + port);
    try {
      ServerBootstrap b = new ServerBootstrap();
      b = b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class)
          .option(ChannelOption.SO_BACKLOG, 128)
          .childHandler(new ChildChannelHandler());
      //绑定端口,同步等待成功
      ChannelFuture f = b.bind(port).sync();
      //等待服务监听端口关闭
      f.channel().closeFuture().sync();
    } finally {
      //退出,释放线程资源
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }
  public static void main(String[] args) throws Exception {
    new DiscardServer().run(8080);
  }
}

2. ChildChannelHandler类:

public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

  protected void initChannel(SocketChannel socketChannel) throws Exception {
    socketChannel.pipeline().addLast(new DiscardServerHandler());
  }
}

3. DiscardServerHandler类

在这里是继承的ChannelHandlerAdapter类,当然还可以继承其他的类,例如SimpleChannelInboundHandler,ChannelInboundHandlerAdapter都可以

public class DiscardServerHandler extends ChannelHandlerAdapter {
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {

    try {
      ByteBuf in = (ByteBuf) msg;
      System.out.println("传输内容是");
      System.out.println(in.toString(CharsetUtil.UTF_8));
      ByteBuf resp= Unpooled.copiedBuffer("收到信息$".getBytes());
      ctx.writeAndFlush(resp);
    } finally {
      ReferenceCountUtil.release(msg);
    }
  }
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    // 出现异常就关闭
    cause.printStackTrace();
    ctx.close();
  }
}

启动netty服务;

好了,到这里就能开始接收数据了;

客服端

1.TimeClient类

public class TimeClient {
  public void connect(int port,String host)throws Exception{
    //配置客户端
    System.out.println(port+"--"+host);
    EventLoopGroup eventLoopGroup=new NioEventLoopGroup();
    try {
      Bootstrap b=new Bootstrap();
      b.group(eventLoopGroup).channel(NioSocketChannel.class)
          .option(ChannelOption.TCP_NODELAY,true)
          .handler(new ChannelInitializer<SocketChannel>() {
            protected void initChannel(SocketChannel socketChannel) throws Exception {
              socketChannel.pipeline().addLast(new TimeClientHandler());
            }
          });
      //绑定端口,同步等待成功
      ChannelFuture f = b.connect(host,port).sync();
      //等待服务监听端口关闭
      f.channel().closeFuture().sync();
    }finally {
      //优雅退出,释放线程资源
      eventLoopGroup.shutdownGracefully();
    }
  }
  public static void main(String[] args) throws Exception {
    new TimeClient().connect(8090,"localhost");
  }
}

2.TimeClientHandler 类

public class TimeClientHandler extends ChannelHandlerAdapter {
  private byte[] req;
  public TimeClientHandler(){
    req="$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$".getBytes();
  }
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ByteBuf message=null;
    for(int i=0;i<100;i++){
      message=Unpooled.buffer(req.length);
      message.writeBytes(req);
      ctx.writeAndFlush(message);
    }
  }
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
      ByteBuf in = (ByteBuf) msg;
      System.out.println(in.toString(CharsetUtil.UTF_8));
    } finally {
      ReferenceCountUtil.release(msg);
    }
  }
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    // 出现异常就关闭
    cause.printStackTrace();
    ctx.close();
  }
}

在channelActive类中向服务端发送100次消息

先启动服务端,再启动客户端;

测试结果一:

服务端:

传输内容是
$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00$$tmb00035ET3318/08/22 11:5704026.7
传输内容是
5,027.31,20.00,20.00$$tmb00035ET3318/08/22 

客户端:

8080--localhost
收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息收到信息

由于内容太多,就不都贴出来了j,直接写结果吧:

客户端发送100次数据,但是服务端只收到了28次,然后服务端向客户端返回28次数据,客户端却只收到一次;

可以发现服务端接收的数据不是完整接收的,这里出现了拆包,粘包的问题

这里就不讨论拆包,粘包了,百度一大堆,相信你也能看明白;

解决粘包,拆包的问题

解决拆包粘包的方法有很多:

  • 消息定长,固定每个消息的固定长度
  • 在消息末尾使用换行符对消息进行分割,或者使用其他特殊字符来对消息进行分割;
  • 将消息分为消息头和消息体,消息头中包含标识消息总长度;
  • 更复杂的,或者其他的协议。

由于我负责的这个项目户端发送是由$开始和结束的数据,返回的数据我也设置的$结束,所以我选择了第二种方法;

只需要在服务端的DiscardServerHandler中和客户端的ChannelInitializer中添加几行相同的代码就行了;

服务端:

public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

  protected void initChannel(SocketChannel socketChannel) throws Exception {
    ByteBuf byteBuf= Unpooled.copiedBuffer("$".getBytes());
    socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,byteBuf));
    socketChannel.pipeline().addLast(new DiscardServerHandler());
  }
}

客户端:

在如下的位置添加如下的代码:

 .handler(new ChannelInitializer<SocketChannel>() {
            protected void initChannel(SocketChannel socketChannel) throws Exception {
              ByteBuf byteBuf= Unpooled.copiedBuffer("$".getBytes());
              socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,byteBuf));
              socketChannel.pipeline().addLast(new TimeClientHandler());
            }
          });

测试结果

这里我就不发送100次数据了,值发送10次:

服务端:

传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00
传输内容是
tmb00035ET3318/08/22 11:5704026.75,027.31,20.00,20.00

客户端:

收到信息
收到信息
收到信息
收到信息
收到信息
收到信息
收到信息
收到信息
收到信息
收到信息

解决我所遇到的问题了;

总结

  • 本来我只需要写服务端的代码的,但是为了更好的演示,所以我写了客户端
  • 本篇文章主要就是使用netty发送和接收数据,还有就是拆包和粘包的问题,当然,netty还可以做其他很多的事情;
  • netty针对对拆包粘包的问题有很多种解决办法:例如可以用LineBasedFrameDecoder和StringDecoder组合将信息已换行符来进行拆分;也可以用我上边的解决方法来解决以特殊字符结束的信息;
  • 在解决拆包粘包信息的时候,注意信息是否符合定义的规则,不然会处理不了数据:例如我上边的例子,如果服务端在返回信息是不以$符结尾的话,客户端是打印不出来信息的,因为客户端会认为服务端还没有发送完信息,会一直等待,而且打印不出数据;
  • 这篇文章只是我入门netty的一个小demo,对我还是很有帮助的,当然也希望对阅读者有那么一点点帮助;
  • 有什么不对的地方还请指正,建议也是多多益善;
  • 源码地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Netty学习教程之基础使用篇

    什么Netty? Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用.Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发. 我们下面编写四个类 1.用于接收数据的服务器端Socket

  • Spring Boot实战之netty-socketio实现简单聊天室(给指定用户推送消息)

    网上好多例子都是群发的,本文实现一对一的发送,给指定客户端进行消息推送 1.本文使用到netty-socketio开源库,以及MySQL,所以首先在pom.xml中添加相应的依赖库 <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version&

  • spring+netty服务器搭建的方法

    游戏一般是长连接,自定义协议,不用http协议,BIO,NIO,AIO这些我就不说了,自己查资料 我现在用spring+netty搭起简单的游戏服 思路:1自定义协议和协议包:2spring+netty整合:3半包粘包处理,心跳机制等:4请求分发(目前自己搞的都是单例模式) 下个是测试用的,结构如下 首先自定义包头 Header.java package com.test.netty.message; /** * Header.java * 自定义协议包头 * @author janehuang

  • Java NIO框架Netty简单使用的示例

    之前写了一篇文章:Java 网络IO编程总结(BIO.NIO.AIO均含完整实例代码),介绍了如何使用Java原生IO支持进行网络编程,本文介绍一种更为简单的方式,即Java NIO框架. Netty是业界最流行的NIO框架之一,具有良好的健壮性.功能.性能.可定制性和可扩展性.同时,它提供的十分简单的API,大大简化了我们的网络编程. 同Java IO介绍的文章一样,本文所展示的例子,实现了一个相同的功能. 1.服务端 Server: package com.anxpp.io.calculat

  • springboot整合netty过程详解

    这篇文章主要介绍了springboot整合netty过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 上一篇讲了netty的一个入门的demo:项目上我也把数据处理做好了,就要开始存数据库了:我用的mybatis框架,如果单独使用还是觉得比较麻烦,所以就用了springboot+mybatis+netty:本篇主要讲netty与springboot的整合,以及我在这个过程中遇到的问题,又是怎么去解决的: 正文 我在做springbo

  • Spring Boot集成netty实现客户端服务端交互示例详解

    前言 Netty 是一个高性能的 NIO 网络框架,本文主要给大家介绍了关于SpringBoot集成netty实现客户端服务端交互的相关内容,下面来一起看看详细的介绍吧 看了好几天的netty实战,慢慢摸索,虽然还没有摸着很多门道,但今天还是把之前想加入到项目里的 一些想法实现了,算是有点信心了吧(讲真netty对初学者还真的不是很友好......) 首先,当然是在SpringBoot项目里添加netty的依赖了,注意不要用netty5的依赖,因为已经废弃了 <!--netty--> <

  • Netty与Spring Boot的整合的实现

    ​ 最近有朋友向我询问一些Netty与SpringBoot整合的相关问题,这里,我就总结了一下基本整合流程,也就是说,这篇文章 ,默认大家是对netty与Spring,SpringMVC的整合是没有什么问题的.现在,就进入正题吧. Server端: 总的来说,服务端还是比较简单的,自己一共写了三个核心类.分别是 NettyServerListener:服务启动监听器 ServerChannelHandlerAdapter:通道适配器,主要用于多线程共享 RequestDispatcher:请求分

  • spring boot整合netty的实现方法

    之前花了几天去研究怎么使用netty做一个网关服务器,虽然最后还是没能用上我做的网关,但是呢netty是会用了,总结一下netty和spring boot整合.感觉不用spring boot都不会写代码了.哈哈哈 在pom文件中添加相关的依赖,这里主要的就是netty的依赖,spring boot的相关依赖本文不提 <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artif

  • 通过入门demo简单了解netty使用方法

    这篇文章主要介绍了通过入门demo简单了解netty使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 最近做一个项目: 大概需求: 多个温度传感器不断向java服务发送温度数据,该传感器采用socket发送数据:该数据以$符号开头和结尾,最后将处理的数据存入数据库: 我想到的处理方式:采用netty来接收和处理数据,然后用mybatis将处理后的数据存入数据库: 我在这之前从来没使用过netty,在网上倒是看到不少关于netty的文

  • JS简单编号生成器实现方法(附demo源码下载)

    本文实例讲述了JS简单编号生成器实现方法.分享给大家供大家参考,具体如下: 运行效果截图如下: 具体代码如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>编号生成器</title> </head> <body&g

  • python实现简单socket通信的方法

    本文实例讲述了python实现简单socket通信的方法.分享给大家供大家参考,具体如下: 刚刚开始接触python,实现了一个helloworld程序---关于udp协议的socket通信demo. 首先服务端这边的实现如下: import socket, traceback host = '' # Bind to all interfaces port = 51500 # Step1: 创建socket对象 s = socket.socket(socket.AF_INET, socket.S

  • struts2入门Demo示例

    本文讲述了struts2入门Demo示例.分享给大家供大家参考.具体如下: 1.新建Web Project, 名称:struts2Demo; 2.建立一个用户库struts2, 包含最少的struts2的最少的6个jar文件; 其实呢, 对于MyEclipse8以上来说, 是不必须的, 因为它直接支持struts2了.不需要另外导包. 3.用Build Path将struts2的库加进来; 4.在web.xml中加入以下配置: <?xml version="1.0" encodi

  • Android使用socket创建简单TCP连接的方法

    本文实例讲述了Android使用socket创建简单TCP连接的方法.分享给大家供大家参考,具体如下: 不管是在Java还是Android编程中,通信都是及其重要的一部分.有连接的socket编程,重要性自然毋庸置疑. 这里以一个简单的demo演示一个最基本的socket编程. 先写服务端.服务端是Java代码.笔者懒得装eclipse等编程软件,就是直接notepad编程,dos运行的.服务端一般是新建一个绑定端口的serversocket,监听客户端请求(死循环监听).当接收到客户端消息时,

  • Spring AOP入门Demo分享

    在阅读本文之前,大家可先行参阅<简单理解Spring之IOC和AOP及代码示例>一文,简单了解下ioc和aop的相关内容.下面进入正题. 本文将会一步一步创建一个最简单的例子,来使用Spring的AOP特性,算是一个Spring AOP的入门Demo.作为一个初学者,运行出这么简单的一个Demo也踩了很多的坑. OOP的问题,AOP的补充 当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力.也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系.例如日志功能.日志代

  • Spring Security入门demo案例

    目录 一.简介 二.入门案例 三.自定义认证逻辑 四.自定义授权逻辑 五.注销登录 六.记住我功能 七.会话管理 一.简介 Spring Security是一个高度自定义的安全框架.利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作.主要包含如下几个重要的内容: 认证(Authentication),系统认为用户是否能登录. 授权(Authorization),系统判断用户是否有权限去做某些事情. 二.入门案例 首先引入必要的

  • php简单复制文件的方法

    本文实例讲述了php简单复制文件的方法.分享给大家供大家参考,具体如下: <?php /** *author:果冻 *qq:52091199 *wyg517.blog.163.com **/ $file = 'image/a1.jpg'; $newfile = 'a/123.jpg'; //必须有写入权限 if (file_exists($file) == false) { die ('文件不在,无法复制'); } $result = copy($file, $newfile); if ($re

  • JS+CSS简单树形菜单实现方法

    本文实例讲述了JS+CSS简单树形菜单实现方法.分享给大家供大家参考.具体如下: 这是一款不错的CSS树形菜单,树状列表,当然不全是CSS实现,部分功能还使用了JavaScript代码进行配合,删减了修饰用的菜单图片,大家用的时候自己图片加进去,IMG标签大部分都预留了出来,稍懂Html语法的朋友很容易就看懂的. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-css-simple-tree-menu-style-codes/ 具体代码如下

  • 一个极为简单的requirejs实现方法

    require和 sea的源码分析,我之前的博客有写过, 今天我想分享的是一个很简单的核心代码(不带注释和空行大概60行), 没有容错判断. require.js require函数实现用一句话概括: 依次加载require的模块,然后监测script的onload事件,判断所有模块加载成功,执行require的callback, 如果只带一个参数且不是数组,就是加载成功后return 模块. //标记已经加载成功的个数 var REQ_TOTAL = 0; //模块导出 window.expo

随机推荐