Netty实现客户端和服务端通信简单例子
Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。
在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。
Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的并发模型。
Netty提供了内置的常用编解码器,包括行编解码器[一行一个请求],前缀长度编解码器[前N个字节定义请求的字节长度],可重放解码器[记录半包消息的状态],HTTP编解码器,WebSocket消息编解码器等等
Netty提供了一些列生命周期回调接口,当一个完整的请求到达时,当一个连接关闭时,当一个连接建立时,用户都会收到回调事件,然后进行逻辑处理。
Netty可以同时管理多个端口,可以使用NIO客户端模型,这些对于RPC服务是很有必要的。
Netty除了可以处理TCP Socket之外,还可以处理UDP Socket。
在消息读写过程中,需要大量使用ByteBuffer,Netty对ByteBuffer在性能和使用的便捷性上都进行了优化和抽象。
代码:
服务端:
package com.kinson.netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* descripiton:服务端
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 15:37
* @modifier:
* @since:
*/
public class NettyServer {
/**
* 端口
*/
private int port;
public NettyServer(int port) {
this.port = port;
}
public void run() {
//EventLoopGroup是用来处理IO操作的多线程事件循环器
//负责接收客户端连接线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
//负责处理客户端i/o事件、task任务、监听任务组
EventLoopGroup workerGroup = new NioEventLoopGroup();
//启动 NIO 服务的辅助启动类
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
//配置 Channel
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ServerIniterHandler());
//BACKLOG用于构造服务端套接字ServerSocket对象,
// 标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
//是否启用心跳保活机制
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
//绑定服务端口监听
Channel channel = bootstrap.bind(port).sync().channel();
System.out.println("server run in port " + port);
//服务器关闭监听
/*channel.closeFuture().sync()实际是如何工作:
channel.closeFuture()不做任何操作,只是简单的返回channel对象中的closeFuture对象,对于每个Channel对象,都会有唯一的一个CloseFuture,用来表示关闭的Future,
所有执行channel.closeFuture().sync()就是执行的CloseFuturn的sync方法,从上面的解释可以知道,这步是会将当前线程阻塞在CloseFuture上*/
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭事件流组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new NettyServer(8899).run();
}
}
服务端业务逻辑处理:
package com.kinson.netty.server;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
/**
* descripiton: 服务器的处理逻辑
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 15:50
* @modifier:
* @since:
*/
public class ServerHandler extends SimpleChannelInboundHandler<String> {
/**
* 所有的活动用户
*/
public static final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
/**
* 读取消息通道
*
* @param context
* @param s
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext context, String s)
throws Exception {
Channel channel = context.channel();
//当有用户发送消息的时候,对其他的用户发送消息
for (Channel ch : group) {
if (ch == channel) {
ch.writeAndFlush("[you]: " + s + "\n");
} else {
ch.writeAndFlush("[" + channel.remoteAddress() + "]: " + s + "\n");
}
}
System.out.println("[" + channel.remoteAddress() + "]: " + s + "\n");
}
/**
* 处理新加的消息通道
*
* @param ctx
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
for (Channel ch : group) {
if (ch == channel) {
ch.writeAndFlush("[" + channel.remoteAddress() + "] coming");
}
}
group.add(channel);
}
/**
* 处理退出消息通道
*
* @param ctx
* @throws Exception
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
for (Channel ch : group) {
if (ch == channel) {
ch.writeAndFlush("[" + channel.remoteAddress() + "] leaving");
}
}
group.remove(channel);
}
/**
* 在建立连接时发送消息
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
boolean active = channel.isActive();
if (active) {
System.out.println("[" + channel.remoteAddress() + "] is online");
} else {
System.out.println("[" + channel.remoteAddress() + "] is offline");
}
ctx.writeAndFlush("[server]: welcome");
}
/**
* 退出时发送消息
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
if (!channel.isActive()) {
System.out.println("[" + channel.remoteAddress() + "] is offline");
} else {
System.out.println("[" + channel.remoteAddress() + "] is online");
}
}
/**
* 异常捕获
*
* @param ctx
* @param e
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
Channel channel = ctx.channel();
System.out.println("[" + channel.remoteAddress() + "] leave the room");
ctx.close().sync();
}
}
服务端处理器注册:
package com.kinson.netty.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* descripiton: 服务器初始化
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 15:46
* @modifier:
* @since:
*/
public class ServerIniterHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//管道注册handler
ChannelPipeline pipeline = socketChannel.pipeline();
//编码通道处理
pipeline.addLast("decode", new StringDecoder());
//转码通道处理
pipeline.addLast("encode", new StringEncoder());
//聊天服务通道处理
pipeline.addLast("chat", new ServerHandler());
}
}
客户端:
package com.kinson.netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* descripiton: 客户端
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 16:40
* @modifier:
* @since:
*/
public class NettyClient {
private String ip;
private int port;
private boolean stop = false;
public NettyClient(String ip, int port) {
this.ip = ip;
this.port = port;
}
public void run() throws IOException {
//设置一个多线程循环器
EventLoopGroup workerGroup = new NioEventLoopGroup();
//启动附注类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
//指定所使用的NIO传输channel
bootstrap.channel(NioSocketChannel.class);
//指定客户端初始化处理
bootstrap.handler(new ClientIniterHandler());
try {
//连接服务
Channel channel = bootstrap.connect(ip, port).sync().channel();
while (true) {
//向服务端发送内容
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String content = reader.readLine();
if (StringUtils.isNotEmpty(content)) {
if (StringUtils.equalsIgnoreCase(content, "q")) {
System.exit(1);
}
channel.writeAndFlush(content);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
} finally {
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyClient("127.0.0.1", 8899).run();
}
}
客户端逻辑处理:
package com.kinson.netty.client;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* descripiton: 客户端逻辑处理
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 16:50
* @modifier:
* @since:
*/
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
//打印服务端的发送数据
System.out.println(s);
}
}
客户端处理器注册:
package com.kinson.netty.client;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* descripiton: 客户端处理初始化
*
* @author: www.iknowba.cn
* @date: 2018/3/23
* @time: 16:55
* @modifier:
* @since:
*/
public class ClientIniterHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//注册管道
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("http", new HttpClientCodec());
pipeline.addLast("chat", new ClientHandler());
}
}
测试时先启动服务端,再启动客户端。。。
Netty实现客户端和服务端通信简单例子的更多相关文章
- Netty入门——客户端与服务端通信
Netty简介Netty是一个基于JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性.换句话说,Netty是一个NIO框架,使用它可以简单快速 ...
- WCF客户端与服务端通信简单入门教程
服务端 1.新建空白解决方案,然后再空白解决方案中新建:WCF服务应用程序.建完后如图: 2.删掉自动生成的IService1.cs和Service.svc并添加WCF服务文件StudentServi ...
- netty-3.客户端与服务端通信
(原) 第三篇,客户端与服务端通信 以下例子逻辑: 如果客户端连上服务端,服务端控制台就显示,XXX个客户端地址连接上线. 第一个客户端连接成功后,客户端控制台不显示信息,再有其它客户端再连接上线,则 ...
- Netty入门之客户端与服务端通信(二)
Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...
- Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令.
Python进阶----SOCKET套接字基础, 客户端与服务端通信, 执行远端命令. 一丶socket套接字 什么是socket套接字: 专业理解: socket是应用层与TCP/IP ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- 基于开源SuperSocket实现客户端和服务端通信项目实战
一.课程介绍 本期带给大家分享的是基于SuperSocket的项目实战,阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何实现打通B/S与C/S网络通讯,如果您对本期的<基于开源Supe ...
- Python socket编程客户端与服务端通信
[本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...
- 实验09——java基于TCP实现客户端与服务端通信
TCP通信 需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手 底层通过 流 发送数据 数据没有大小限制 可靠的传输机制 - 丢包重发 包的顺序的 ...
随机推荐
- 使用lambda编程之延迟执行
使用lambda表达式的主要原因是,将代码的执行延迟到一个合适的时间点. 所有的lambda表达式都是延迟执行的.毕竟,如果你希望立即执行一段代码,那就没有必要使用lambda表达式了.延迟执行代码的 ...
- 谈一谈Java中的Error和Exception
Error和Exception的联系 继承结构:Error和Exception都是继承于Throwable,RuntimeException继承自Exception. Error和RuntimeExc ...
- java日期转化
package com.kang.util; import java.text.ParseException; import java.text.SimpleDateFormat; import ja ...
- visual studio调试功能简述
vs调试简述 1.调试功能简述 vs提供了很强大的调试功能,能够让我们一步步执行中找到每个变量的值,便于查错改错.很多IDE都有调试功能,在使用调试功能时,记得先创建一个项目,哪怕是一个文件,也创建一 ...
- Pokémon Go呼应设计:让全世界玩家疯狂沉迷
引言:什么样的呼应设计会让移动游戏玩家沉迷?那必须为玩家构建一个属于玩家本人或者被玩家认可的虚拟环境,或者说是被玩家认可的虚拟世界.在移动游戏时代,想要做到这一点并不容易.但Pokémon Go却做到 ...
- Android项目中的换肤总结
纵观现在各种Android app,其换肤需求可以归为 白天/黑夜主题切换(或者别的名字,通常2套),如同花顺/自选股/天天动听等,UI表现为一个switcher. 多种主题切换,通常为会员特权,如Q ...
- Android JNI的Android.mk文件语法详解
Android.mk简介: Android.mk文件用来告知NDK Build 系统关于Source的信息. Android.mk将是GNU Makefile的一部分,且将被Build System解 ...
- Fresco-FaceBook推出的Android图片加载库
在Android设备上面,快速高效的显示图片是极为重要的.过去的几年里,我们在如何高效的存储图像这方面遇到了很多问题.图片太大,但是手机的内存却很小.每一个像素的R.G.B和alpha通道总共要占用4 ...
- Flex中的FusionCharts 2D饼图
1.设计思路 (1)FusionCharts中有Flex组件文件FusionCharts.swc,这样可以让FusionCharts用Flex展示出来: (2)利用xmlns:components=& ...
- 无法连接MySQL
1.错误描述 Error No. 2003 Can't connect to MySQL Server on 'localhost' (10061) 2.错误原因 打开任务管理器,关闭了m ...