【WebSocket初探 】
众所周知,socket是编写网络通信应用的基本技术,网络数据交换大多直接或间接通过socket进行。对于直接使用socket的client与服务端,一旦连接被建立则均可主动向对方传送数据,而对于使用更上层的HTTP/HTTPS协议的应用,因为它们是非连接协议,所以通常仅仅能由client主动向服务端发送请求才干获得服务端的响应并取得相关的数据。而当前越来越多的应用希望可以及时获取服务端提供的数据,甚至希望可以达到接近实时的数据交换(比如非常多站点提供的在线客户系统)。为达到此目的,通常採用的技术主要有轮询、长轮询、流等,而伴随着HTML5的出现,相对更优异的WebSocket方案也应运而生。
一、 非WebSocket方案简单介绍
1. 轮询
轮询是由client定时向服务端发起查询数据的请求的一种实现方式。早期的轮询是通过不断自己主动刷新页面而实现的(在那个基本是IE统治浏览器的时代,那不断刷新页面产生的噪声就难以让人忍受),后来随着技术的发展,特别是Ajax技术的出现,实现了无刷新更新数据。但本质上这些方式均是client定时轮询服务端,这样的方式的最显著的缺点是假设client数量庞大而且定时轮询间隔较短服务端将承受响应这些client海量请求的巨大的压力。
2. 长轮询
在数据更新不够频繁的情况下,使用轮询方法获取数据时client常常会得到没有数据的响应,显然这样的轮询是一个浪费网络资源的无效的轮询。长轮询则是针对普通轮询的这样的缺陷的一种改进方案,其详细实现方式是假设当前请求没有数据能够返回,则继续保持当前请求的网络连接状态,直到服务端有数据能够返回或者连接超时。长轮询通过这样的方式降低了client与服务端交互的次数,避免了一些无谓的网络连接。可是假设数据变更较为频繁,则长轮询方式与普通轮询在性能上并无显著差异。同一时候,添加连接的等待时间,往往意味着并发性能的下降。
3. 流
所谓流是指client在页面之下向服务端发起一个长连接请求,服务端收到这个请求后响应它并不断更新连接状态,以确保这个连接在client与服务端之间一直有效。服务端能够通过这个连接将数据主动推送到client。显然,这样的方案实现起来相对照较麻烦,并且可能被防火墙阻断。
二、 WebSocket简单介绍
1. WebSocket协议简单介绍
WebSocket是为解决client与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,此后服务端与client通过此TCP连接进行实时通信。
WebSocket规范当前还没有正式版本号,草案变化也较为迅速。Tomcat7(本文中的例程来自7.0.42)当前支持RFC 6455(http://tools.ietf.org/html/rfc6455)定义的WebSocket,而RFC
6455眼下还未冻结,将来可能会修复一些Bug,甚至协议本身也可能会产生一些变化。
RFC6455定义的WebSocket协议由握手和传输数据两个部分组成。
来自client的握手信息类似例如以下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服务端的握手信息类似例如以下:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
一旦client和服务端都发送了握手信息而且成功握手,则传输数据部分将開始。传输数据对client和服务端而言都是一个双工通信通道,client和服务端来回传递的数据称之为“消息”。
client通过WebSocket URI发起WebSocket连接,WebSocket URIs模式定义例如以下:
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
ws是普通的WebSocket通信协议,而wss是安全的WebSocket通信协议(就像HTTP与HTTPS之间的差异一样)。在缺省情况下,ws的port是80而wss的port是443。
关于WebSocke协议规范的完整详尽说明,请參考RFC 6455。
2. Tomcat7提供的WebSocket包简单介绍
Tomcat7提供的与WebSocket相关的类均位于包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的实现包括于文件catalina.jar之中),它包括有类Constants、MessageInbound、StreamInbound、WebSocketServlet、WsFrame、WsHttpServletRequestWrapper、WsInputStream、WsOutbound。这些类的关系如图
1所看到的。
图1
包org.apache.catalina.websocket中的这些类为WebSocket开发服务端提供了支持,这些类的主要功能简述例如以下:
Constants:包org.apache.catalina.websocket中用到的常数定义在这个类中,它仅仅包括静态常数定义,无不论什么逻辑实现。
MessageInbound:基于消息的WebSocket实现类(带内消息),应用程序应当扩展这个类并实现其抽象方法onBinaryMessage和onTextMessage。
StreamInbound:基于流的WebSocket实现类(带内流),应用程序应当扩展这个类并实现其抽象方法onBinaryData和onTextData。
WebSocketServlet:提供遵循RFC6455的WebSocket连接的Servlet基本实现。client使用WebSocket连接服务端时,须要将WebSocketServlet的子类作为连接入口。同一时候,该子类应当实现WebSocketServlet的抽象方法createWebSocketInbound,以便创建一个inbound实例(MessageInbound或StreamInbound)。
WsFrame:代表完整的WebSocket框架。
WsHttpServletRequestWrapper:包装过的HttpServletRequest对象。
WsInputStream:基于WebSocket框架底层的socket的输入流。
WsOutbound:提供发送消息到client的功能。它提供的全部向client的写方法都是同步的,能够防止多线程同一时候向client写入数据。
三、 基于Tomcat7的WebSocket例程
利用当前HTML5和Tomcat7为WebSocket提供的支持,基本仅仅须要编写简单的代码对不同的事件做对应的逻辑处理就能够实现利用WebSocket进行实时通信了。
Tomcat7为WebSocket提供了3个例程(echo、chat及snake),下面就当中的echo和chat分别做一简要解析。
1. echo例程
echo例程主要演示下面功能:client连接服务端、client向服务端发送消息、服务端收到client发送的消息后将其原样返回给client、client收到消息后将其显示在网页之上。
在client页面选择streams或messages作为“Connectusing”,然后点击“Connect”button,能够在右側窗体看到WebSocket连接打开的消息。随后点击“Echo
message”button,client将向服务端发送一条消息,在右側窗体,能够看到,消息发出的后的瞬间,client已经收到了服务端原样返回的消息。
client页面及执行效果截图如图 2所看到的。
图2
client实现上述功能的核心脚本例如以下,其关键点通过凝视的形式加以说明:
<script type="text/javascript">
var ws = null;
// 界面元素可用性控制
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('echo').disabled = !connected;
} function connect() {
// 取得WebSocket连接入口(WebSocket URI)
var target = document.getElementById('target').value;
if (target == '') {
alert('Please select server side connection implementation.');
return;
}
// 创建WebSocket
if ('WebSocket' in window) {
ws = new WebSocket(target);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
} else {
alert('WebSocket is not supported by this browser.');
return;
}
// 定义Open事件处理函数
ws.onopen = function () {
setConnected(true);
log('Info: WebSocket connection opened.');
};
// 定义Message事件处理函数(收取服务端消息并处理)
ws.onmessage = function (event) {
log('Received: ' + event.data);
};
// 定义Close事件处理函数
ws.onclose = function () {
setConnected(false);
log('Info: WebSocket connection closed.');
};
}
// 关闭WebSocket连接
function disconnect() {
if (ws != null) {
ws.close();
ws = null;
}
setConnected(false);
} function echo() {
if (ws != null) {
var message = document.getElementById('message').value;
log('Sent: ' + message);
// 向服务端发送消息
ws.send(message);
} else {
alert('WebSocket connection not established, please connect.');
}
}
// 生成WebSocket URI
function updateTarget(target) {
if (window.location.protocol == 'http:') {
document.getElementById('target').value =
'ws://' + window.location.host + target;
} else {
document.getElementById('target').value =
'wss://' + window.location.host + target;
}
}
// 在界面显示log及消息
function log(message) {
var console = document.getElementById('console');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
console.appendChild(p);
while (console.childNodes.length > 25) {
console.removeChild(console.firstChild);
}
console.scrollTop = console.scrollHeight;
}
</script>
注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\echo.html
以上client能够依据“Connectusing”的不同选择连接不同的服务端WebSocket。比如messages选项相应的服务端代码例如以下,其核心逻辑是在收到client发来的消息后马上将其发回client。
public class EchoMessage extends WebSocketServlet {
private static final long serialVersionUID = 1L;
private volatile int byteBufSize;
private volatile int charBufSize; @Override
public void init() throws ServletException {
super.init();
byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);
charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);
} public int getInitParameterIntValue(String name, int defaultValue) {
String val = this.getInitParameter(name);
int result;
if(null != val) {
try {
result = Integer.parseInt(val);
}catch (Exception x) {
result = defaultValue;
}
} else {
result = defaultValue;
} return result;
} // 创建Inbound实例,WebSocketServlet子类必须实现的方法
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest request) {
return new EchoMessageInbound(byteBufSize,charBufSize);
}
// MessageInbound子类,完毕收到WebSocket消息后的逻辑处理
private static final class EchoMessageInbound extends MessageInbound {
public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {
super();
setByteBufferMaxSize(byteBufferMaxSize);
setCharBufferMaxSize(charBufferMaxSize);
}
// 二进制消息响应
@Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
getWsOutbound().writeBinaryMessage(message);
}
// 文本消息响应
@Override
protected void onTextMessage(CharBuffer message) throws IOException {
// 将收到的消息发回client
getWsOutbound().writeTextMessage(message);
}
}
}
注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\EchoMessage.java。
2. chat例程
chat例程实现了通过网页进行群聊的功能。每一个打开的聊天网页都能够收到全部在线者发出的消息,同一时候,每一个在线者也都能够(也仅仅能够)向其他全部人发送消息。也就是说,chat实例演示了怎样通过WebSocket实现对全部在线client的广播。
图3
chat例程client核心代码例如以下,能够看到事实上现方式与echo例程形式上稍有变化,本质依然是对WebSocket事件进行响应与处理。
<script type="text/javascript">
var Chat = {}; Chat.socket = null; Chat.connect = (function(host) {
// 创建WebSocket
if ('WebSocket' in window) {
Chat.socket = new WebSocket(host);
} else if ('MozWebSocket' in window) {
Chat.socket = new MozWebSocket(host);
} else {
Console.log('Error: WebSocket is not supported by this browser.');
return;
}
// 定义Open事件处理函数
Chat.socket.onopen = function () {
Console.log('Info: WebSocket connection opened.');
document.getElementById('chat').onkeydown = function(event) {
if (event.keyCode == 13) {
Chat.sendMessage();
}
};
};
// 定义Close事件处理函数
Chat.socket.onclose = function () {
document.getElementById('chat').onkeydown = null;
Console.log('Info: WebSocket closed.');
};
// 定义Message事件处理函数
Chat.socket.onmessage = function (message) {
Console.log(message.data);
};
}); Chat.initialize = function() {
if (window.location.protocol == 'http:') {
Chat.connect('ws://' +
window.location.host + '/examples/websocket/chat');
} else {
Chat.connect('wss://' +
window.location.host + '/examples/websocket/chat');
}
};
// 发送消息至服务端
Chat.sendMessage = (function() {
var message = document.getElementById('chat').value;
if (message != '') {
Chat.socket.send(message);
document.getElementById('chat').value = '';
}
}); var Console = {}; Console.log = (function(message) {
var console = document.getElementById('console');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.innerHTML = message;
console.appendChild(p);
while (console.childNodes.length > 25) {
console.removeChild(console.firstChild);
}
console.scrollTop = console.scrollHeight;
}); Chat.initialize(); </script>
注:完整代码參见apache-tomcat-7.0.42\webapps\examples\websocket\chat.html
public class ChatWebSocketServlet extends WebSocketServlet { private static final long serialVersionUID = 1L; private static final String GUEST_PREFIX = "Guest"; private final AtomicInteger connectionIds = new AtomicInteger(0);
private final Set<ChatMessageInbound> connections =
new CopyOnWriteArraySet<ChatMessageInbound>();
// 创建Inbound实例,WebSocketServlet子类必须实现的方法
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest request) {
return new ChatMessageInbound(connectionIds.incrementAndGet());
}
// MessageInbound子类,完毕收到WebSocket消息后的逻辑处理
private final class ChatMessageInbound extends MessageInbound { private final String nickname; private ChatMessageInbound(int id) {
this.nickname = GUEST_PREFIX + id;
}
// Open事件
@Override
protected void onOpen(WsOutbound outbound) {
connections.add(this);
String message = String.format("* %s %s",
nickname, "has joined.");
broadcast(message);
}
// Close事件
@Override
protected void onClose(int status) {
connections.remove(this);
String message = String.format("* %s %s",
nickname, "has disconnected.");
broadcast(message);
}
// 二进制消息事件
@Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
throw new UnsupportedOperationException(
"Binary message not supported.");
}
// 文本消息事件
@Override
protected void onTextMessage(CharBuffer message) throws IOException {
// Never trust the client
String filteredMessage = String.format("%s: %s",
nickname, HTMLFilter.filter(message.toString()));
broadcast(filteredMessage);
}
// 向全部已连接的客户端发送文本消息(广播)
private void broadcast(String message) {
for (ChatMessageInbound connection : connections) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
connection.getWsOutbound().writeTextMessage(buffer);
} catch (IOException ignore) {
// Ignore
}
}
}
}
注:完整代码參见apache-tomcat-7.0.42\webapps\examples\WEB-INF\classes\websocket\echo\ChatWebSocketServlet.java。
通过上述例程能够看到WebSocket广播实际上是通过遍历全部连接并通过每一个连接向对应的client发送消息实现的。
四、 WebSocket实战
实时向在线用户推送通知是一个WebSocket应用的简单场景,后台提交通知信息以后,所在在线用户均应非常快收到这个通知。通过上述例程了解WebSocket后,能够尝试编写一个实现这个需求的WebSocket应用。
首先编写一个用户的Sample页面,该页面没有实质的内容,可是在收到后台发出的通知时要在右下角通过弹窗显示通知的内容。其代码例如以下:
<!DOCTYPE html>
<html>
<head>
<title>Receive Message</title>
<style type="text/css">
#winpop { width:200px; height:0px;
position:absolute;
right:0; bottom:0;
border:1px solid #999999;
margin:0;
padding:1px;
overflow:hidden;
display:none;
background:#FFFFFF}
#winpop .con { width:100%; height:80px;
line-height:80px;
font-weight:bold;
font-size:12px;
color:#FF0000;
text-align:center}
</style>
<script type="text/javascript">
// 弹窗相关
function tips_pop(){
var MsgPop=document.getElementById("winpop");
var popH=parseInt(MsgPop.style.height); if(isNaN(popH)) {
popH = 0;
} if (popH==0){
MsgPop.style.display="block";
show=setInterval("changeH('up')",100);
}
else {
hide=setInterval("changeH('down')",100);
}
} function changeH(str) {
var MsgPop=document.getElementById("winpop");
var popH=parseInt(MsgPop.style.height); if(isNaN(popH)) {
popH = 0;
} if(str=="up"){
if (popH<=100){
MsgPop.style.height=(popH+4).toString()+"px";
}
else{
clearInterval(show);
setTimeout("tips_pop()", 5000);
}
}
if(str=="down"){
if (popH>=4){
MsgPop.style.height=(popH-4).toString()+"px";
}
else{
clearInterval(hide);
MsgPop.style.display="none";
}
}
} // WebSocket相关
var ws = null; function connect() {
var target = 'ws://' + window.location.host
+ "/test/NotifyWebSocketServlet"; if ('WebSocket' in window) {
ws = new WebSocket(target);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
} else {
alert('WebSocket is not supported by this browser.');
return;
} ws.onopen = function () {
document.getElementById('msg').innerHTML =
"WebSocket has opened, Waiting message.......";
}; ws.onmessage = function (event) {
document.getElementById('infomsg').innerHTML = event.data;
tips_pop();
}; ws.onclose = function () {
document.getElementById('msg').innerHTML = "WebSocket has closed";
};
} function disconnect() {
if (ws != null) {
ws.close();
ws = null;
}
} connect();
</script>
</head>
<body>
<h1 align="center" id="msg">Try to connect websocket.</h1>
<div id="winpop">
<div class="con" id="infomsg"></div>
</div>
</body>
</html>
当用户界面打开时,它会尝试通过/test/NotifyWebSocketServlet建立与server的WebSocket连接,而NotifyWebSocketServlet的实现代码则例如以下:
package net.yanzhijun.example; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet; public class NotifyWebSocketServlet extends WebSocketServlet { private static final long serialVersionUID = 1L; @Override
protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest request) {
ServletContext application = this.getServletContext();
return new NofityMessageInbound(application);
}
}
与Tomcat给出的演示样例代码不同的是,在NotifyWebSocketServlet中并未将继承于MessageInbound的NofityMessageInbound作为一个内嵌类。前述演示样例代码中发送消息和接收消息都是在同一组client页面和服务端响应Servlet间进行的,而当前须要实现是在一个页面中提交通知,而在其他用户的页面上显示通知信息,因此须要将全部client与服务端的连接存储一个全局域中,故而NofityMessageInbound将不仅仅在当前Servlet中被使用,所以有必要将其独立出来。
NofityMessageInbound的完整代码例如以下:
package net.yanzhijun.example; import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import javax.servlet.ServletContext; import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.MessageInbound; public class NofityMessageInbound extends MessageInbound {
private ServletContext application;
private Set<NofityMessageInbound> connections = null; public NofityMessageInbound(ServletContext application) {
this.application = application;
connections =
(Set<NofityMessageInbound>)application.getAttribute("connections");
if(connections == null) {
connections =
new CopyOnWriteArraySet<NofityMessageInbound>();
}
} @Override
protected void onOpen(WsOutbound outbound) {
connections.add(this);
application.setAttribute("connections", connections);
} @Override
protected void onClose(int status) {
connections.remove(this);
application.setAttribute("connections", connections);
} @Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
throw new UnsupportedOperationException(
"message not supported.");
} @Override
protected void onTextMessage(CharBuffer message) throws IOException {
throw new UnsupportedOperationException(
"message not supported.");
}
}
后台发送通知的页面实现的相当简单,仅仅是一个表单提交一条通知信息。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>PushMessage</title>
</head>
<body>
<h1 align="Center">Online Broadcast</h1>
<form method="post" action="PushMessageServlet">
<p>Message:<br/>
<textarea name="message" rows="5" cols="30"></textarea>
</p>
<p><input type="submit" value="Send">
<input type="reset" value="Reset">
</p>
</form>
</body>
</html>
接收提交通知的Servlet是PushMessageServlet,它在收到后台提交的通知后,就通过全部用户的WebSocket连接将通知发送出去。
package net.yanzhijun.example; import java.io.PrintWriter;
import java.nio.CharBuffer;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.io.IOException; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class PushMessageServlet extends HttpServlet {
private static final long serialVersionUID = 1L; @Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
doPost(request, response);
} @Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException { request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String message = request.getParameter("message");
if(message == null || message.length() == 0) {
out.println("The message is empty!");
return;
} // 广播消息
broadcast(message); out.println("Send success!");
} // 将參数中的消息发送至全部在线client
private void broadcast(String message) {
ServletContext application=this.getServletContext();
Set<NofityMessageInbound> connections =
(Set<NofityMessageInbound>)application.getAttribute("connections");
if(connections == null){
return;
} for (NofityMessageInbound connection : connections) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
connection.getWsOutbound().writeTextMessage(buffer);
} catch (IOException ignore) {
// Ignore
}
}
}
}
编译相关文件并完毕部署,尝试在后台发送消息,能够看到用户界面右下角出现的弹窗中显示了后台所提交的内容。
五、 WebSocket总结
通过以上例程和实例能够看出,从开发角度使用WebSocket相当easy,基本仅仅须要创建WebSocket实例并对关心的事件进行处理就能够了;从应用角度WebSocket提供了优异的性能,图 5是来自websocket.org的性能測试图表(http://www.websocket.org/quantum.html),能够看到当并发和负载添加时轮询与WebSocket的差异。
【WebSocket初探 】的更多相关文章
- Spring WebSocket初探2 (Spring WebSocket入门教程)<转>
See more: Spring WebSocket reference整个例子属于WiseMenuFrameWork的一部分,可以将整个项目Clone下来,如果朋友们有需求,我可以整理一个独立的de ...
- websocket初探
本文尚未完成,在此只写一些句子,以后慢慢整理. 一.参数 IllegalArgumentException No payload parameter present on the method[mes ...
- Spring WebSocket初探1 (Spring WebSocket入门教程)<转>
See more: Spring WebSocket reference整个例子属于WiseMenuFrameWork的一部分,可以将整个项目Clone下来,如果朋友们有需求,我可以整理一个独立的de ...
- 初探WebSocket
初探WebSocket node websocket socket.io 我们平常开发的大部分web页面都是主动'拉'的形式,如果需要更新页面内容,则需要"刷新"一个,但Slack ...
- 初探和实现websocket心跳重连
心跳重连缘由 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开, 而浏览器不会执行websocket 的 onclos ...
- Lua编写wireshark插件初探——解析Websocket上的MQTT协议
一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...
- 初探和实现websocket心跳重连(npm: websocket-heartbeat-js)
提示:文章最下方有仓库地址 心跳重连缘由 websocket是前后端交互的长连接,前后端也都可能因为一些情况导致连接失效并且相互之间没有反馈提醒.因此为了保证连接的可持续性和稳定性,websocket ...
- WebSocket集成XMPP网页即时通讯1:Java Web Project服务端/客户端Jetty9开发初探
Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事, ...
- 网页实时聊天之PHP实现websocket
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...
随机推荐
- css Tab选项卡2
注意上述 红色方框 这个是锚点的变相 以及overflow:hiden结合, 利用 锚点对应 id , 也可以实现. 兼容ie6+ 适合手机tab 简单 不需要脚本 其实还可以利用 ...
- Python网络02 Python服务器进化
原文:Python网络02 Python服务器进化 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3. ...
- Doug Lea
如果IT的历史,是以人为主体串接起来的话,那么肯定少不了Doug Lea.这个鼻梁挂着眼镜,留着德王威廉二世的胡子,脸上永远挂着谦逊腼腆笑容,服务于纽约州立大学Oswego分校计算机科学系的老大爷. ...
- Cordova 使用经验
1. 需要下载ant,ant需要的文件: build.xml <?xml version="1.0" ?> <project name ="antPro ...
- 数据库文档生成工具——word2chm,SqlSpec
首先使用代码生成器可以生成word版本的数据库文档. 转成chm格式的更加小巧和方便~ SqlSpec是个好工具,可以生成所有数据库相关的信息 之后可以一键生成chm文档.
- Cannot update paths and switch to branch at the same time(转)
当使用git进行操作: git checkout -b local-name origin/remote-name 出现错误: fatal: git checkout: updating paths ...
- Drupal 7.31 SQL注射分析POC
此漏洞昨日爆发 ,我们有时间去看看今天的代码. 于Drupal于,跑sql声明使用PDO型号,这是一般能够避免大部分的注射,由于使用占位符的sql语法语句是限制. 但是,这并不意味着绝对安全,. 在D ...
- httppost body的实现, 和body是gb2312等编码的实现
使用的是http4.X 版本,里面推荐使用的post是key value的形式 List<NameValuePair> formparams = new ArrayList<Name ...
- VC各种方法获得的窗口句柄
AfxGetMainWnd AfxGetMainWnd获取窗口句柄本身 HWND hWnd = AfxGetMainWnd()->m_hWnd; GetTopWindow 功能:子窗体z序(Z序 ...
- python_在windows下安装配置python开发环境及Ulipad开发工具
最近开始学习Python,在网上寻找一下比较好的IDE.因为以前用C#做开发的,用Visual Studio作为IDE,鉴于用惯了VS这么强大的IDE,所以对IDE有一定的依赖性. Python的ID ...