Netty入门 - 秒懂
Netty 入门
疯狂创客圈 Java 分布式聊天室【 亿级流量】实战系列之 -入门【 博客园 总入口 】
@
前言:
问题: 我们需要高度优化的协议
现在我们使用通用应用程序或包进行通信。例如,我们经常使用HTTP客户端库从Web服务器检索信息,并通过Web服务调用远程过程调用。然而,通用协议或其实现有时不能很好地扩展。这就像我们不使用通用HTTP服务器来交换大量文件,电子邮件和近实时消息,如财务信息和多人游戏数据。
我们需要的是高度优化的协议实现,专门用于特殊目的。例如,您可能希望实现针对基于AJAX的聊天应用程序,媒体流或大型文件传输进行了优化的HTTP服务器。你甚至可以设计和实施一个全新的协议,这个协议是根据你的需要而定制的。另一个不可避免的情况是当您必须处理旧版专有协议以确保与旧系统的互操作性。在这种情况下重要的是我们能够快速实现该协议,而不会牺牲最终应用程序的稳定性和性能。
方案
Netty项目是为了快速开发可维护的高性能高可扩展性协议服务器和客户端而努力提供异步事件驱动的网络应用程序框架和工具。换句话说,Netty是一个NIO客户端服务器框架,可以快速轻松地开发诸如协议服务器和客户端之类的网络应用程序。它大大简化了网络编程流程,如TCP和UDP套接字服务器开发。
“快速和容易”并不意味着由此产生的应用程序将遭受可维护性或性能问题的困扰。Netty经过精心设计,实现了许多协议,如FTP,SMTP,HTTP以及各种基于二进制和基于文本的传统协议。因此,Netty成功地找到了一种方法来实现轻松的开发,性能,稳定性和灵活性,而无需妥协。
有些用户可能已经找到了声称具有相同优势的其他网络应用程序框架,您可能想问问Netty与他们的区别。答案是它建立的哲学。Netty旨在为您提供API和执行方面最舒适的体验,从第一天开始。这不是有形的东西,但你会意识到,这个哲学将使你的生活更容易,当你阅读本指南和玩Netty的时候。
好了,以上就是关于netty的一个官网的初步介绍。
下面进入搭建最简单的服务器的环节,我这里会按照官网的思路走,不过不会完全一点不差。
好了,我们开始。
建立项目
首先我们需要建立项目,如下图所示:
项目名称是NettyDemo,官网建议使用JDK1.6以上,我这里使用的JDK1.8,然后加入使用maven导入Netty依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
</dependencies>
那么现在我们可以正式开始我们的项目编写了。
编写一个Discard Handler 处理器
编写一个Discard服务器(按我理解就是啥也不干的服务器,别着急反驳,往下看)
世界上最简单的协议不是“hello world”,而是。。。。什么也不做的协议Discard,丢弃的意思,服务端丢弃,那就是啥也不做的协议呗(尝试把协议理解为用户自定义功能)。
想要实现一个Discard协议,那么我们唯一需要做的就是忽略所有接收到的数据。让我们从处理器实现开始,它处理由netty生成的I/O事件。
首先我们创建一个java包:netty_beginner,然后在里面创建一个类DiscardServerHandler
类的内容如下:
package netty_beginner;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
- Created by moon on 2017/4/5.
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // (2)
// super.channelRead(ctx, msg);
((ByteBuf) msg).release(); // (3)
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) {
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception { // (5)
// super.exceptionCaught(ctx, cause);
cause.printStackTrace();
ctx.close();
}
}
DiscardServerHandler 继承自ChannelInboundHandlerAdapter,它是 ChannelInboundHandler的实现。提供可以覆盖的各种事件处理程序方法。现在,只需要扩展ChannelInboundHandlerAdapter即可,而不是自己实现处理程序接口。
在这里,我们重写通道读取channelRead()事件处理方法。每当从客户端收到新数据时,都会使用接收到的消息调用此方法。
在这个例子中,接收到的消息的类型是ByteBuf。
为了实现DISCARD 丢弃的功能,处理程序必须丢弃掉收到的消息。
ByteBuf是一个引用计数对象,必须通过release()方法显式释放。请记住,处理程序有责任释放传递给处理程序的引用计数对象。通常,channelRead()处理方法的实现方式如下:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
}
当由于I / O错误或由于在处理事件时抛出异常而使得Netty抛出异常时,exceptionCaught() 事件将会被Throwable抛出。
在大多数情况下,应该记录捕获到的异常,并在此关闭其关联的通道,虽然这种方法的实现可以根据你想要处理的异常情况而有所不同。例如,您可能希望在关闭连接之前发送带有错误代码的响应消息。
编写一个Discard 服务器
到目前位置一切顺利。我们已经实现了DISCARD服务器的前半部分。现在剩下的是写入使用DiscardServerHandler启动服务器的main()方法。
我们创建另外一个类:DiscardServer,实现 Discard 服务的功能。
如下:
package netty_beginner;
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;
/**
- Created by moon on 2017/4/5.
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
线程组
NioEventLoopGroup 是一个处理I / O操作的多线程事件循环线程组。
Netty为不同类型的传输提供了各种EventLoopGroup实现。在这个例子中,使用两个NioEventLoopGroup线程组。一般的服务器,都使用两个以上的线程组。
第一个,通常称为“Boss”,用于接受客户端的连接。
第二个,通常称为“Worker”,一旦“Boss” 接受客户端的连接,并将接受的连接注册给“Worker”。
“Boss”负责连接的监听,“Worker”负责处理连接的输入和输出。
一个线程组,包含一条或者多条线程。
启动帮助类
ServerBootstrap是一个帮助类,用于设置服务器。可以不用ServerBootstrap直帮助类,直接使用Channel设置服务器。但是,这是一个繁琐的过程,在大多数情况下您不需要这样做。
在这里,我们指定使用NioServerSocketChannel类来实例化一个新的Channel来接受传入的连接。(可以这么理解,每个客户端连接我们服务端,我们都会为他们创建一个channel,那么这个channel对于面向对象的我们来说就是一个类,我们同意对于我们接受到的连接都初始化为:NioServerSocketChannel。
这里指定的处理程序将始终由新接受的Channel进行评估。ChannelInitializer是一个特殊的处理程序,旨在帮助用户配置新的Channel。很可能您想通过添加一些处理程序(如DiscardServerHandler)来配置新Channel的ChannelPipeline来实现您的网络应用程序。随着应用程序的复杂化,您可能会在管道中添加更多的处理程序,并将这个匿名类最终提取到顶级类中。(个人感觉说白了就是想自己实现包含自己处理逻辑的Channel,但是又需要包含一些通用的原有功能,咋办,继承呗,这就是为什么上面的DiscardServerHandler继承netty的类)
您还可以设置特定于Channel实现的参数。我们正在编写一个TCP / IP服务器,因此我们可以设置套接字选项,如tcpNoDelay和keepAlive。请参阅ChannelOption的apidocs和特定的ChannelConfig实现,以获得有关支持的ChannelOptions的概述。
设置Channel 通道的选项
你有没有注意到option()和childOption()?
option()用于配置 服务器连接监听通道 ,也就是 NioServerSocketChannel。
childOption()用于配置每一个客户端连接成功的通道 —— NioSocketChannel。
我们现在准备好了。剩下的是绑定到端口并启动服务器。这里,我们绑定机器中端口 (比如 8080)。如果有多个地址,您现在可以根据需要调用 bind()方法多次(具有不同的绑定地址)。
恭喜,到了现在这个阶段我们已经完成了。下面可以进行尝试,那么在尝试之前,我要说一句,这个例子非常好,就是一点比较费解,即使我开始测试。
步骤是:
- 首先启动服务器的main方法。
- 然后,通过telnet ,本机8080端口发送内容,看看是否成功
测试:发送消息到Discard服务器
我们打开cmd,输入 telnet,进入一个新的窗口:
我们使用 telnet 命令:open localhost 8080 ,开启Discard服务器的telnet连接。
如下图:
如果忘记了命令,可以使用帮助命令。 这个帮助命令为: ?/help
连接成功后,我们可以输入任何内容,比如 hello,在idea控制台会挨个字符输出:
写在最后
至此为止,可以看到,Netty 的开发,其实是容易入门滴。
下一篇: Netty 的 Echo 服务器,比这个例子稍微复杂一点点。
疯狂创客圈 Java 死磕系列
- Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战
- Netty 源码、原理、JAVA NIO 原理
- Java 面试题 一网打尽
- 疯狂创客圈 【 博客园 总入口 】
Netty入门 - 秒懂的更多相关文章
- Netty入门之客户端与服务端通信(二)
Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...
- Netty入门之HelloWorld
Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ...
- Netty入门
一.NIO Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,在jdk1.4 里提供的新api,他的他的特性 ...
- netty入门(一)
1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ...
- Netty入门(三)之web服务器
Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ...
- Netty入门(二)之PC聊天室
参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...
- Netty入门(一)之webSocket聊天室
一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...
- netty同时做http和websocket(netty入门)
---恢复内容开始--- http://www.jianshu.com/p/5c29c6c6d28c ---恢复内容结束--- http://www.jianshu.com/p/5c29c6c6d28 ...
- Netty入门教程——认识Netty
什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...
随机推荐
- Codeforces Round #451 (Div. 2) B. Proper Nutrition【枚举/扩展欧几里得/给你n问有没有两个非负整数x,y满足x·a + y·b = n】
B. Proper Nutrition time limit per test 1 second memory limit per test 256 megabytes input standard ...
- Artix : Arch拥抱OpenRC 使用笔记
轻量桌面Archlinux用户逃离systemd,拥抱Gentoo的openrc. 镜像源:官方镜像源非常慢,曾经一度体验artix后就放弃了,后来发现了清华和腾讯云的镜像,速度非常快,现在又重新安装 ...
- windows线程yield以及Sleep(0)和SwitchToThread之间的区别
C++的自定义线程函数内调用了一个自定义的yield()接口. 在windows上是调用了SwitchToThread来实现的,linux是pthread_yield实现的. Sleep(0):时间片 ...
- TOYS-POJ2318
本题主要是确定给定的点在那块区域.原题给出n条直线,将长方形分为n+1快区域.我们可以对每个给定的点来判断它在那块区域,判段方法可以根据点与直线的位置关系,具体如下,对于点(x0,y0)和直线ax+b ...
- mysql忘记密码的解决办法
mysql忘记密码时,需要重设密码. 在Windows下的操作如下: 1.关闭正在运行的MySQL. 2.打开DOS窗口,转到mysql\bin目录. 3.输入mysqld --skip-grant- ...
- TensorFlow笔记三:从Minist数据集出发 两种经典训练方法
Minist数据集:MNIST_data 包含四个数据文件 一.方法一:经典方法 tf.matmul(X,w)+b import tensorflow as tf import numpy as np ...
- 【Salvation】——人物角色动画实现
写在前面:这个角色动画主要使用JavaScript编写脚本,在Unity3D游戏引擎的环境中实现. 一.显示角色并实现镜像效果 1.显示贴图: create→cube→修改名称为player,位置归0 ...
- 2016.6.20 eclipse中maven的配置
前期准备: 安装maven,配置maven的环境变量,并且通过mvn -v验证安装成功. 网上的教程说,需要在线或者离线安装maven integration for eclipse插件. 但是我 ...
- 根据DatabaseMetaData确定数据库类型
根据DatabaseMetaData确定数据库类型 DataSource dataSource = dataSourceTransactionManager.getDataSource(); conn ...
- C#读取资源文件的两种方法及保存资源文件到本地
方法1 GetManifestResourceStream VB.NET中资源的名称为:项目默认命名空间.资源文件名 C#中则是:项目命名空间.资源文件所在文件夹名.资源文件名 例如:istr = ...