搭建WebSocket服务器与客户端
市场上有几款比较好的开源库供使用,比如PyWebSocket,WebSocket-Node, LibWebSockets等,这些库文件已经实现了WebSocket数据包的封装和解析,我们可以调用这些接口减少工作量。
1. PyWebSocket
PyWebSocket采用Python语言编写,可以很好的跨平台,扩展起来也比较简单,目前WebKit采用它搭建WebSocket服务器来做LayoutTest,参见http://code.google.com/p/pywebsocket/
2.WebSocket-Node
WebSocket-Node采用JavaScript语言编写,这个库是建立在nodejs之上的,参见https://github.com/Worlize/Websocket-Node
3. LibWebSockets
LibWebSockets采用C/C++语言编写,可定制化的力度更大,从TCP监听开始到封包的完成我们都可以参与编程。参见git://git.warmcat.com/libwebsockets
4.Netty
采用了
Netty 中的 NIO 来构建 WebSocket 后端,参见:https://github.com/netty/netty
一.下面介绍一下用Netty搭建WebSocket服务器
1.WebsocketChatServer .java
package com.waylau.netty.demo.websocketchat; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * Websocket 聊天服务器-服务端 * * @author waylau.com * @date 2015-3-7 */ public class WebsocketChatServer { private int port; public WebsocketChatServer(int port) { this.port = port; } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // (3) .childHandler(new WebsocketChatServerInitializer()) //处理初始化类 .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); // 保持长连接 System.out.println("WebsocketChatServer 启动了" + port); // 绑定端口,开始接收进来的连接 ChannelFuture f = b.bind(port).sync(); // (7) // 等待服务器 socket 关闭 。 // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); System.out.println("WebsocketChatServer 关闭了"); } } public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new WebsocketChatServer(port).run(); } }2.服务端 ChannelInitializer
package com.waylau.netty.demo.websocketchat; 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; /** * 服务端 ChannelInitializer * * @author * @date 2015-3-13 */ public class WebsocketChatServerInitializer extends ChannelInitializer<SocketChannel> { //1 @Override public void initChannel(SocketChannel ch) throws Exception {//2 ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(64*1024)); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpRequestHandler("/ws")); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TextWebSocketFrameHandler()); } }3.处理TextWebSocketFrame
package com.waylau.netty.demo.websocketchat; 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.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.concurrent.GlobalEventExecutor; /** * 处理TextWebSocketFrame * * @author * 2015年3月26日 */ public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // 读取数据并转发 Channel incoming = ctx.channel(); for (Channel channel : channels) { if (channel != incoming){ channel.writeAndFlush(new TextWebSocketFrame(msg.text())); } else { channel.writeAndFlush(new TextWebSocketFrame(msg.text())); } } } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // 连接时调用 Channel incoming = ctx.channel(); // Broadcast a message to multiple Channels channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 加入")); channels.add(incoming); System.out.println("Client:"+incoming.remoteAddress() +"加入"); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // 移除时调用 Channel incoming = ctx.channel(); // Broadcast a message to multiple Channels channels.writeAndFlush(new TextWebSocketFrame("[SERVER] - " + incoming.remoteAddress() + " 离开")); System.out.println("Client:"+incoming.remoteAddress() +"离开"); // A closed Channel is automatically removed from ChannelGroup, // so there is no need to do "channels.remove(ctx.channel());" } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // Channel incoming = ctx.channel(); System.out.println("Client:"+incoming.remoteAddress()+"在线"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { // Channel incoming = ctx.channel(); System.out.println("Client:"+incoming.remoteAddress()+"掉线"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) // 异常则关闭 throws Exception { Channel incoming = ctx.channel(); System.out.println("Client:"+incoming.remoteAddress()+"异常"); // 当出现异常就关闭连接 cause.printStackTrace(); ctx.close(); } }二.,客户端
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>WebSocket Chat</title> </head> <body> <script type="text/javascript"> var socket; if (!window.WebSocket) { window.WebSocket = window.MozWebSocket; } if (window.WebSocket) { socket = new WebSocket("ws://192.169.1.101:8080/ws"); socket.onmessage = function(event) { var ta = document.getElementById("responseText"); ta.value = ta.value + " " + event.data }; socket.onopen = function(event) { var ta = document.getElementById("responseText"); ta.value = "连接开启!"; }; socket.onclose = function(event) { var ta = document.getElementById("responseText"); ta.value = ta.value + "连接被关闭"; }; } else { alert("你的浏览器不支持 WebSocket!"); } function send(message) { if (!window.WebSocket) { return; } if (socket.readyState == WebSocket.OPEN) { socket.send(message); } else { alert("连接没有开启."); } } </script> <form onsubmit="return false;"> <h3>WebSocket 聊天室:</h3> <textarea id="responseText" style="width: 500px; height: 300px;"></textarea> <br> <input type="text" name="message" style="width: 300px" value="Welcome to www.waylau.com"> <input type="button" value="发送消息" onclick="send(this.form.message.value)"> <input type="button" onclick="javascript:document.getElementById("responseText").value=""" value="清空聊天记录"> </form> <br> <br> </body> </html>点击下载源码
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: HTML5WebSocket实现点对点聊天
- 下一篇: 剑指offer06题二叉树的重建(c语言)