Netty入门使用教程
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11827026.html
本文介绍Netty的使用, 结合我本人的一些理解和操作来快速的让初学者入门Netty, 理论知识会有, 但是不会太深入, 够用即可, 仅供入门! 需要想详细的知识可以移步Netty官网查看官方文档!
理论知识 : Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序
当然, 我们这里主要是用Netty来发送消息, 接收消息, 测试一下demo, 更厉害的功能后面再慢慢发掘, 我们先看看这玩意怎么玩, 后面再深入
需要工具和Java类:
netty-4.1.43
netty服务器类 SayHelloServer.java
netty服务端处理器类 SayHelloServerHandler.java
netty客户端类 SayHelloClient.java
netty客户端处理器类 SayHelloClientHandler.java
服务器main方法测试类 MainNettyServer.java
客户端main方法测试类 MainNettyClient.java
首先先来一张演示图, 最下面也会放:
我们看完以下部分就能实现这个东西了!
话不多说, 先贴代码:
package netty.server; 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 netty.handler.SayHelloServerHandler; /**
* sayhello 服务器
*/
public class SayHelloServer { /**
* 端口
*/
private int port ; public SayHelloServer(int port){
this.port = port;
} public void run() throws Exception{
/**
* Netty 负责装领导的事件处理线程池
*/
EventLoopGroup leader = new NioEventLoopGroup();
/**
* Netty 负责装码农的事件处理线程池
*/
EventLoopGroup coder = new NioEventLoopGroup();
try {
/**
* 服务端启动引导器
*/
ServerBootstrap server = new ServerBootstrap(); server
.group(leader, coder)//把事件处理线程池添加进启动引导器
.channel(NioServerSocketChannel.class)//设置通道的建立方式,这里采用Nio的通道方式来建立请求连接
.childHandler(new ChannelInitializer<SocketChannel>() {
//构造一个由通道处理器构成的通道管道流水线
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 此处添加服务端的通道处理器
*/
socketChannel.pipeline().addLast(new SayHelloServerHandler());
}
})
/**
* 用来配置一些channel的参数,配置的参数会被ChannelConfig使用
* BACKLOG用于构造服务端套接字ServerSocket对象,
* 标识当服务器请求处理线程全满时,
* 用于临时存放已完成三次握手的请求的队列的最大长度。
* 如果未设置或所设置的值小于1,Java将使用默认值50
*/
.option(ChannelOption.SO_BACKLOG, 128)
/**
* 是否启用心跳保活机制。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)
* 并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。
*/
.childOption(ChannelOption.SO_KEEPALIVE, true);
/**
* 服务端绑定端口并且开始接收进来的连接请求
*/
ChannelFuture channelFuture = server.bind(port).sync();
/**
* 查看一下操作是不是成功结束了
*/
if (channelFuture.isSuccess()){
//如果没有成功结束就处理一些事情,结束了就执行关闭服务端等操作
System.out.println("服务端启动成功!");
}
/**
* 关闭服务端
*/
channelFuture.channel().closeFuture().sync();
System.out.println("服务端即将关闭!");
} finally {
/**
* 关闭事件处理组
*/
leader.shutdownGracefully();
coder.shutdownGracefully();
System.out.println("服务端已关闭!");
} }
}
package netty.handler; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil; /**
* 服务端入站处理器适配器的继承类
* 用来处理服务端的一些事情
* 根据需要来实现一些方法
*/
public class SayHelloServerHandler extends ChannelInboundHandlerAdapter { @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("直接打印服务端需要处理的信息: " + buf.toString(CharsetUtil.UTF_8));
ByteBuf res = Unpooled.wrappedBuffer(new String("塔台收到!塔台收到!信息如下, 请确认 " + buf.toString(CharsetUtil.UTF_8)).getBytes());
/**
* 给客户端回复消息
*/
ctx.writeAndFlush(res);
} /**
* 连接成功后,自动执行该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务器首次处理!");
/**
* 这种发送的消息格式是错误的!!!!!
* 消息格式必须是ByteBuf才行!!!!!
*/
ctx.writeAndFlush("Hello is server !");
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
/**
* 异常捕获
*/
cause.printStackTrace();
ctx.close();
}
}
package netty.client; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import netty.handler.SayHelloClientHandler; /**
* sayhello 客户端
*/
public class SayHelloClient { private int port;
private String host = "127.0.0.1";
private Channel channel; public SayHelloClient(int port){
this.port = port;
} /**
* 客户端运行方法
* @throws InterruptedException
*/
public void run() throws InterruptedException {
/**
* 负责装客户端的事件处理线程池
*/
EventLoopGroup clientWorker = new NioEventLoopGroup();
try {
/**
* netty客户端引导启动器
*/
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(clientWorker)//把事件处理线程池添加进启动引导器
.channel(NioSocketChannel.class)//设置通道的建立方式,这里采用Nio的通道方式来建立请求连接
//.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
/**
* 此处添加客户端的通道处理器
*/
socketChannel.pipeline().addLast(new SayHelloClientHandler());
}
});
/**
* 客户端绑定端口并且开始发起连接请求
*/
ChannelFuture future = bootstrap.connect(host, port).sync();
if (future.isSuccess()){
System.out.println("客户端连接服务器成功!");
}
/**
* 将通道设置好, 以便外面获取
*/
this.channel = future.channel();
/**
* 关闭客户端
*/
future.channel().closeFuture().sync();
System.out.println("客户端即将关闭!");
} finally {
/**
* 关闭事件处理组
*/
clientWorker.shutdownGracefully();
System.out.println("客户端已关闭!");
}
} public Channel getChannel(){
return this.channel;
}
}
package netty.handler; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import java.nio.charset.Charset;
import java.util.Date; /**
* sayhello 客户端处理器
*/
public class SayHelloClientHandler extends ChannelInboundHandlerAdapter { /**
* 通道信息读取处理
* @param ctx
* @param msg
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg; // 将消息转化成bytebuf
try {
System.out.println("客户端直接打印接收到的消息: " + m.toString(Charset.defaultCharset()));
long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
/**
* 给服务端回复消息
*/
ctx.writeAndFlush("客户端收到! 消息为: " + m.toString(Charset.defaultCharset()));
} finally {
m.release();
}
} /**
* 连接成功后,自动执行该方法
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
/**
* 往服务端发送消息
* 消息格式必须是ByteBuf才行!!!!!
* 如果是其他格式服务端是接收不到的!!!!
*/
String helo = "你好呀!";
ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
ctx.channel().writeAndFlush(byteBuf);
System.out.println("首次连接完成!");
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
package netty; import netty.server.SayHelloServer; /**
* Netty server 使用main类
*/
public class MainNettyServer { /**
* 端口
*/
private static int port = 8686; public static void main(String[] args) throws Exception {
/**
* 启动netty服务器
*/
SayHelloServer sayHelloServer = new SayHelloServer(port);
sayHelloServer.run();
}
}
package netty; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import netty.client.SayHelloClient; import java.util.Scanner; /**
* 客户端main方法类
*/
public class MainNettyClient {
public static void main(String[] args) throws InterruptedException {
/**
* 创建netty客户端
*/
SayHelloClient client = new SayHelloClient(8686);
/**
* 新建一个线程让它单独去跑,我们可以main方法测试一下发送消息和接受消息
*/
Thread clientThread = new Thread(new Runnable() {
@Override
public void run() {
try {
client.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
clientThread.start();
/**
* 如果不新建一个线程去跑客户端的话, 以下的代码就执行不到
* 这里用while是因为客户端的channel并不能立马生成, 会在client启动后一段时间才生成获取到
* 所以需要延迟一点获取channel, 否则channel为null
*/
Channel channel = null;
boolean isStart = false;
while (!isStart) {
if (null != client.getChannel()) {
channel = client.getChannel();
isStart = true;
}
}
String helo = "你好呀!我这里是客户端, 收到请回答";
ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
channel.writeAndFlush(byteBuf);
/**
* 我们通过控制台输入来给服务端发送消息
* 此处只做模拟使用
*/
for (int i = 0; i < 10 ; i++) {
Scanner scanner = new Scanner(System.in);
String text = scanner.nextLine();
channel.writeAndFlush(Unpooled.wrappedBuffer(text.getBytes()));
}
}
}
接下来我们来使用一下看看, 我们用客户端控制台输入消息和服务端"对话": (gif录制软件-->ScreenToGif)
首先我们把项目打包成jar包, 步骤如下:(eclipse的自行百度, 此处用的是IDEA)
保存之后
去输出的文件目录找到jar包即可!
我放在了一起, 然后开始用CMD运行!
运行命令 : java -jar client.jar
开两个CMD演示!
左边是服务器, 右边是客户端, 直接看图!
演示到此结束! over !
Netty入门使用教程的更多相关文章
- Netty入门与实战教程总结分享
前言:都说Netty是Java程序员必须要掌握的一项技能,带着不止要知其然还要知其所以然的目的,在慕课上找了一个学习Netty源码的教程,看了几章后着实有点懵逼.虽然用过Netty,并且在自己的个人网 ...
- Netty入门教程——认识Netty
什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...
- Netty学习_Netty框架入门教程:Netty入门之HelloWorld实现
我们可能都学过Socket通信/io/nio/aio等的编程.如果想把Socket真正的用于实际工作中去,那么还需要不断的完善.扩展和优化.比如很经典的Tcp读包写包问题,或者是数据接收的大小,实际的 ...
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
- Angular2入门系列教程4-服务
上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...
- ASP.NET Aries 入门开发教程7:DataGrid的行操作(主键操作区)
前言: 抓紧勤奋,再接再励,预计共10篇来结束这个系列. 上一篇介绍:ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑 本篇介绍主键操作区相关内容. 1:什么时候有默认的 ...
- ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑
前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...
随机推荐
- centos7 安装python3 小白教程-CV大法
安装python3 centos7,默认系统自带python2.7的版本,这个版本被系统很多程序所依赖,所以不建议删除,如果使用最新的Python3那么我们知道编译安装源码包和系统默认包之间是没有任何 ...
- 在pycharm中创建py文件——创建你的第一个项目
开启编程第一步 创建一个项目 创建项目了xdm,敲黑板了哈 首先打开你的pycharm,点击New Project新建项目 就会进入到配置你这个项目所要用到的环境,这里我们用python列举 在L ...
- PHP 中的回调函数
回调函数就是在主进程执行过程中,满足某个条件后,跳转到预先设置好的函数中去执行的一种函数. 举例说明: 张三到一个水果店买苹果,刚好苹果卖完了,于是张三在店员那里留下了自己的电话,一个小时后店里有货了 ...
- 【白话科普】10s 从零看懂 H5
最近大家有没有发现,很多微信公众号的动态内容变多了?我们常常可以看到,公众号内容中有很多动态图片甚至动画,还可以通过手指滑动切换页面,或者有一些小的点击互动. 这种看起来有点高级的小作品,其实是都是& ...
- CF995E Number Clicker
题目分析 首先,我们必须明白,操作都是互逆的,\(1,2\)之间是可以互相转化的,这是不需证明的,对于操作\(3\),实际上,是求当前数的逆元,我们知道,逆元就是求当前数在模另一个数下的倒数,那么,逆 ...
- antd的table组件设置Column的width列宽度不生效问题
超长连续字段(长数字和长单词) 破坏表格布局的问题(即使你指定了列的宽度也会被挤开),之前组件内默认加过 word-break: break-word; 去纠正此类布局,又会引起其他的问题. 所以最好 ...
- 使用 SourceGenerator 简化 Options 绑定
目录 摘要 Options 绑定 使用 SourceGenerator 简化 如何 Debug SourceGenerator 如何 Format 生成的代码 使用方法 SourceCode & ...
- 使用.NET 6开发TodoList应用(31)——实现基于Github Actions和ACI的CI/CD
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求和目标 在这个系列的最后一节中,我们将使用GitHub Actions将TodoList应用部署到Azure Container ...
- (2)用Micropython将ESP32数据上云
之前我们尝试过直接把LED点亮并且闪烁. 今天尝试一下将LED的开关状态上云,并可以通过云来进行数据下发. 数据要上云,首先开发板要联网. 首先我们会用 Python的network 库, 在netw ...
- 【经验总结】CodeBlocks使用mingw64
CodeBlocks使用 标签:c++ 一.安装并配置mingw-w64 使用中发现CB默认的编译器版本过低,c++11的一些东西无法使用,比如string中的stoi函数,因此尝试安装新版本的编译器 ...