首页 技术 正文
技术 2022年11月22日
0 收藏 997 点赞 2,950 浏览 6999 个字

服务端:

引入Netty依赖

        <!-- netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.27.Final</version>
</dependency>

WebSocket全局配置类的编写

/**
* 存储整个工程的全局配置
*/
public class NettyConfig { /**
* 存储每一个客户端接入进来时的channel对象
*/
public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);}

接收/处理/响应客户端websocket请求的核心业务处理类

package com.imooc.netty;import java.util.Date;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;/**
* 接收/处理/响应客户端websocket请求的核心业务处理类
*/
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> { private WebSocketServerHandshaker handshaker;
private static final String WEB_SOCKET_URL = "ws://localhost:8888/websocket"; /**
* 客户端与服务端创建连接的时候调用
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
NettyConfig.group.add(ctx.channel());
System.out.println("客户端与服务端连接开启...");
} /**
* 客户端与服务端断开连接的时候调用
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
NettyConfig.group.remove(ctx.channel());
System.out.println("客户端与服务端连接关闭...");
} /**
* 服务端接收客户端发送过来的数据结束之后调用
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} /**
* 工程出现异常的时候调用
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
} /**
* 服务端处理客户端websocket请求的核心方法
*/
protected void channelRead0(ChannelHandlerContext context, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
// 处理客户端向服务端发起http握手请求的业务
this.handHttpRequest(context, (FullHttpRequest) msg);
}else if (msg instanceof WebSocketFrame) {
// 处理websocket连接业务
this.handWebsocketFrame(context, (WebSocketFrame) msg);
}
} /**
* 处理客户端与服务端之前的websocket业务
*/
private void handWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame){
// 判断是否是关闭websocket的指令
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
} // 判断是否是ping消息
if (frame instanceof PingWebSocketFrame) {
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
} // 判断是否是二进制消息,如果是二进制消息,抛出异常
if (!(frame instanceof TextWebSocketFrame)){
System.out.println("目前我们不支持二进制消息");
throw new RuntimeException("【"+this.getClass().getName()+"】不支持消息");
} // 返回应答消息
// 获取客户端向服务端发送的消息
String request = ((TextWebSocketFrame) frame).text();
System.out.println("服务端收到客户端的消息====>>>" + request);
TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + " ===>>> " + request);
// 群发,服务端向每个连接上来的客户端群发消息
NettyConfig.group.writeAndFlush(tws);
}
/**
* 处理客户端向服务端发起http握手请求的业务
*/
private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req){
if (!req.decoderResult().isSuccess() || !("websocket".equals(req.headers().get("Upgrade")))) {
this.sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
}else{
handshaker.handshake(ctx.channel(), req);
}
} /**
* 服务端向客户端响应消息
*/
private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req,
DefaultFullHttpResponse res){
if (res.status().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
}
// 服务端向客户端发送数据
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}}

WebSocket初始化连接时各个组件的类实现

package com.imooc.netty;import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;/**
* 初始化连接时候的各个组件
*/
public class MyWebSocketChannelHandler extends ChannelInitializer<SocketChannel> { @Override
protected void initChannel(SocketChannel e) throws Exception {
e.pipeline().addLast("http-codec", new HttpServerCodec());
e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
e.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
e.pipeline().addLast("handler", new MyWebSocketHandler());
}}

WebSocket启动类的编写

package com.imooc.netty;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;/**
* 程序的入口,负责启动应用
*/
public class Main { public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new MyWebSocketChannelHandler());
System.out.println("服务端开启等待客户端连接....");
Channel ch = b.bind(8888).sync().channel();
ch.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally{
// 退出程序
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}}

客户端

编写客户端页面

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset = utf-8"/>
<title>WebSocket客户端</title>
<script type="text/javascript">
var socket;
if(!window.WebSocket){
window.WebSocket = window.MozWebSocket;
} if(window.WebSocket){
socket = new WebSocket("ws://localhost:8888/websocket");
socket.onmessage = function(event){
var ta = document.getElementById('responseContent');
ta.value += event.data + "\r\n";
}; socket.onopen = function(event){
var ta = document.getElementById('responseContent');
ta.value = "你当前的浏览器支持WebSocket,请进行后续操作\r\n";
}; socket.onclose = function(event){
var ta = document.getElementById('responseContent');
ta.value = "";
ta.value = "WebSocket连接已经关闭\r\n";
};
}else{
alert("您的浏览器不支持WebSocket");
} function send(message){
if(!window.WebSocket){
return;
}
if(socket.readyState == WebSocket.OPEN){
socket.send(message);
}else{
alert("WebSocket连接没有建立成功!!");
}
}
</script>
</head>
<body>
<form onSubmit="return false;">
<input type = "text" name = "message" value = ""/>
<br/><br/>
<input type = "button" value = "发送WebSocket请求消息" onClick = "send(this.form.message.value)"/>
<hr color="red"/>
<h2>客户端接收到服务端返回的应答消息</h2>
<textarea id = "responseContent" style = "width:1024px; height:300px"></textarea>
</form>
</body>
</html>

测试

启动服务端,然后打开客户端页面

【Netty】Netty入门之WebSocket小例子

客户端向服务端发送WebSocket消息,观察结果

【Netty】Netty入门之WebSocket小例子

服务端控制台:

【Netty】Netty入门之WebSocket小例子

关闭服务端

【Netty】Netty入门之WebSocket小例子

上一篇: activity-alias
下一篇: Wordpress入门笔记
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,088
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,564
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,413
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,186
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,822
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,905