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通信 需要先创建连接 - 并且在创建连接的过程中 需要经过三次握手 底层通过 流 发送数据 数据没有大小限制 可靠的传输机制 - 丢包重发 包的顺序的 ...
随机推荐
- mac的一些小技巧
切换到超级管理员: sudo -s: 让你很快的全屏之间进行切换!很方便!很实用! command+tab 今天的感觉到公司的每一个人员,对于mac的系统的使用都是非常的熟悉的,我还什么都不会. 我得 ...
- java读取数据写入txt文件并将读取txt文件写入另外一个表
package com.xsw.test; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.F ...
- mysql导入导出数据
mysqldump是MySQL自带的导出数据工具,通常我们用它来导出MySQL中,但是有时候我们需要导出MySQL数据库中某个表的部分数据作为测试. mysqldump命令中带有一个 --where/ ...
- java日期转化
package com.kang.util; import java.text.ParseException; import java.text.SimpleDateFormat; import ja ...
- 解决VC6下调不出MSDN的问题!
原文:http://www.programfan.com/blog/article.asp?id=1524http://blog.programfan.com/trackback.asp?id=15 ...
- linux下编译sphinx拓展
编译libsphinxclient sphinx 源码包里的api文件夹下的libsphinxclient cd /root/api/libsphinxclient/ ./configure make ...
- 【mongodb系统学习之四】查看mongodb进程
四.查看mongodb进程(可以配合启动和关闭使用): 1).方法一:直接查看mongodb进程是否已经存在(用上面的方式启动后,需要另开一个窗口操作):ps –ef|grep mongodb, 如图 ...
- 在.Net Core中使用MongoDB的入门教程(二)
在上一篇文章中,讲到了MongoDB在导入驱动.MongoDB的连接,数据的插入等. 在.Net Core中使用MongoDB的入门教程(一) 本篇文章将接着上篇文章进行介绍MongoDB在.Net ...
- Python基础_函数闭包、调用、递归
这节的主要内容是函数的几个用法闭包,调用.递归. 一.函数闭包 对闭包更好的理解请看:https://www.cnblogs.com/Lin-Yi/p/7305364.html 我们来看一个简单的例子 ...
- WPF自学入门(四)WPF路由事件之自定义路由事件
在上一遍博文中写到了内置路由事件,其实除了内置的路由事件,我们也可以进行自定义路由事件.接下来我们一起来看一下WPF中的自定义路由事件怎么进行创建吧. 创建自定义路由事件分为3个步骤: 1.声明并注册 ...