什么是WebSocket?看过html5的同学都知道,WebSocket protocol 是HTML5一种新的协议。它是实现了浏览器与服务器全双工通信(full-duplex)。HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。现在我们来探讨一下html5的WebSocket
 

概念

HTML5作为下一代WEB标准,拥有许多引人注目的新特性,如Canvas、本地存储、多媒体编程接口、WebSocket 等等。今天我们就来看看具有“Web TCP”之称的WebSocket.

WebSocket的出现是基于Web应用的实时性需要而产生的。这种实时的Web应用大家应该不陌生,在生活中都应该用到过,比如新浪微博的评论、私信的通知,腾讯的WebQQ等。让我们来回顾下实时 Web 应用的窘境吧。

在WebSocket出现之前,一般通过两种方式来实现Web实时用:轮询机制和流技术;其中轮询有不同的轮询,还有一种叫Comet的长轮询。

轮询:这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的缺点是,当客户端以固定频率向服务 器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。

长轮询:是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者 时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提 高。

流:常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务 器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进 用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。

上述方式其实并不是真正的实时技术,只是使用了一种技巧来实现的模拟实时。在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量。但这些方式最痛苦的是开发人员,因为不论客户端还是服务器端的实现都很复杂,为了模拟比较真实的实时效果,开发人员 往往需要构造两个HTTP连接来模拟客户端和服务器之间的双向通讯,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数 据传输,这不可避免地增加了编程实现的复杂度,也增加了服务器端的负载,制约了应用系统的扩展性。

基于上述弊端,实现Web实时应用的技术出现了,WebSocket通过浏览器提供的API真正实现了具备像C/S架构下的桌面系统的实时通讯能 力。其原理是使用JavaScript调用浏览器的API发出一个WebSocket请求至服务器,经过一次握手,和服务器建立了TCP通讯,因为它本质 上是一个TCP连接,所以数据传输的稳定性强和数据传输量比较小。

WebSocket 协议

WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

下面我们来详细介绍一下 WebSocket 协议,由于这个协议目前还是处于草案阶段,版本的变化比较快,我们选择目前最新的 draft-ietf-hybi-thewebsocketprotocol-17 版本来描述 WebSocket 协议。因为这个版本目前在一些主流的浏览器上比如 Chrome,、FireFox、Opera 上都得到比较好的支持。通过描述可以看到握手协议

客户端发到服务器的内容:

 代码如下 复制代码

    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

这些请求和通常的 HTTP 请求很相似,但是其中有些内容是和 WebSocket 协议密切相关的。我们需要简单介绍一下这些请求和应答信息,”Upgrade:WebSocket”表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。其中客户端的Sec-WebSocket-Key和服务器端的Sec-WebSocket-Accept就是重要的握手认证信息了,

关键是服务器端Sec-WebSocket-Accept,它是根据Sec-WebSocket-Key计算出来的:

取出Sec-WebSocket-Key,与一个magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 连接成一个新的key串;
将新的key串SHA1编码,生成一个由多组两位16进制数构成的加密串;
把加密串进行base64编码生成最终的key,这个key就是Sec-WebSocket-Key;

实例代码如下:

 代码如下 复制代码

/// <summary>
/// 生成Sec-WebSocket-Accept
/// </summary>
/// <param name="handShakeText">客户端握手信息</param>
/// <returns>Sec-WebSocket-Accept</returns>
private static string GetSecKeyAccetp(byte[] handShakeBytes,int bytesLength)
{
string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
string key = string.Empty;
Regex r = new Regex(@"Sec-WebSocket-Key:(.*?)rn");
Match m = r.Match(handShakeText);
if (m.Groups.Count != 0)
{
key = Regex.Replace(m.Value, @"Sec-WebSocket-Key:(.*?)rn", "$1").Trim();
}
byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
return Convert.ToBase64String(encryptionString);
}

如果握手成功,将会触发客户端的onopen事件。

解析接收的客户端信息

接收到客户端数据解析规则如下:

1byte
bit: frame-fin,x0表示该message后续还有frame;x1表示是message的最后一个frame
3bit: 分别是frame-rsv1、frame-rsv2和frame-rsv3,通常都是x0
4bit: frame-opcode,x0表示是延续frame;x1表示文本frame;x2表示二进制frame;x3-7保留给非控制frame;x8表示关 闭连接;x9表示ping;xA表示pong;xB-F保留给控制frame
2byte
1bit: Mask,1表示该frame包含掩码;0,表示无掩码
7bit、7bit+2byte、7bit+8byte: 7bit取整数值,若在0-125之间,则是负载数据长度;若是126表示,后两个byte取无符号16位整数值,是负载长度;127表示后8个 byte,取64位无符号整数值,是负载长度
3-6byte: 这里假定负载长度在0-125之间,并且Mask为1,则这4个byte是掩码
7-end byte: 长度是上面取出的负载长度,包括扩展数据和应用数据两部分,通常没有扩展数据;若Mask为1,则此数据需要解码,解码规则为1-4byte掩码循环和数据byte做异或操作。

解析代码如下,但没有处理多帧和不包含掩码的包:

 代码如下 复制代码

/// <summary>
/// 解析客户端数据包
/// </summary>
/// <param name="recBytes">服务器接收的数据包</param>
/// <param name="recByteLength">有效数据长度</param>
/// <returns></returns>
private static string AnalyticData(byte[] recBytes, int recByteLength)
{
if (recByteLength < 2) { return string.Empty; }
 
bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧
if (!fin){
return string.Empty;// 超过一帧暂不处理
}
 
bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码
if (!mask_flag){
return string.Empty;// 不包含掩码的暂不处理
}
 
int payload_len = recBytes[1] & 0x7F; // 数据长度
 
byte[] masks = new byte[4];
byte[] payload_data;
 
if (payload_len == 126){
Array.Copy(recBytes, 4, masks, 0, 4);
payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 8, payload_data, 0, payload_len);
 
}else if (payload_len == 127){
Array.Copy(recBytes, 10, masks, 0, 4);
byte[] uInt64Bytes = new byte[8];
for (int i = 0; i < 8; i++){
uInt64Bytes[i] = recBytes[9 - i];
}
UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
 
payload_data = new byte[len];
for (UInt64 i = 0; i < len; i++){
payload_data[i] = recBytes[i + 14];
}
}else{
Array.Copy(recBytes, 2, masks, 0, 4);
payload_data = new byte[payload_len];
Array.Copy(recBytes, 6, payload_data, 0, payload_len);
 
}
 
for (var i = 0; i < payload_len; i++){
payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
}
return Encoding.UTF8.GetString(payload_data);
}

发送数据至客户端

服务器发送的数据以0x81开头,紧接发送内容的长度(若长度在0-125,则1个byte表示长度;若长度不超过0xFFFF,则后2个byte 作为无符号16位整数表示长度;若超过0xFFFF,则后8个byte作为无符号64位整数表示长度),最后是内容的byte数组。

代码如下:

/// <summary>
/// 打包服务器数据
/// </summary>
/// <param name="message">数据</param>
/// <returns>数据包</returns>
private static byte[] PackData(string message)
{
byte[] contentBytes = null;
byte[] temp = Encoding.UTF8.GetBytes(message);
 
if (temp.Length < 126){
contentBytes = new byte[temp.Length + 2];
contentBytes[0] = 0x81;
contentBytes[1] = (byte)temp.Length;
Array.Copy(temp, 0, contentBytes, 2, temp.Length);
}else if (temp.Length < 0xFFFF){
contentBytes = new byte[temp.Length + 4];
contentBytes[0] = 0x81;
contentBytes[1] = 126;
contentBytes[2] = (byte)(temp.Length & 0xFF);
contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
Array.Copy(temp, 0, contentBytes, 4, temp.Length);
}else{
// 暂不处理超长内容

return contentBytes;

}

websocket两种实现方式

1.两种方式,一种使用tomcat的websocket实现,一种使用spring的websocket

2.tomcat的方式需要tomcat 7.x,JEE7的支持。

3.spring与websocket整合需要spring 4.x,并且使用了socketjs,对不支持websocket的浏览器可以模拟websocket使用

方式一:tomcat

使用这种方式无需别的任何配置,只需服务端一个处理类,

服务器端代码

[java]
  1. package com.Socket;
  2. import java.io.IOException;
  3. import java.util.Map;
  4. import java.util.concurrent.ConcurrentHashMap;
  5. import javax.websocket.*;
  6. import javax.websocket.server.PathParam;
  7. import javax.websocket.server.ServerEndpoint;
  8. import net.sf.json.JSONObject;
  9. @ServerEndpoint("/websocket/{username}")
  10. public class WebSocket {
  11. private static int onlineCount = 0;
  12. private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
  13. private Session session;
  14. private String username;
  15. @OnOpen
  16. public void onOpen(@PathParam("username") String username, Session session) throws IOException {
  17. this.username = username;
  18. this.session = session;
  19. addOnlineCount();
  20. clients.put(username, this);
  21. System.out.println("已连接");
  22. }
  23. @OnClose
  24. public void onClose() throws IOException {
  25. clients.remove(username);
  26. subOnlineCount();
  27. }
  28. @OnMessage
  29. public void onMessage(String message) throws IOException {
  30. JSONObject jsonTo = JSONObject.fromObject(message);
  31. if (!jsonTo.get("To").equals("All")){
  32. sendMessageTo("给一个人", jsonTo.get("To").toString());
  33. }else{
  34. sendMessageAll("给所有人");
  35. }
  36. }
  37. @OnError
  38. public void onError(Session session, Throwable error) {
  39. error.printStackTrace();
  40. }
  41. public void sendMessageTo(String message, String To) throws IOException {
  42. // session.getBasicRemote().sendText(message);
  43. //session.getAsyncRemote().sendText(message);
  44. for (WebSocket item : clients.values()) {
  45. if (item.username.equals(To) )
  46. item.session.getAsyncRemote().sendText(message);
  47. }
  48. }
  49. public void sendMessageAll(String message) throws IOException {
  50. for (WebSocket item : clients.values()) {
  51. item.session.getAsyncRemote().sendText(message);
  52. }
  53. }
  54. public static synchronized int getOnlineCount() {
  55. return onlineCount;
  56. }
  57. public static synchronized void addOnlineCount() {
  58. WebSocket.onlineCount++;
  59. }
  60. public static synchronized void subOnlineCount() {
  61. WebSocket.onlineCount--;
  62. }
  63. public static synchronized Map<String, WebSocket> getClients() {
  64. return clients;
  65. }
  66. }

客户端js

[javascript]
  1. var websocket = null;
  2. var username = localStorage.getItem("name");
  3. //判断当前浏览器是否支持WebSocket
  4. if ('WebSocket' in window) {
  5. websocket = new WebSocket("ws://" + document.location.host + "/WebChat/websocket/" + username + "/"+ _img);
  6. } else {
  7. alert('当前浏览器 Not support websocket')
  8. }
  9. //连接发生错误的回调方法
  10. websocket.onerror = function() {
  11. setMessageInnerHTML("WebSocket连接发生错误");
  12. };
  13. //连接成功建立的回调方法
  14. websocket.onopen = function() {
  15. setMessageInnerHTML("WebSocket连接成功");
  16. }
  17. //接收到消息的回调方法
  18. websocket.onmessage = function(event) {
  19. setMessageInnerHTML(event.data);
  20. }
  21. //连接关闭的回调方法
  22. websocket.onclose = function() {
  23. setMessageInnerHTML("WebSocket连接关闭");
  24. }
  25. //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
  26. window.onbeforeunload = function() {
  27. closeWebSocket();
  28. }
  29. //关闭WebSocket连接
  30. function closeWebSocket() {
  31. websocket.close();
  32. }

发送消息只需要使用websocket.send("发送消息"),就可以触发服务端的onMessage()方法,当连接时,触发服务器端onOpen()方法,此时也可以调用发送消息的方法去发送消息。关闭websocket时,触发服务器端onclose()方法,此时也可以发送消息,但是不能发送给自己,因为自己的已经关闭了连接,但是可以发送给其他人。

方法二:spring整合

此方式基于spring mvc框架,相关配置可以看我的相关博客文章

WebSocketConfig.java

这个类是配置类,所以需要在spring mvc配置文件中加入对这个类的扫描,第一个addHandler是对正常连接的配置,第二个是如果浏览器不支持websocket,使用socketjs模拟websocket的连接。

[java]
  1. package com.websocket;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.socket.config.annotation.EnableWebSocket;
  5. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
  6. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
  7. import org.springframework.web.socket.handler.TextWebSocketHandler;
  8. @Configuration
  9. @EnableWebSocket
  10. public class WebSocketConfig implements WebSocketConfigurer {
  11. @Override
  12. public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  13. registry.addHandler(chatMessageHandler(),"/webSocketServer").addInterceptors(new ChatHandshakeInterceptor());
  14. registry.addHandler(chatMessageHandler(), "/sockjs/webSocketServer").addInterceptors(new ChatHandshakeInterceptor()).withSockJS();
  15. }
  16. @Bean
  17. public TextWebSocketHandler chatMessageHandler(){
  18. return new ChatMessageHandler();
  19. }
  20. }

ChatHandshakeInterceptor.java

这个类的作用就是在连接成功前和成功后增加一些额外的功能,Constants.java类是一个工具类,两个常量。

[java]
  1. package com.websocket;
  2. import java.util.Map;
  3. import org.apache.shiro.SecurityUtils;
  4. import org.springframework.http.server.ServerHttpRequest;
  5. import org.springframework.http.server.ServerHttpResponse;
  6. import org.springframework.web.socket.WebSocketHandler;
  7. import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
  8. public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
  9. @Override
  10. public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
  11. Map<String, Object> attributes) throws Exception {
  12. System.out.println("Before Handshake");
  13. /*
  14. * if (request instanceof ServletServerHttpRequest) {
  15. * ServletServerHttpRequest servletRequest = (ServletServerHttpRequest)
  16. * request; HttpSession session =
  17. * servletRequest.getServletRequest().getSession(false); if (session !=
  18. * null) { //使用userName区分WebSocketHandler,以便定向发送消息 String userName =
  19. * (String) session.getAttribute(Constants.SESSION_USERNAME); if
  20. * (userName==null) { userName="default-system"; }
  21. * attributes.put(Constants.WEBSOCKET_USERNAME,userName);
  22. *
  23. * } }
  24. */
  25. //使用userName区分WebSocketHandler,以便定向发送消息(使用shiro获取session,或是使用上面的方式)
  26. String userName = (String) SecurityUtils.getSubject().getSession().getAttribute(Constants.SESSION_USERNAME);
  27. if (userName == null) {
  28. userName = "default-system";
  29. }
  30. attributes.put(Constants.WEBSOCKET_USERNAME, userName);
  31. return super.beforeHandshake(request, response, wsHandler, attributes);
  32. }
  33. @Override
  34. public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
  35. Exception ex) {
  36. System.out.println("After Handshake");
  37. super.afterHandshake(request, response, wsHandler, ex);
  38. }
  39. }

ChatMessageHandler.java

这个类是对消息的一些处理,比如是发给一个人,还是发给所有人,并且前端连接时触发的一些动作

[java]
  1. package com.websocket;
  2. import java.io.IOException;
  3. import java.util.ArrayList;
  4. import org.apache.log4j.Logger;
  5. import org.springframework.web.socket.CloseStatus;
  6. import org.springframework.web.socket.TextMessage;
  7. import org.springframework.web.socket.WebSocketSession;
  8. import org.springframework.web.socket.handler.TextWebSocketHandler;
  9. public class ChatMessageHandler extends TextWebSocketHandler {
  10. private static final ArrayList<WebSocketSession> users;// 这个会出现性能问题,最好用Map来存储,key用userid
  11. private static Logger logger = Logger.getLogger(ChatMessageHandler.class);
  12. static {
  13. users = new ArrayList<WebSocketSession>();
  14. }
  15. /**
  16. * 连接成功时候,会触发UI上onopen方法
  17. */
  18. @Override
  19. public void afterConnectionEstablished(WebSocketSession session) throws Exception {
  20. System.out.println("connect to the websocket success......");
  21. users.add(session);
  22. // 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户
  23. // TextMessage returnMessage = new TextMessage("你将收到的离线");
  24. // session.sendMessage(returnMessage);
  25. }
  26. /**
  27. * 在UI在用js调用websocket.send()时候,会调用该方法
  28. */
  29. @Override
  30. protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
  31. sendMessageToUsers(message);
  32. //super.handleTextMessage(session, message);
  33. }
  34. /**
  35. * 给某个用户发送消息
  36. *
  37. * @param userName
  38. * @param message
  39. */
  40. public void sendMessageToUser(String userName, TextMessage message) {
  41. for (WebSocketSession user : users) {
  42. if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {
  43. try {
  44. if (user.isOpen()) {
  45. user.sendMessage(message);
  46. }
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. break;
  51. }
  52. }
  53. }
  54. /**
  55. * 给所有在线用户发送消息
  56. *
  57. * @param message
  58. */
  59. public void sendMessageToUsers(TextMessage message) {
  60. for (WebSocketSession user : users) {
  61. try {
  62. if (user.isOpen()) {
  63. user.sendMessage(message);
  64. }
  65. } catch (IOException e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. }
  70. @Override
  71. public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
  72. if (session.isOpen()) {
  73. session.close();
  74. }
  75. logger.debug("websocket connection closed......");
  76. users.remove(session);
  77. }
  78. @Override
  79. public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
  80. logger.debug("websocket connection closed......");
  81. users.remove(session);
  82. }
  83. @Override
  84. public boolean supportsPartialMessages() {
  85. return false;
  86. }
  87. }

spring-mvc.xml

正常的配置文件,同时需要增加对WebSocketConfig.java类的扫描,并且增加

[html]
  1. xmlns:websocket="http://www.springframework.org/schema/websocket"
  2. http://www.springframework.org/schema/websocket
  3. <a target="_blank" href="http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd</a>

客户端

[html]
  1. <script type="text/javascript"
  2. src="http://localhost:8080/Bank/js/sockjs-0.3.min.js"></script>
  3. <script>
  4. var websocket;
  5. if ('WebSocket' in window) {
  6. websocket = new WebSocket("ws://" + document.location.host + "/Bank/webSocketServer");
  7. } else if ('MozWebSocket' in window) {
  8. websocket = new MozWebSocket("ws://" + document.location.host + "/Bank/webSocketServer");
  9. } else {
  10. websocket = new SockJS("http://" + document.location.host + "/Bank/sockjs/webSocketServer");
  11. }
  12. websocket.onopen = function(evnt) {};
  13. websocket.onmessage = function(evnt) {
  14. $("#test").html("(<font color='red'>" + evnt.data + "</font>)")
  15. };
  16. websocket.onerror = function(evnt) {};
  17. websocket.onclose = function(evnt) {}
  18. $('#btn').on('click', function() {
  19. if (websocket.readyState == websocket.OPEN) {
  20. var msg = $('#id').val();
  21. //调用后台handleTextMessage方法
  22. websocket.send(msg);
  23. } else {
  24. alert("连接失败!");
  25. }
  26. });
  27. </script>

注意导入socketjs时要使用地址全称,并且连接使用的是http而不是websocket的ws

websocket 介绍及实现相关推荐

  1. WebSocket介绍和Socket的区别

    WebSocket介绍与原理 WebSocket protocol 是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(full-duplex).一开始的握手需要借助HTTP)请求完成. -- ...

  2. webSocket介绍及项目实战【在线聊天系统】

    文章目录 一:消息推送常用方式介绍 1.1 轮询:浏览器以指定的时间间隔向服务器发出HTTP请求,服务器实时返回数据给浏览器 1.2 长轮询:浏览器发出ajax请求,服务器端接收到请求后,会阻塞请求直 ...

  3. WebSocket介绍

    WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex ...

  4. apache 配置 wss websocket打开握手超时_「Web应用架构」WebSocket介绍和WebSocket API

    WebSocket支持在客户端和服务器之间双向的.面向消息的文本和二进制数据流.它是浏览器中最接近原始网络套接字的API.除了WebSocket连接也不仅仅是一个网络套接字,因为浏览器在一个简单的AP ...

  5. Html5 WebSocket 技术介绍(转载)

    WebSocket是html5规范新引入的功能,用于解决浏览器与后台服务器双向通讯的问题,使用WebSocket技术,后台可以随时向前端推送消息,以保证前后台状态统一,在传统的无状态HTTP协议中,这 ...

  6. websocket 群/单聊 基础

    websocket 介绍 1.用户A 给 用户B 发送一条消息 问 用户B 多久可以收到 用户A 的消息电子邮件 - 可能是 一周期的时间 及时性很差传达室大爷 - 消息托付 及时性很差即时通讯 - ...

  7. javascript python 通信_Python通过websocket与js客户端通信示例分析

    具体的 websocket 介绍可见 http://zh.wikipedia.org/wiki/WebSocket 这里,介绍如何使用 Python 与前端 js 进行通信. websocket 使用 ...

  8. 从Http它被连接到WebSocket

    1.HTTP协议长期-fi支持和各支持的浏览器 http://blog.csdn.net/fenglibing/article/details/7100222 2.WEBclient与服务端信息交互的 ...

  9. DBUtils连接池,websocket

    1.mysql数据库连接池 概念:数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放. 这样 ...

最新文章

  1. CM: webservice 元数据在word template中的存储
  2. 小批量梯度下降算法步骤_TensorFlow从0到1 - 6 - 解锁梯度下降算法
  3. vue 输入框获取焦点
  4. 【问题解决方案】CentOS7替换yum的问题:使用yum makecache出现File contains no section headers
  5. 8 天扩容超 100 万核,腾讯会议正在刷新历史
  6. 内部类访问,及修饰符
  7. SpringMVC之“HelloWorld”起步
  8. 解线性方程组——有机物燃烧的化学方程组的配平
  9. Struts(十二):异常处理:exception-mapping元素
  10. 樊昌信通信原理第7版笔记和课后习题答案
  11. idea导入eclipse快捷键
  12. HMC5883L指南针罗盘模块连接arduino使用的注意事项
  13. ps怎么去掉框框不伤字体_PS如何去掉和替换图片中的文字? 照着学就行了
  14. ov7725图像帧率计算公式总结
  15. 深入浅出Oracle Spatial
  16. 《C++ Primer 第5版》-11.4无序容器-康奈尔笔记
  17. 请检查下面的程序,找出其中的错误并改正,然后上机调试,使之能正常运行,从键盘输入,检查输出
  18. 如何在TIA 博途 WinCC中组态WinCC Runtime Advanced 和 S7 控制器的PROFINET通信连接?
  19. BMC-web的介绍(一)
  20. KNN算法和Kernel KNN算法的区别

热门文章

  1. android samba github,安卓手机访问树莓派samba文件共享出错解决
  2. 触发更新机制_王者荣耀1.14更新:11名英雄调整,韩信加强,鲁班大师重做
  3. set java底层实现_Java:List,Map,Set底层实现
  4. 阿里云终端连接与实例管理
  5. 《Pro ASP.NET MVC 3 Framework》学习笔记之二十四【Controllers和Actions】
  6. 下拉菜单,防鼠标反复触发
  7. HBase的Row Key设计
  8. HBase之KeyValueScanner
  9. linux ip不设置网关,linux下ip与网关不在同一段配置
  10. (19)Xilinx PCIE中断理论(学无止境)