一:为什么要用websocket
Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。
所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素。
二:netty框架搭建websocket服务器
本文简述了一个简单web聊天室功能。
1.下载所依赖jar包
netty-3.6.6.Final.jar 这里的版本最好下载比较新的版本。
2.新建jar工程
创建四个类:Globle.java,WebSocketServer.java,WebSocketServerHandler.java,WebSocketServerPipelineFactory.java
Globle.java
package com.hundsun.websocket; import java.util.HashSet; import java.util.Set; import org.jboss.netty.channel.Channel; public class Globle { public static Set<Channel> ctxs= new HashSet<>(); }
WebSocketServer.java
package com.hundsun.websocket; import java.net.InetSocketAddress; import java.util.concurrent.Executors; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; public class WebSocketServer { private final int port; public static ChannelHandlerContext ctx = null; public WebSocketServer(int port){ this.port = port; } public void run(){ ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(new WebSocketServerPipelineFactory()); System.out.println(); bootstrap.bind(new InetSocketAddress(port)); System.out.println("Web socket server started at port " + port + '.'); System.out.println("Open your browser and navigate to http://localhost:" + port + '/'); } public static void main(String[] args) { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8081; } new WebSocketServer(port).run(); } }
WebSocketServerHandler.java
package com.hundsun.websocket; import java.util.Iterator; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*; public class WebSocketServerHandler extends SimpleChannelUpstreamHandler{ private static final String WEBSOCKET_PATH = "/websocket"; private WebSocketServerHandshaker handshaker; @Override public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { Globle.ctxs.add(ctx.getChannel()); System.out.println(ctx.getChannel()+"s"); } @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { Globle.ctxs.remove(ctx.getChannel()); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object msg = e.getMessage(); String receivedMsg = msg.toString(); receivedMsg = receivedMsg.substring(receivedMsg.indexOf('(')+1, receivedMsg.indexOf(')')); String[] RealMsg = receivedMsg.split(":"); if(!RealMsg[1].trim().equals("")){ if (msg instanceof HttpRequest) { handleHttpRequest(ctx, (HttpRequest) msg); } else if (msg instanceof WebSocketFrame) { Iterator<Channel> MyCtx = Globle.ctxs.iterator(); while(MyCtx.hasNext()){ MyCtx.next().write(new TextWebSocketFrame(RealMsg[1])); } handleWebSocketFrame(ctx, (WebSocketFrame) msg); } } } private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception { WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( getWebSocketLocation(req), null, false); handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel()); } else { handshaker.handshake(ctx.getChannel(), req).addListener(WebSocketServerHandshaker.HANDSHAKE_LISTENER); } WebSocketServer.ctx = ctx; } private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { // Check for closing frame if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame); WebSocketServer.ctx = null; return; } } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getCause().printStackTrace(); e.getChannel().close(); WebSocketServer.ctx = null; } private static String getWebSocketLocation(HttpRequest req) { return "ws://" + req.getHeader(HOST) + WEBSOCKET_PATH; } }
WebSocketServerPipelineFactory.java
package com.hundsun.websocket; import static org.jboss.netty.channel.Channels.*; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.handler.codec.http.HttpChunkAggregator; import org.jboss.netty.handler.codec.http.HttpRequestDecoder; import org.jboss.netty.handler.codec.http.HttpResponseEncoder; public class WebSocketServerPipelineFactory implements ChannelPipelineFactory{ @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = pipeline(); pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("aggregator", new HttpChunkAggregator(65536)); pipeline.addLast("encoder", new HttpResponseEncoder()); pipeline.addLast("handler", new WebSocketServerHandler()); return pipeline; } }
运行main函数即可启动websocket服务器:
三:web客户端
客户端包括文件:js/websocket.js,css/websocket.css,websocket.html
再在img文件夹中添加一个psd.jpg图片文件 img/psd.jpg
js/websocket.js
document.addEventListener("DOMContentLoaded", connect, false) var socket; function connect() { if (!window.WebSocket) { window.WebSocket = window.MozWebSocket; } if (window.WebSocket) { if(socket == null){ socket = new WebSocket("ws://localhost:8081/"); } socket.onmessage = function(event) { var ul = document.getElementById("chart_msg"); var li = document.createElement("li"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); var span = document.createElement("span"); span.style.paddingLeft="20px"; span.style.marginLeft="20px"; span.style.width="230px"; span.style.overflow="hidden"; span.style.marginTop="20px"; span.innerHTML=event.data; var img = document.createElement("img"); img.src="img/psb.jpg"; div2.style.backgroundColor="white"; div2.style.minHeight="70px"; div2.style.width="250px"; div2.style.borderRadius="10px"; div2.className="chart_text"; div1.className="head_img"; div1.appendChild(img); div2.appendChild(span); li.appendChild(div1); li.appendChild(div2); ul.appendChild(li); }; socket.onopen = function(event) { }; socket.onclose = function(event) { }; } else { alert("Your browser does not support Web Socket."); } } function sendContent() { var send_text = document.getElementById("send_text").value; socket.send(send_text); }
css/websocket.css
.whole_area{ position: absolute; width: 940px; height: 550px; left: 50%; top: 50%; margin-left: -450px; margin-top: -270px; /*background: darkgray;*/ } .chart_friend_area{ width: 120px; height: 550px; float: left; background: darkcyan; } .chart_area{ width: 560px; height: 550px; background: wheat; padding-left: 120px; } .chart_msg{ width: 560px; height: 370px; /* background: salmon;*/ overflow: auto; } .chart_msg ul{ list-style-type: none; margin:0; padding: 0; } .chart_msg ul li{ margin-top: 50px; border-radius: 2px; width: 400px; word-break:break-all } .head_img{ float: left; width: 35px; height: 35px; border-radius: 4px; overflow: hidden; } .chart_text{ word-break: break-all; margin-left: 60px; } .head_img img{ border-radius: 4px; width: 30px; height: 30px; } .send_msg{ width: 560px; height: 110px; background: cadetblue; } .face_icon{ width: 560px; height: 30px; background: saddlebrown; } .send_msg textarea{ width: 560px; height: 110px; font: "宋体"; } .send_clear{ width: 560px; height: 40px; background: sandybrown; } .send_but{ width: 90px; height: 30px; background: seagreen; float: right; margin-top: 5px; margin-right: 10px; text-align: center; line-height: 30px; border-radius: 5px; } .send_but:active{ } .send_but:hover{ background: seashell; cursor: pointer; } .friend_area{ width: 220px; height: 550px; background-color: darkcyan; margin-top: -550px; margin-left:680px; } .close_but{ width: 40px; height: 100px; background: skyblue; float: right; margin-top: -550px; }
websocket.html
<html> <head> <title>websocket</title> <script src="js/websocket.js"></script> <link href="css/websocket.css" rel="stylesheet" type="text/css"/> </head> <body> <div class="whole_area"> <!--左 --> <div class="chart_friend_area"> </div> <!--中,聊天内容显示区--> <div class="chart_area"> <div class="chart_msg"> <ul id="chart_msg"> </ul> </div> <div class="face_icon"> </div> <div class="send_msg"> <textarea id="send_text"></textarea> </div> <div class="send_clear"> <a class="send_but" onclick="sendContent()" accesskey="c"> 发送 </a> </div> </div> <!--右,好友列表区--> <div class="friend_area"> </div> <div class="close_but"> </div> </div> </body> </html>
客户端效果图:
原创博客地址:http://www.cnblogs.com/chaizezhao/articles/5291608.html