Netty入门实例-时间服务器

本教程中实现的协议是TIME协议。 它与先前的示例不同,时间服务器只发送包含32位整数的消息,而不接收任何请求,并在消息发送后关闭连接。 在本示例中,您将学习如何构造和发送消息,以及在完成时关闭连接。

因为时间服务器将忽略任何接收到的数据,但是一旦建立连接就发送消息,所以我们不能使用channelRead()方法。而是覆盖channelActive()方法。 以下是代码的实现:

package cn.sxt.netty.time;

public class TimeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(final ChannelHandlerContext ctx) { // (1)
        final ByteBuf time = ctx.alloc().buffer(4); // (2)
        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

        final ChannelFuture f = ctx.writeAndFlush(time); // (3)
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                assert f == future;
                ctx.close();
            }
        }); // (4)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }}


下面我们来看看上面代码的一些解释分析:

  1. 如上所述,当建立连接并准备好生成流量时,将调用channelActive()方法。现在在这个方法中编写一个32位的整数来表示当前的时间。

  2. 要发送新消息,需要分配一个包含消息的新缓冲区。我们要写入一个32位整数,因此需要一个ByteBuf,其容量至少为4个字节。 通过ChannelHandlerContext.alloc()获取当前的ByteBufAllocator并分配一个新的缓冲区。

  3. 像之前一样,编写构造的消息。
    但是,在NIO中发送消息之前,我们是否曾调用java.nio.ByteBuffer.flip()? ByteBuf没有这样的方法,它只有两个指针; 一个用于读取操作,另一个用于写入操作。 当您向ByteBuf写入内容时,写入索引会增加,而读取器索引不会更改。读取器索引和写入器索引分别表示消息的开始和结束位置。
    相比之下,NIO缓冲区不提供一个干净的方式来确定消息内容开始和结束,而不用调用flip方法。当您忘记翻转缓冲区时,就将会遇到麻烦,因为不会发送任何或发送不正确的数据。但是这样的错误不会发生在Netty中,因为不同的操作类型我们有不同的指针。
    另一点要注意的是ChannelHandlerContext.write()(和writeAndFlush())方法返回一个ChannelFutureChannelFuture表示尚未发生的I/O操作。这意味着,任何请求的操作可能尚未执行,因为所有操作在Netty中是异步的。 例如,以下代码可能会在发送消息之前关闭连接:

    Channel ch = ...;
    ch.writeAndFlush(message);
    ch.close();
  4. 因此,需要在ChannelFuture完成后调用close()方法,该方法由write()方法返回,并在写入操作完成时通知其监听器。 请注意,close()也可能不会立即关闭连接,并返回一个ChannelFuture。

当写请求完成时,我们如何得到通知? 这就像向返回的ChannelFuture添加ChannelFutureListener一样简单。 在这里,我们创建了一个新的匿名ChannelFutureListener,当操作完成时关闭Channel

或者,可以使用预定义的侦听器来简化代码:

f.addListener(ChannelFutureListener.CLOSE);


要测试我们的时间服务器是否按预期工作,可以使用UNIX rdate命令:

$ rdate -o <port> -p <host>


其中<port>是在main()方法中指定的端口号,<host>通常是localhost或服务器的IP地址。

编写时间客户端

DISCARDECHO服务器不同,我们需要一个用于TIME协议的客户端,因为我们无法将32位二进制数据转换为日历上的日期。 在本节中,我们讨论如何确保服务器正常工作并学习如何使用Netty编写客户端。

Netty中服务器和客户端之间最大的和唯一的区别是使用了不同的BootstrapChannel实现。 请看看下面的代码:

package cn.sxt.netty.time;

public class TimeClient {
    public static void main(String[] args) throws Exception {
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap(); // (1)
            b.group(workerGroup); // (2)
            b.channel(NioSocketChannel.class); // (3)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });

            // Start the client.
            ChannelFuture f = b.connect(host, port).sync(); // (5)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}


  1. BootstrapServerBootstrap类似,只是它用于非服务器通道,例如客户端或无连接通道。

  2. 如果只指定一个EventLoopGroup,它将同时用作boss组和worker组。boss组和worker组不是用于客户端。

  3. 不使用NioServerSocketChannel,而是使用NioSocketChannel来创建客户端通道。

  4. 注意,这里不像我们使用的ServerBootstrap,所以不使用childOption(),因为客户端SocketChannel没有父类。

  5. 应该调用connect()方法,而不是bind()方法。

如上面所见,它与服务器端代码没有什么不同。 ChannelHandler实现又是怎么样的呢? 它应该从服务器接收一个32位整数,将其转换为人类可读的格式,打印转换为我们熟知的时间格式 ,并关闭连接:

package cn.sxt.netty.time;

import java.util.Date;

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
                ByteBuf m = (ByteBuf) msg; // (1)
        try {
            long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
            Date currentTime = new Date(currentTimeMillis);
            System.out.println("Default Date Format:" + currentTime.toString());

            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = formatter.format(currentTime);
            // 转换一下成中国人的时间格式
            System.out.println("Date Format:" + dateString);

            ctx.close();
        } finally {
            m.release();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}


(1). 在TCP/IP中,Netty读取从对端发送的ByteBuf数据。

客户端看起来很简单,与服务器端示例没什么区别。 但是,这个处理程序有时会拒绝抛出IndexOutOfBoundsException。 我们将在下一节讨论为什么会发生这种情况。

先运行 TimeServer.java 程序,然后再运行 TimeClient.java , 当运行 TimeClient.java时就可以到有一个时间日期输出,然后程序自动退出。输出结果如下 -

Default Date Format:Thu Mar 02 20:50:23 CST 2017
Date Format:2017-03-02 20:50:23



2019-04-20 15:42

相关知识点

开源项目

知识点

相关教程

更多

Netty入门实例-编写服务器端程序

channelRead()处理程序方法实现如下

Netty入门实例-使用POJO代替ByteBuf

使用TIME协议的客户端和服务器示例,让它们使用POJO来代替原来的ByteBuf。

服务器编程

我想问下,我想给自己写的服务器发请求,让他帮我做一些计算,然后再把结果返回给我,属于什么方面的编程啊?然后有什么比较好的框架或者资源介绍一下,想学习下这方面的知识,充点电!

搭建Git服务器-git入门教程

在远程仓库一节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改。 GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。 搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简

利用微信的公众平台监控管理linux服务器[python实例]

利用 微信公众平台 查看管理linux服务器  前沿: 最近懒得做运维自动化了,看到市场部门的同事在调用公众平台的接口,感觉很有意思~就想试试用这个管理简单的管理linux主机,并推送告警信息。 打算这段时间主要再扩展下这个应用~     1 增加一些危险字符的判断,可以自己指定一个转义字符  2 针对post的数据进行加密下 比如 555ifconfig 这个555就是密码,在后端会有判断的,没

web服务器介绍

这些服务器端的程序通常产生一个HTML的响应来让浏览器可以浏览

如何构建一个属于自己私有的git服务器

现在java大学网的项目也是基于git管理,当时建的git服务器是基于gitosis,本教程是基于gitolite构建一个属于自己私有的git服务器

Solr 搭建搜索服务器

Solr已经发布3.5版本了,同时它是基于Lucene 3.5的。我们在基于Solr进行二次开发之前,首先要搭建起一个搜索服务器,在熟悉Solr的基本功能的基础上,可以根据实际应用的需要进行个性化定制开发。因为Solr提供了一种插件机制,我们可以根据自己的需要进行定制,然后在Solr的配置文件中(solrconfig.xml)进行配置即可达到预期的要求。在Solr的发行包中给出了一个配置的例子,我

linux svn服务器搭建

使用yum安装subversion,安装svn服务 yum install subversion 使用--version查看安装是否成功 svnserve --version 创建一个目录,作为svn的版本库 mkdir /opt/svn/repos 创建版本库 svnadmin create /opt/svn/repos 配置svnserve.conf vim /opt/svn/repos/co

solr之服务器搭建步骤

一、前期准备:     JDK1.7(必需)   Tomcat7   solr4.8.0   二、操作步骤:    安装JDK和Tomcat,详细步骤略,可以参考Linux下安装JDK。安装位置分别是/opt/jdk7,/opt/tomcat   解压solr压缩包。(如果没有unzip,需要安装, sudo aptitude install unzip) unzip solr-4.8.0.zip

使用grep和正则来分析Web服务器日志

使用grep和正则来分析Web服务器日志 | 彭琪谈编程               使用grep和正则来分析Web服务器日志         作者: 彭 琪 日期: 2011 年 05 月 14 日       发表评论 (2)      查看评论           前两天因为第三方游戏服务器掉线,导致大量用户同时登录我的服务器,将服务器负载瞬间提高到200+,如此恐怖的数字让我不得不考虑增加

分散处理 Hadoop架构服务器角色分工

  在   Hadoop运算集群架构中,先分解任务,分工处理再汇总结果这些服务器依据用途可分成Master节点和Worker节点,Master负责分配任务,而Worker负责执行任务,如负责分派任务的操作,角色就像是Master节点。         Hadoop架构服务器角色分工      Hadoop运算集群中的服务器依用途分成Master节点和Worker节点。Master节点中安装了Job

ssh框架需要什么样的服务器承载

如题,ssh框架的网站,如果每天有100万IP访问应该需要什么样的服务器呢? 能不能推存下机器配置?和贷款之类的

如何实现两个sqlserver服务器之间互相访问

现在的情况是:  中国有一个数据库服务器,里面有一个数据库A  日本有一个数据库服务器,里面有一个数据库B  A数据库里面的表要用到B数据库里面的数据。  我们该怎么实现阿。愁死我了。  大牛们,帮帮忙阿

修改服务器的 ssh 端口后,git 拉取不到远程服务器的代码

为了安全起见,修改了服务器的ssh端口,使用idea更新git代码时,提示没法访问,解决方案:在当前用户的.ssh目录的config文件中添加ssh端口,如果.ssh目录没有config文件,手动创建一个

最新教程

更多

java线程状态详解(6种)

java线程类为:java.lang.Thread,其实现java.lang.Runnable接口。 线程在运行过程中有6种状态,分别如下: NEW:初始状态,线程被构建,但是还没有调用start()方法 RUNNABLE:运行状态,Java线程将操作系统中的就绪和运行两种状态统称为“运行状态” BLOCK:阻塞状态,表示线程阻塞

redis从库只读设置-redis集群管理

默认情况下redis数据库充当slave角色时是只读的不能进行写操作,如果写入,会提示以下错误:READONLY You can't write against a read only slave.  127.0.0.1:6382> set k3 111  (error) READONLY You can't write against a read only slave. 如果你要开启从库

Netty环境配置

netty是一个java事件驱动的网络通信框架,也就是一个jar包,只要在项目里引用即可。

Netty基于流的传输处理

​在TCP/IP的基于流的传输中,接收的数据被存储到套接字接收缓冲器中。不幸的是,基于流的传输的缓冲器不是分组的队列,而是字节的队列。 这意味着,即使将两个消息作为两个独立的数据包发送,操作系统也不会将它们视为两个消息,而只是一组字节(有点悲剧)。 因此,不能保证读的是您在远程定入的行数据

Netty入门实例-使用POJO代替ByteBuf

使用TIME协议的客户端和服务器示例,让它们使用POJO来代替原来的ByteBuf。

Netty入门实例-编写服务器端程序

channelRead()处理程序方法实现如下

Netty开发环境配置

最新版本的Netty 4.x和JDK 1.6及更高版本

电商平台数据库设计

电商平台数据库表设计:商品分类表、商品信息表、品牌表、商品属性表、商品属性扩展表、规格表、规格扩展表

HttpClient 上传文件

我们使用MultipartEntityBuilder创建一个HttpEntity。 当创建构建器时,添加一个二进制体 - 包含将要上传的文件以及一个文本正文。 接下来,使用RequestBuilder创建一个HTTP请求,并分配先前创建的HttpEntity。

MongoDB常用命令

查看当前使用的数据库    > db    test  切换数据库   > use foobar    switched to db foobar  插入文档    > post={"title":"领悟书生","content":"这是一个分享教程的网站","date":new

快速了解MongoDB【基本概念与体系结构】

什么是MongoDB MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era. MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

windows系统安装MongoDB

安装 下载MongoDB的安装包:mongodb-win32-x86_64-2008plus-ssl-3.2.10-signed.msi,按照提示步骤安装即可。 安装完成后,软件会安装在C:\Program Files\MongoDB 目录中 我们要启动的服务程序就是C:\Program Files\MongoDB\Server\3.2\bin目录下的mongod.exe,为了方便我们每次启动,我

Spring boot整合MyBatis-Plus 之二:增删改查

基于上一篇springboot整合MyBatis-Plus之后,实现简单的增删改查 创建实体类 添加表注解TableName和主键注解TableId import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baom

分布式ID生成器【snowflake雪花算法】

基于snowflake雪花算法分布式ID生成器 snowflake雪花算法分布式ID生成器几大特点: 41bit的时间戳可以支持该算法使用到2082年 10bit的工作机器id可以支持1024台机器 序列号支持1毫秒产生4096个自增序列id 整体上按照时间自增排序 整个分布式系统内不会产生ID碰撞 每秒能够产生26万ID左右 Twitter的 Snowflake分布式ID生成器的JAVA实现方案

Spring boot整合mybatis plus

快速了解mybatis plus 是对Mybatis框架的二次封装和扩展 纯正血统:完全继承原生 Mybatis 的所有特性 最少依赖:仅仅依赖Mybatis以及Mybatis-Spring 性能损耗小:启动即会自动注入基本CURD ,性能无损耗,直接面向对象操作 自动热加载:Mapper对应的xml可以热加载,大大减少重启Web服务器时间,提升开发效率 性能分析:自带Sql性能分析插件,开发测试