原创:转载需注明原创地址 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入门使用教程的更多相关文章

  1. Netty入门与实战教程总结分享

    前言:都说Netty是Java程序员必须要掌握的一项技能,带着不止要知其然还要知其所以然的目的,在慕课上找了一个学习Netty源码的教程,看了几章后着实有点懵逼.虽然用过Netty,并且在自己的个人网 ...

  2. Netty入门教程——认识Netty

    什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...

  3. Netty学习_Netty框架入门教程:Netty入门之HelloWorld实现

    我们可能都学过Socket通信/io/nio/aio等的编程.如果想把Socket真正的用于实际工作中去,那么还需要不断的完善.扩展和优化.比如很经典的Tcp读包写包问题,或者是数据接收的大小,实际的 ...

  4. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  5. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  6. Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数

    上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...

  7. Angular2入门系列教程4-服务

    上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...

  8. ASP.NET Aries 入门开发教程7:DataGrid的行操作(主键操作区)

    前言: 抓紧勤奋,再接再励,预计共10篇来结束这个系列. 上一篇介绍:ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑 本篇介绍主键操作区相关内容. 1:什么时候有默认的 ...

  9. ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑

    前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...

随机推荐

  1. LDAP理解要点

    1.介绍 LDAP(Lightweight Directory Access Protocol)是"轻量级目录访问协议", 是一个用于访问"目录服务器"(Dir ...

  2. SSH公/私秘钥的生成及使用

    如果使用GitHub比较多的朋友,对SSH Key肯定也不陌生,当我们SSH进行代码的pull&push时,往往需要我们配置SSH Key. 如果Linux用的多朋友,肯定对SSH Key都很 ...

  3. 开源实践 | 携程在OceanBase的探索与实践

    写在前面:选型考虑 携程于1999年创立,2016-2018年全面推进应用 MySQL 数据库,前期线上业务.前端技术等以 SQL Server 为主,后期数据库逐步从 SQL Server 转到开源 ...

  4. APP自动化,怎样让应用不重置?

    noReset =True产生的背景: 在编写APP自动化代码时,除了登录用例需要填写账号和密码外,其余很多用例都是需要先登录再操作的,如果每一个用例都从头开始到具体的操作,这样将会耗费很多时间,此时 ...

  5. Pytest_用例分组(6)

    用例分组 pytest进行分组测试的方法是使用装饰器 @pytest.mark.标记名称,被标记为相同名称的用例可以看做为同一个组. 分组用例的运行方式是在执行命令中追加 -m "标记名称& ...

  6. shell中的2>/dev/null

    1.文件描述符Linux系统预留可三个文件描述符:0.1和2,他们的意义如下所示:0--标准输入(stdin)1--标准输出(stdout)2--标准错误(stderr) 标准输出--stdout假设 ...

  7. 痞子衡嵌入式:我入选了2021年度与非网(eefocus)星选创作者Top10

    本周二「与非网」一个美女运营小姐姐加痞子衡微信,告知痞子衡评上了一个奖,让痞子衡把收件地址告诉她,她把证书寄过来. 昨天痞子衡收到了快递,拆开一看,原来是被评上了 与非网 2021 年度创作者,这个证 ...

  8. kafka时间轮的原理(一)

    概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...

  9. 如何使用 GitHub Pages 维护自己的博客

    目录 前置知识 实际操作 声明 本文地址:如何使用 GitHub Pages 维护自己的博客 前置知识 首先,你应该知道如何用 Hexo 在本地搭建一个博客系统,具体见 Hexo. 其次,我们如果想使 ...

  10. covid19数据挖掘与可视化实验

    数据说明: 来源: https://www.kesci.com/mw/project/5e68db4acdf64e002c97b413/dataset (ncov) 日期:从2020年1月21日开始 ...