1.后端netty的步骤跟上一篇一样:
==========================================================================================
1.先创建第一个类 HelloServer :
package netty; /* 实现客户端 发送一个请求 服务器会返回 hello netty */ import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.sctp.nio.NioSctpServerChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.ScheduledFuture; import netty.HelloServerInitializer; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class HelloServer { public static void main(String[] args) throws Exception { // 定义一对线程组 // 主线程组, 用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // netty服务器的创建, ServerBootstrap 是一个启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) // 设置主从线程组 .channel(NioServerSocketChannel.class) // 设置nio的双向通道 .childHandler(new HelloServerInitializer()); // 子处理器,用于处理workerGroup // 启动server,并且设置8088为启动的端口号,同时启动方式为同步 ChannelFuture channelFuture = serverBootstrap.bind(8088).sync(); // 监听关闭的channel,设置位同步方式 channelFuture.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
==========================================================================================
2.创建第二个类 WSServerInitializer :
package netty.websocket; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; import netty.CustomHandler; public class WSServerInitializer extends ChannelInitializer<SocketChannel> { protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline =channel.pipeline(); //websocket 基于 http 协议,所以要有 http 编解码器 pipeline.addLast(new HttpServerCodec()); //对写大数据流的支持 pipeline.addLast(new ChunkedWriteHandler()); //对httpMessage 进行聚合,聚合成 FullHttpRequest 或 FullHttpResonse pipeline.addLast(new HttpObjectAggregator(1024*64)); // ================ 以上是 用于支持 http协议=============== // websocket 服务器处理的协议,用于指定 给 客户端连接访问的路由:/ws (/ws 是可以自定义的) // 本 handler 会帮助你处理 一些 繁琐的复杂的事 // 会帮助 你处理一些 握手动作:handingshaking(close,ping,pong) ping+pong=心跳 // 对于 websocket 来讲 ,都是以 frames 进行 传输的,不同的数据类型 对应的frames 也不同 pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); // ================ 以下是自定义的 handler=============== //自定义的 handler pipeline.addLast("ChatHandler", new ChatHandler()); } }
==========================================================================================
创建第三个类 CustomHandler :
package netty; import com.sun.deploy.net.HttpRequest; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; /** * * 创建自定义助手类 * * SimpleChannelInboundHandler:对于请求来讲,其实相当于【入栈,入境】 */ public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> { @Override public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { // 获取channel Channel channel = ctx.channel(); // if (msg instanceof HttpRequest) { //不加可以用,加了,就不行 // 显示客户端的远程地址 System.out.println(channel.remoteAddress()); // 定义发送的数据消息 ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8); // 构建一个http response FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); // 为响应增加数据类型和长度 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); // 把响应刷到客户端 ctx.writeAndFlush(response); } // } }
==========================================================================================
这三个类就是全部的后端。
使用 Hbuilder编写前端 websocket(只有一个页面):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div>发送消息:</div>
<input type="text" id="msgContent"/>
<input type="button" value="点我发送" οnclick="CHAT.chat()"/>
<div>接受消息:</div>
<div id="receiveMsg" style="background-color: gainsboro;"></div>
<script type="application/javascript">
window.CHAT = {
socket: null,
init: function() {
if (window.WebSocket) {
//注意:一开始的ws不能变,(相当于http),后面的ws能变,和后端的 ws一致就行,
// 172.21.160.104 是本机电脑的ip地址 ,8086 也是后端 bind(端口号)
CHAT.socket = new WebSocket("ws://172.21.160.104:8086/ws");
CHAT.socket.onopen = function() {
console.log("连接建立成功...");
},
CHAT.socket.onclose = function() {
console.log("连接关闭...");
},
CHAT.socket.onerror = function() {
console.log("发生错误...");
},
CHAT.socket.onmessage = function(e) {
console.log("接受到消息:" + e.data);
var receiveMsg = document.getElementById("receiveMsg");
var html = receiveMsg.innerHTML;
receiveMsg.innerHTML = html + "<br/>" + e.data;
}
} else {
alert("浏览器不支持websocket协议...");
}
},
chat: function() {
var msg = document.getElementById("msgContent");
CHAT.socket.send(msg.value);
}
};
CHAT.init();
</script>
</body>
</html>
====================================================================================
在浏览器上: