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通信 需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手 底层通过 流 发送数据 数据没有大小限制 可靠的传输机制 - 丢包重发 包的顺序的 ...
随机推荐
- SpringMVC的常用注解
在SpringMVC中常用的注解主要都是用于Controller上,所以下面的四大不同类型的注解都是根据它们处理的request的不同内容部分来区分的: 处理requ ...
- HI3531uboot开机画面
startvo 0 36 13; startgx 0 0x88000000 1600 0 0 800 600; //startgx 0 0x88000000 2048 0 0 1024 768; se ...
- 通过地址获得经纬度(百度Geocoding API)
1.什么是Geocoding? Geocoding API 是一类简单的HTTP接口,用于提供从地址到经纬度坐标或者从经纬度坐标到地址的转换服务,用户可以使用C# .C++.Java等开发语言发送HT ...
- vxWorks下intel82567v3网卡驱动的更新
/* 82567 devicesID */ #define INTEL_DEVICEID_82567LF 0x10BF#define INTEL_DEVICEID_82567 ...
- 使用NPOI导入导出标准Excel
尝试过很多Excel导入导出方法,都不太理想,无意中逛到oschina时,发现了NPOI,无需Office COM组件且不依赖Office,顿时惊为天人,怀着无比激动的心情写下此文. 曾使用过的方法 ...
- ASP.NET Core源码学习(一)Hosting
ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...
- Go基础之--操作Mysql(三)
事务是数据库的一个非常重要的特性,尤其对于银行,支付系统,等等.database/sql提供了事务处理的功能.通过Tx对象实现.db.Begin会创建tx对象,后者的Exec和Query执行事务的数据 ...
- 图片压缩上传Thumbnailator 插件
一,接口已经写死 public static String upload(String appCode, MultipartFile inputFile) public static String u ...
- JVM内存分析
1.java内存模型分析 java虚拟机运行时数据存储区域包括线程隔离和线程共享两类,整个PC的内存图如下所示: 下面对以上内存区域说明: 1.1 register和cache 当代计算机一般有多个c ...
- 《Master Bitcoin》学习笔记01
前言 关于比特币入门,其运行原理(理解性描述),什么是挖矿,还有一些学习资料的整理,详见此博客链接 第一章讲比特币的历史,算是导入,上面的博客链接有概括. 第二章举了使用比特币交易的例子,其中介绍了一 ...