代码地址如下:
http://www.demodashi.com/demo/13577.html

一款基于Netty开发的WebSocket服务器

这是一款基于Netty框架开发的服务端,通信协议为WebSocket。主要用于Java后台服务器向浏览器进行消息推送。

需求

对于一个Web项目而言,客户端一般均为各种各样的浏览器,如何从后端服务器向浏览器客户端进行消息推送,便成了一个棘手的问题,好在在HTTP1.1之后,HTTP可以支持长连接,由此,我在Netty框架的基础上开发了这个WebSocket服务端。

当然,你依旧可以下载源码进行测试,集成,二次开发等等。

环境

  • Intellij IDEA2018
  • JDK 1.8
  • 插件:Simple WebSocket Client 0.1.3
    • FireFox Quantum 60.0.1 (64 位)
    • Google Chrome 67.0.3396.99(正式版本)(64 位)

运行结果

请看下图:

实现步骤及源码

WebSocket

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebSocket通信协议于2011年被IETF定为标准RFC 6455,WebSocketAPI被W3C定为标准。 在WebSocket API中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

目录结构

核心类讲解

  • WebSocketChildHandler
  • WebSocketServerHandler

WebSocketChildHandler

这个类的主要作用就是为Netty的通道注册事件。其核心代码见下,其中webSocketUrl就是客户端与服务端进行连接的请求路径,我将其写入了配置文件,交由Spring管理,以注入的方式传递到WebSocketServerHandler中。

ChannelPipeline pipeline = socketChannel.pipeline();
// 将请求与应答消息编码或者解码为HTTP消息
pipeline.addLast("http-codec", new HttpServerCodec());
// 将http消息的多个部分组合成一条完整的HTTP消息
pipeline.addLast("aggregator", new HttpObjectAggregator(HttpObjectConstant.MAX_CONTENT_LENGTH));
// 向客户端发送HTML5文件。主要用于支持浏览器和服务端进行WebSocket通信
pipeline.addLast("http-chunked", new ChunkedWriteHandler());
// 服务端Handler
pipeline.addLast("handler", new WebSocketServerHandler(webSocketUrl));

WebSocketServerHandler

这个类是真正的核心类,这个类的主要功能为:

  • 进行第一次握手
  • 对消息进行处理
    • 可以实现点对点通信
    • 可以实现广播功能
    • 可以实现点对端通信
/**
* 接收客户端发送的消息
*
* @param channelHandlerContext ChannelHandlerContext
* @param receiveMessage 消息
*/
@Override
protected void messageReceived(ChannelHandlerContext channelHandlerContext, Object receiveMessage) throws Exception {
// 传统http接入 第一次需要使用http建立握手
if (receiveMessage instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) receiveMessage;
LOGGER.info("├ [握手]: {}", fullHttpRequest.uri());
// 握手
handlerHttpRequest(channelHandlerContext, fullHttpRequest);
// 发送连接成功给客户端
channelHandlerContext.channel().write(new TextWebSocketFrame("连接成功"));
}
// WebSocket接入
else if (receiveMessage instanceof WebSocketFrame) {
WebSocketFrame webSocketFrame = (WebSocketFrame) receiveMessage;
handlerWebSocketFrame(channelHandlerContext, webSocketFrame);
}
} /**
* 第一次握手
*
* @param channelHandlerContext channelHandlerContext
* @param req 请求
*/
private void handlerHttpRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest req) {
// 构造握手响应返回,本机测试
WebSocketServerHandshakerFactory wsFactory
= new WebSocketServerHandshakerFactory(webSocketUrl, Constant.NULL, Constant.FALSE);
// region 从连接路径中截取连接用户名
String uri = req.uri();
int i = uri.lastIndexOf("/");
String userName = uri.substring(i + 1, uri.length());
// endregion
Channel connectChannel = channelHandlerContext.channel();
// 加入在线用户
WebSocketUsers.put(userName, connectChannel);
socketServerHandShaker = wsFactory.newHandshaker(req);
if (socketServerHandShaker == null) {
// 发送版本错误
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(connectChannel);
} else {
// 握手响应
socketServerHandShaker.handshake(connectChannel, req);
}
} /**
* webSocket处理逻辑
*
* @param channelHandlerContext channelHandlerContext
* @param frame webSocketFrame
*/
private void handlerWebSocketFrame(ChannelHandlerContext channelHandlerContext, WebSocketFrame frame) throws IOException {
Channel channel = channelHandlerContext.channel();
// region 判断是否是关闭链路的指令
if (frame instanceof CloseWebSocketFrame) {
LOGGER.info("├ 关闭与客户端[{}]链接", channel.remoteAddress());
socketServerHandShaker.close(channel, (CloseWebSocketFrame) frame.retain());
return;
}
// endregion
// region 判断是否是ping消息
if (frame instanceof PingWebSocketFrame) {
LOGGER.info("├ [Ping消息]");
channel.write(new PongWebSocketFrame(frame.content().retain()));
return;
}
// endregion
// region 纯文本消息
if (frame instanceof TextWebSocketFrame) {
String text = ((TextWebSocketFrame) frame).text();
LOGGER.info("├ [{} 接收到客户端的消息]: {}", new Date(), text);
channel.write(new TextWebSocketFrame(new Date() + " 服务器将你发的消息原样返回:" + text));
}
// endregion
// region 二进制消息 此处使用了MessagePack编解码方式
if (frame instanceof BinaryWebSocketFrame) {
BinaryWebSocketFrame binaryWebSocketFrame = (BinaryWebSocketFrame) frame;
ByteBuf content = binaryWebSocketFrame.content();
LOGGER.info("├ [二进制数据]:{}", content);
final int length = content.readableBytes();
final byte[] array = new byte[length];
content.getBytes(content.readerIndex(), array, 0, length);
MessagePack messagePack = new MessagePack();
WebSocketMessageEntity webSocketMessageEntity = messagePack.read(array, WebSocketMessageEntity.class);
LOGGER.info("├ [解码数据]: {}", webSocketMessageEntity);
WebSocketUsers.sendMessageToUser(webSocketMessageEntity.getAcceptName(), webSocketMessageEntity.getContent());
}
// endregion
}

至此,服务端算是开发完成。但可以看出,服务端中仍有很大的发展空间,细心的同学可以发现我在第一次握手时,将Channel存储了起来,对于上述的三种情况也有简易的实现方案。

如果有必要,我也会将非浏览器客户端代码(非Js客户端)写成例子,共享出来。

一款基于Netty开发的WebSocket服务器

代码地址如下:
http://www.demodashi.com/demo/13577.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

一款基于Netty开发的WebSocket服务器的更多相关文章

  1. (二)基于Netty的高性能Websocket服务器(netty-websocket-spring-boot)

    @toc Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高. 1.Netty为 ...

  2. Xsoup 是一款基于 Jsoup 开发的

    Xsoup 是一款基于Jsoup 开发的,使用XPath抽取Html元素的工具.它被用于作者的爬虫框架 WebMagic 中,进行XPath 解析和抽取. 此次更新主要增加了一些XPath语法的支持. ...

  3. 基于线程开发一个FTP服务器

    一,项目题目:基于线程开发一个FTP服务器 二,项目要求: 基本要求: 1.用户加密认证   2.允许同时多用户登录   3.每个用户有自己的家目录 ,且只能访问自己的家目录   4.对用户进行磁盘配 ...

  4. easy-im:一款基于netty的即时通讯系统

    介绍 easy-im是面向开发者的一款轻量级.开箱即用的即时通讯系统,帮助开发者快速搭建消息推送等功能. 基于easy-im,你可以快速实现以下功能: + 聊天软件 + IoT消息推送 基本用法 项目 ...

  5. VBox 一款基于vue开发的音乐盒 序章

    己基于vue写了一个 Mplayer, github地址:https://github.com/xiangwenhu/MPlaer, 演示地址:http://babydairy2017.cloudap ...

  6. Netty实现简单WebSocket服务器

    本文参考<Netty权威指南>├── WebSocketServerHandler.java├── WebSocketServer.java└── wsclient.html packag ...

  7. 推荐一款基于 AI 开发的 IDE 插件,帮助提升编码效率

    最近在浏览技术社区的时候,发现了一款神奇 IDE 插件,官网称可以利用 AI 帮助程序员写代码,一下子吸引了我的好奇心.赶紧下载下来使用一番,感觉确实蛮神奇,可以火速提升编程效率. 这款插件叫做 ai ...

  8. 一款基于 Android 开发的离线版的 MM 图片浏览 App

    一款离线版的 MM 图片浏览 App,有点类似掌上百度的图片专栏应用.图片采用瀑布流展示方式,点击图片集,支持左右手势滑动切换图片:支持放大缩小功能. 实现功能:1)图片完全离线,不耗个人 GPRS ...

  9. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

随机推荐

  1. 原型开发工具 mockplus

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 原型开发工具  mockplus 微信(演示) - Mockup Plus Web Ap ...

  2. 【推导】【NTT】hdu6061 RXD and functions(NTT)

    题意:给定一个n次多项式f(x)的各项系数,让你求f(x-Σai)的各项系数. http://blog.csdn.net/v5zsq/article/details/76780053 推导才是最关键的 ...

  3. vue的开发环境搭建命令加图解

    vue的开发环境的搭建 不管什么软件我们都要去官网下载安装,这是作为专业程序员的安全意识. 1.安装node.js 官方下载的页面:点击这里 大约展示的页面是这样子的!我们演示是windows 64位 ...

  4. python基础--接口与归一化设计、封装、异常、网络编程

    1 接口与归一化设计 1.1 归一化概念: 归一化的好处: 1.归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大降低了使用者的使用难度. 2.归一化使得高层的外部 ...

  5. [BZOJ1003](ZJOI 2006) 物流运输trans

    [题目描述] 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟 ...

  6. HDU 2389 Rain on your Parade(二分匹配,Hopcroft-Carp算法)

    Rain on your Parade Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 655350/165535 K (Java/Ot ...

  7. Linux设备文件简介

    转:http://www.360doc.com/content/11/0418/00/5087210_110410837.shtml 版权声明 本 文作者是一位自由软件爱好者,所以本文虽然不是软件,但 ...

  8. 【shiro】报错: If the controller requires proxying (e.g. due to @Transactional), please use class-based proxying.

    spring整合shiro,项目报如下错误: ==============异常开始============= java.lang.IllegalStateException: The mapped c ...

  9. Java递归删除文件目录的方法

    public static void delDir(String path){ File dir=new File(path); if(dir.exists()){ File[] tmp=dir.li ...

  10. c++类模板template中的typename使用方法-超级棒

    转载:https://blog.csdn.net/vanturman/article/details/80269081 如有问题请联系我删除: 目录 起因 typename的常见用法 typename ...