丢弃服务器

详细代码:test-netty4-discard

丢弃服务器,就是将收到的所有数据都丢掉,不做任何处理

DiscardServerHandler

package org.zln.test.netty4.discard;

@Slf4j
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//丢弃收到的数据
((ByteBuf) msg).release();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
// 出现异常时关闭连接
ctx.close();
}
}

DiscardServerHandler继承自ChannelInboundHandlerAdapter,

ChannelInboundHandlerAdapter实现了ChannelInboundHandler接口。

ChannelInboundHandler提供了很多事件处理方法

  • channelRead

当服务端收到新数据的时候,channelRead方法被调用

收到的消息的类型是 ByteBuf,它是一个引用计数对象,必须显示调用release方法来释放。

一般在channelRead中处理的代码形如


@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
// Do something with msg
} finally {
ReferenceCountUtil.release(msg);
}
}
  • exceptionCaught

当服务端产生Throwable对象后,就会调用exceptionCaught

在大部分情况下,捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。

然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,

比如你可能想在关闭连接之前发送一个错误码的响应消息。

DiscardServer

package org.zln.test.netty4.discard;

public class DiscardServer {
private int port; public DiscardServer(int port) {
this.port = port;
} public static void main(String[] args) {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new DiscardServer(port).run();
} private void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel)
throws Exception {
socketChannel.pipeline()
.addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}

NioEventLoopGroup:用于处理I/O操作的多线程事件循环器

bossGroup:接收客户端连接

workerGroup:处理已经接收到的连接

​ 一旦boss接收到连接,就会把连接信息注册到worker中

ServerBootstrap:用于启动NIO服务的辅助类

测试

telnet localhost 8080

目前在DiscardServerHandler上,是直接丢弃的,没有打印出来,所以我们现在加一个打印

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println(in.toString(CharsetUtil.UTF_8));
}

在toString中已经做了release动作,所以不需要再次手工释放资源

详细代码:test-netty4-discard-demo2

应答服务器

详细代码:test-netty4-ask

将客户端的请求消息原样返回

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write(msg);//写入后,内部已经实现了消息资源的释放
ctx.flush();
}

或者

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.writeAndFlush(msg);//写入后,内部已经实现了消息资源的释放
}

时间服务器

时间服务器

在与客户端建立连接时,就发送时间消息

TimeServerHandler

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final ByteBuf time = ctx.alloc().buffer(4);
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time);
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
assert f == future;
ctx.close();
}
});
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
// 出现异常时关闭连接
ctx.close();
}
}

final ByteBuf time = ctx.alloc().buffer(4);

分配指定大小的缓冲

因为要写入32位蒸熟,所以分配4个字节大小的缓冲区

为什么不需要flip操作?

传统NIO缓冲区,因为只有一个位置索引,所以在写完后,如果想要读区,需要执行一次flip操作,将位置指针设置到头部。

Netty提供的ByteBuf缓冲区对象,有读写两个指针,执行写的时候只是写的指针索引增加,读指针位置索引不变,所以不需要flip操作。

ChannelFuture

表示一个还未发送的I/O事件

对ChannelFuture添加监听,可以得知当前I/O操作的具体执行情况

addListener

添加一个事件监听。

如果直接ctx.close();的话,由于writeAndFlush是异步的,会出现还没写完连接就被关闭的情况。

所以要在监听到写完成的事件后再执行close操作

ctx.close()

连接关闭也不是立马生效的,其也是返回一个ChannelFuture对象

另一种简单的监听写法为

    @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final ByteBuf time = ctx.alloc().buffer(4);
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time);
f.addListener(ChannelFutureListener.CLOSE);
}

内置的ChannelFutureListener.CLOSE其实和我们自己对ChannelFutureListener内容是完全一样的

TimeClient

public class TimeClient {

    private static final String HOST = "localhost";
private static final int PORT = 8080; public static void main(String[] args) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
ChannelFuture f = b.connect(HOST, PORT).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
}
  • 比较客户端与服务的启动类的若干不同

1、只有worker没有boss

2、使用NioSocketChannel,不是NioServerSocketChannel

3、不需要childOption。因为客户端的SocketChannel没有父类

4、使用connect,不是bind

TimeClientHandler

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf m = (ByteBuf) msg;
try {
long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
} finally {
m.release();
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
// 出现异常时关闭连接
ctx.close();
}
}

客户端的Handler就比较简单了,就不细说了

整个流程是这样子的

1、客户端发起连接

2、服务端channelActive监听到连接,发送时间数据

3、客户端channelRead接收到服务端请求,打印数据

漏洞

这里其实是有一个漏洞的,就是我们其实是希望一条完整的消息大小为4个字节,可实际情况是不一定的。

也就是会函数TCP/IP协议的粘包与拆包的问题

使用对象传递日期

完整代码

我们需要处理的数据,往往是有一个结构的,一般会封装到一个对象中,

而数据在网络中实际传输的时候,肯定是以字节的形式的。

如果我们在代码上想要直接处理对象,那么就需要编写解码器和编码器。

编码器:将发送的对象转化为字节

解码器:将收到的字节转化为对象

  • UnixTime
public class UnixTime {
private final long value; public UnixTime() {
this(System.currentTimeMillis() / 1000L + 2208988800L);
} public UnixTime(long value) {
this.value = value;
} public long value() {
return value;
} @Override
public String toString() {
return new Date((value() - 2208988800L) * 1000L).toString();
} }

我们把时间戳封装在UnixTime对象中

  • TimeDecoder:解码器
public class TimeDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 4) {
return;
}
out.add(new UnixTime(in.readUnsignedInt()));
}
}
  • TimeEncoder:编码器
public class TimeEncoder extends MessageToByteEncoder<UnixTime> {

    @Override
protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out)
throws Exception {
out.writeInt((int) msg.value());
}
}
  • TimeServerHandler

使用了编码器后,我们可以直接发送对象

    @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ChannelFuture f = ctx.writeAndFlush(new UnixTime());
f.addListener(ChannelFutureListener.CLOSE);
}
  • TimeClientHandler

使用了解码器后,直接转化成对象

    @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
UnixTime m = (UnixTime) msg;
System.out.println("收到:" + m);
ctx.close();
}
  • 配置

编码器和解码器编写完后,需要配置到启动类中

服务端配置编码器:socketChannel.pipeline().addLast(new TimeEncoder(),new TimeServerHandler());

客户端配置解码器:ch.pipeline().addLast(new TimeDecoder(),new TimeClientHandler());

聊天应用

详细代码

ChatServerHandler

@Slf4j
public class ChatServerHandler extends SimpleChannelInboundHandler<String> { public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
}
channels.add(ctx.channel());
} @Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
channels.remove(ctx.channel());
} @Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
if (channel != incoming) {
channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + msg + "\n");
} else {
channel.writeAndFlush("[响应]" + msg + "\n");
}
}
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println("ChatClient:" + incoming.remoteAddress() + "在线");
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println("ChatClient:" + incoming.remoteAddress() + "掉线");
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
Channel incoming = ctx.channel();
System.out.println("ChatClient:" + incoming.remoteAddress() + "异常"); // 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
} }

ChatServerInitializer

public class ChatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ChatServerHandler());
System.out.println("ChatClient:" + ch.remoteAddress() + "连接上");
}
}

ChatServer

public class ChatServer {
private int port; public ChatServer(int port) {
this.port = port;
} public static void main(String[] args) {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new ChatServer(port).run();
} private void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChatServerInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); System.out.println("服务端启动完成"); ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}

ChatClientHandler

public class ChatClientHandler extends SimpleChannelInboundHandler<String> {

    @Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
// 出现异常时关闭连接
ctx.close();
}
}

ChatClientInitializer

public class ChatClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ChatClientHandler());
}
}

ChatClient

public class ChatClient {

    private static final String HOST = "localhost";
private static final int PORT = 8080; public static void main(String[] args) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChatClientInitializer());
ChannelFuture f = b.connect(HOST, PORT).sync();
Channel channel = f.channel();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("请输入:");
String line = bufferedReader.readLine();
if (line.startsWith("exit")) {
System.out.println("结束聊天");
break;
}
channel.writeAndFlush(line + "\r\n");
}
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
}

Netty简单使用的更多相关文章

  1. Netty——简单创建服务器、客户端通讯

    Netty 是一个基于NIO的客户.服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用.Netty相当简化和流线化了网络应用的编程开发过程 ...

  2. Netty简单的HTTP服务器

    本文转载自:https://www.cnblogs.com/luangeng/p/7875710.html HTTP协议:略 基于Netty的HTTP协议栈可以方便的进行异步非阻塞的HTTP服务器的开 ...

  3. Netty简单的重连机制

    其实重连机制并不是多么多高深的技术,其实就是一个在客户端做一个简单的判断,如果连接断了,那么就重新调用连接服务端的代码 当然,我们重连的动作肯定是发生在断连之后发生的,我们可以在上篇的心跳机制的基础上 ...

  4. Netty简单介绍(非原创)

    文章大纲 一.Netty基础介绍二.Netty代码实战三.项目源码下载四.参考文章   一.Netty基础介绍 1. 简介 官方定义为:”Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地 ...

  5. 【Netty整理01-快速入门】Netty简单使用Demo(已验证)

    多处摘抄或手打,为了十积分厚着脸皮标为原创,惭愧惭愧~本篇文章用于快速入门搭建一个简单的netty 应用,如想稍微深入系统的了解,请参照本人下一篇博客,链接: 参考地址: 官方文档:http://ne ...

  6. JAVA netty 简单使用

    实现一个功能,客户端和服务器 轮流对一个数加+1 服务器 public class Server { public static void main(String[] args) { NioEvent ...

  7. Netty简单认识

    简介 Netty 是由JBOSS提供的一个 Java开源框架, 现在是 Github上的开源项目 Netty 是一个异步的.基于事件驱动的网络应用框架式, 用以快速开发高性能.高可靠性的网路IO程序 ...

  8. netty简单样例

    package com.example.demohystrix.process; import io.netty.bootstrap.ServerBootstrap; import io.netty. ...

  9. Netty简单聊天室

    1.创建maven项目,在pom.xml中引入netty的jar包 <project xmlns="http://maven.apache.org/POM/4.0.0" xm ...

随机推荐

  1. CSS3设置背景图片的大小

    设置背景图片的大小,以长度值或百分比显示,还可以通过cover和contain来对图片进行伸缩. background-size 语法详解: 要在插入图片之后进行设置背景图片的大小 backgroun ...

  2. js,indexOf()、lastIndexOf()

    indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置. 提示和注释 注释:indexOf() 方法对大小写敏感! 注释:如果要检索的字符串值没有出现,则该方法返回 -1. 实例 在 ...

  3. react-native初体验(1) — hello world

    没有简介,直接开始干活吧. 默认阅读本文的你已经安装好 nodejs, windows用户需要升级yarn到最新版本. 并且设置安装源为国内的淘宝源: npm config set registry ...

  4. 忘记本地MySQL数据库密码的解决方案。

    忘记本地MySQL数据库密码,解决方案,分以下10个步骤: 参考链接:                  https://blog.csdn.net/weidong_y/article/details ...

  5. Linux读书笔记第一、二章

    第一章    Linux内核简介 1.1Unix历史 Unix特点:1.很简洁 2.所有东西都被当成文件对待 3.Unix内核和相关的系统工具软件都是用C语言编写而成 4.进程创建非常迅速 1.2追寻 ...

  6. c# combobox向上展开

    1.问题情境:实际中的下拉框默认向下扩展,如果屏幕下方空间不足,会向上扩展. 向下扩展情况下,有时候会超出form窗体. 2.解决办法: 寻找相关属性无果. 退而求其次,重画item的框.发现Draw ...

  7. beta冲刺(7/7)

    目录 组员情况 组员1:胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:恺琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:何宇恒 组员11:刘一好 展示组内最新 ...

  8. Java第二天——标识符命名规则、Java的知识、快捷键的使用、Scanner获取值的常用方法

    1.标识符命名规则 字母.下划线.数字.美元符号($)由这四个部分组成. 标识符=首字母+其他 首字母:字母.下划线.美元符号($) 其他:字母.下划线.数字.美元符号($) 注意: 1.首字母不能为 ...

  9. Good Time 冲刺 一

    2018/6/14 我们组之前没有开发小程序的经验,所以在尝试中不断探索与学习.在完成小程序的初步注册和界面完善后,我们组开始进行开发任务. 1.1成员简述: 王怡镔:“今天主要学习小程序开发知识及相 ...

  10. 团队作业7——第二次项目冲刺(Beta版本12.07——12.08)

    1.当天站立式会议照片 本次会议在5号公寓3楼召开,本次会议内容:①:熟悉每个人想做的模块.②:根据项目要求还没做的完成. 2.每个人的工作 经过会议讨论后确定了每个人的分工 组员 任务 陈福鹏 实现 ...