HTML5 and Mobile Web: network traffic

A, Comet- Ajax-based long-polling

Definition: The client sends a request to the server Ajax, hold live connection server upon request, does not return until a new message response information and close the connection, the client sends a new response information after their request to the server.

  1. The server will be blocked until the request times out or return data transfer.
  2. Client-side JavaScript function processing response message returned by the server upon completion of the processing, the request again, to re-establish the connection.
  3. When the client processes the received data, re-establish the connection, the server may have new data arrives; this information will be stored until the client to re-establish the connection, the client will end once the current server to retrieve all the information on the server side.

Advantages: not frequently request message without a case, small consumption of resources.

Drawback : server hold the connection will consume resources, to ensure that no data is returned order, difficult to manage and maintain.

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<link rel="stylesheet"
	href="http://apps.bdimg.com/libs/jquerymobile/1.4.5/jquery.mobile-1.4.5.min.css">
<!-- 引入 jQuery 库 -->
<script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
<!-- 引入 jQuery Mobile 库 -->
<script
	src="http://apps.bdimg.com/libs/jquerymobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>

<script type="text/javascript">

	$(function() {
		getMsgNum();
	});
	function getMsgNum() {
		$.ajax({
			url : 'JsLongPollingMsgServlet',
			type : 'post',
			dataType : 'json',
			data : {
				"pageMsgNum" : $("#pageMsgNum").val()
			},
			timeout : 5000,
			success : function(data, textStatus) {
				if (data && data.msgNum) {
					//请求成功,刷新数据
					$("#msgNum").html(data.msgNum);
					//这个是用来和后台数据作对比判断是否发生了改变
					$("#pageMsgNum").val(data.msgNum);
				}
				if (textStatus == "success") {
					//成功之后,再发送请求,递归调用
					getMsgNum();
				}
			},
			error : function(XMLHttpRequest, textStatus, errorThrown) {
				if (textStatus == "timeout") {
					//有效时间内没有响应,请求超时,重新发请求
					getMsgNum();
				} else {
					// 其他的错误,如网络错误等
					getMsgNum();
				}
			}
		});
	}
</script>
</head>

<body>
	<div id="page1" data-role="page">
		<div data-role="header">
			<h1>AJAX长轮询</h1>
		</div>
		<div data-role="content">
			<input id="pageMsgNum" name="pageMsgNum" type="hidden" /> 您有<span
				id="msgNum" style="color: red;">0</span>条消息!
		</div>
		<div data-role="footer">
			<h1>CopyRight 2019</h1>
		</div>
	</div>
</body>
</html>


import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class JsLongPollingMsgServlet
 */
@WebServlet("/JsLongPollingMsgServlet")
public class JsLongPollingMsgServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public JsLongPollingMsgServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		ServletContext application = this.getServletContext();
	   List msglist= (List)application.getAttribute("msg");
	   request.setCharacterEncoding("utf-8");

	       PrintWriter out = response.getWriter();
	       String pageMsgNumStr = request.getParameter("pageMsgNum");
	       if(pageMsgNumStr==null || "".equals(pageMsgNumStr)){
	           pageMsgNumStr = "0";
	       }
	       int pageMsgNum = Integer.parseInt(pageMsgNumStr);

	       int num = 0;
	       StringBuffer json = null;
	       while(true){
	           num = msglist.size();
	           //数据发生改变 将数据响应客户端
	           if(num != pageMsgNum){
	               json = new StringBuffer("{");
	               json.append("\"msgNum\":"+num);
	               json.append("}");
	               break;
	           }else{
	               //没有新的数据 保持住连接
	               try {
	                   Thread.sleep(1000);
	               } catch (InterruptedException e) {
	                   e.printStackTrace();
	               }
	           }
	       }
	       out.write(json.toString());
	       out.close();
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

	
}

Two, Comet- htmlfile based on the flow and the Iframe (Streaming) mode

  1. The client program iframe, iframe server does not return the data in the page displayed directly, but instead returns to the calling client Javascript functions, such as "<script type =" text / javascript "> js_func (" data from server " ) </ script> ". The server will return the data as a client-side JavaScript function parameters passed; the client browser's Javascript engine in return receive calls will go to the server when JavaScript code execution.
  2. Each data transfer does not close the connection, the connection only when a communication error occurs, or off (some firewalls are often set too long to drop the connection, the server may set a timeout after a timeout notification client connection reestablishment re-establish the connection and close the original connection).
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul id="content"></ul>
<form class="form">
<input type="text" placeholder="请输入发送的消息" class="message" id="message"/>
<input type="button" value="发送" id="send" class="connect"/>
<input type="button" value="连接" id="connect" class="connect"/>
</form>
<script>
	var oUl=document.getElementById('content');
	    var oConnect=document.getElementById('connect');
	    var oSend=document.getElementById('send');
	    var oInput=document.getElementById('message');
	    var ws=null;
	    oConnect.onclick=function(){
	        ws=new WebSocket('ws://localhost:3000');
	         ws.onopen=function(){
	             oUl.innerHTML+="<li>客户端已连接</li>";
	         }
	        ws.onmessage=function(evt){
	            oUl.innerHTML+="<li>"+evt.data+"</li>";
	        }
	        ws.onclose=function(){
	            oUl.innerHTML+="<li>客户端已断开连接</li>";
	        };
	        ws.onerror=function(evt){
	            oUl.innerHTML+="<li>"+evt.data+"</li>";
	 
	        };
	    };
	    oSend.onclick=function(){
	        if(ws){
	            ws.send(oInput.value);
	        }
	    }
</script>
</body>
</html>
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * Servlet implementation class SetMsg
 */
@WebServlet("/SetMsg")
public class SetMsg extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public SetMsg() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		ServletContext application = this.getServletContext();
		   List msglist= new ArrayList();

		if(application.getAttribute("msg")!=null)
		{
			msglist=(List)application.getAttribute("msg");
		}
		
			
		   msglist.add(request.getParameter("msgstr"));
		   application.setAttribute("msg", msglist);
		   	
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

Three, SSH

  • SSE and WebSocket similar effect, are established channels of communication between the browser and the server, and the server to push information to the browser.
  • Overall, WebSocket more powerful and flexible. Because it is a full-duplex channel, bidirectional communication can; SSE is a one-way channel, only the server sends to the browser, because the information flow is essentially download. If the browser sends information to the server, it becomes another HTTP request.
  • SSE advantage
  1. SSE using the HTTP protocol, the existing server software support. WebSocket is a separate agreement.
  2. SSE belongs lightweight, simple to use; a WebSocket protocol is relatively complex.
  3. SSE supported by default reconnection, WebSocket needs its own implementation.
  4. SSE generally used to transmit text, binary data to be encoded after the transfer, a WebSocket default support transmission of binary data.
  5. SSE support for custom message types sent.
  6. SSE is suitable for frequent updates, low latency and data are from service to client.

Communication protocol is a simple protocol based on plain text.

SSE data server sends to the browser, it must be encoded in UTF-8 text, with the following HTTP header.

Content-Type: text/event-stream

Cache-Control: no-cache

Connection: keep-alive

The first line of the MIME Content-Type must specify the type of Event-the Steam .

Each time information transmitted from a plurality of message composition, with \ n \ n separation between each message. Each internal message is composed of rows, each row is the following format.

[field]: value\n

        Data types , indicating that the line contains data. Lines beginning with data can appear multiple times. All these lines are the data of the event.

If the data is long, it can be divided into a plurality of rows, the last row \ n \ end n, with the front row ends with \ n.

       Type the above mentioned id , said the bank identifier used to declare an event, the equivalent number of each piece of data.

       Of type Event , said the bank is used to declare the type of event. When the browser receives the data, it will generate the corresponding types of events. The default is the message events. Browser can listen to this event with addEventListener ().

       Type retry , said the bank is used to declare browser waiting time before a connection again after disconnection.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
    <script>
    var eventSource;
        function start() {
            eventSource = new EventSource("HelloServlet");
            eventSource.onmessage = function(event) {
                document.getElementById("foo").innerHTML = event.data;
            };
            eventSource.addEventListener("ms",function(){})
        }
        
        function close(){
          eventSource.close();
        }
    </script>
</head>
<body>
    Time: <span id="foo"></span>
    
    <br><br>
    <button onclick="start()">Start</button>
 <button onclick="close()">Close</button>
</body>
</html>
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public HelloServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		 // ContentType 必须指定为 text/event-stream
		response.setContentType("text/event-stream");
        // CharacterEncoding 必须指定为 UTF-8
		response.setCharacterEncoding("UTF-8");
        PrintWriter pw = response.getWriter();
 
            // 每次发送的消息必须以\n\n结束

            pw.write("event:ms\n data: " + new Date() + " 这是第1次测试\n\n");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        
        pw.close();
    }
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

Four, WebSocket

principle:

When a client connects the server, it will send a similar following http packet to the server:

you can see, this is a http get request message, note the message there is an upgrade header, its role is to tell the service end communication protocol needs to be switched to websocket, if the server supports websocket agreement, then it will own communication protocol switching to websocket, at the same time to the client a response similar to the following header:

the return status code 101 , it agreed with the client protocol conversion request, and converts it to websocket agreement. The above process is accomplished using the http communication, called websocket protocol handshake (websocket Protocol handshake), after been to this handshake, the client and server to establish a connection websocket, after communications are taking the websocket the agreement. So to summarize websocket need the help of http protocol handshake, the connection is established using the communication process websocket agreement. At the same time need to understand that the connection is based on websocket we have just launched the TCP http connection. Once after the connection is established, we can transfer the data, websocket provides two data transmission: text data and binary data.

Server-side -javax.websocket

Client

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE HTML>
<html>
<head>
<title>Java后端WebSocket的Tomcat实现</title>
<script type="text/javascript">
	var websocket = null;
	var host = document.location.host;
	function connect() {
		//判断当前浏览器是否支持WebSocket
		if ('WebSocket' in window) {
			var value = document.getElementById("b").value;
			websocket = new WebSocket("ws://" + host + "/exam5/websocket/" + value);
			//连接发生错误的回调方法
			websocket.onerror = function() {
				setMessageInnerHTML("WebSocket连接发生错误");
			};
			//连接成功建立的回调方法
			websocket.onopen = function() {
				setMessageInnerHTML("WebSocket连接成功");
			}
			//接收到消息的回调方法
			websocket.onmessage = function(event) {
				setMessageInnerHTML(event.data);
			}
			//连接关闭的回调方法
			websocket.onclose = function() {
				setMessageInnerHTML("WebSocket连接关闭");
			}
			//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
			window.onbeforeunload = function() {
				closeWebSocket();
			}
		} else {
			alert('当前浏览器 Not support websocket')
		}
	}
	//将消息显示在网页上
	function setMessageInnerHTML(innerHTML) {
		document.getElementById('message').innerHTML += innerHTML + '<br/>';
	}
	//关闭WebSocket连接
	function closeWebSocket() {
		websocket.close();
	}
	//发送消息
	function send() {
		var message = document.getElementById('text').value;
		websocket.send(message);
	}
</script>
</head>
<body>
	Welcome
	<br />
	<input id="text" type="text" />
	<input type="button" onclick="send()" value="发送消息"/>
	<br />
	<input id="b" type="text" />
	<!-- 这里用于注册不同的clientId, 多个webSocket客户端只能同步收到相同clientId的消息 -->
	<input type="button" onclick="connect()" value="连接"/>
	<hr />
	<input type="button" onclick="closeWebSocket()" value="关闭WebSocket连接"/>
	<hr />
	<div id="message"></div>
</body>
</html>
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
 
/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 *                 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint("/websocket/{clientId}")
public class WebSocket {
	// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
	private static AtomicInteger onlineCount = new AtomicInteger(0);
	// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
	//若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
	// private static CopyOnWriteArraySet<WebSocket> webSocketSet = new
	// CopyOnWriteArraySet<WebSocket>();
	// 与某个客户端的连接会话,需要通过它来给客户端发送数据
	//记录每个客户端的实例变量, 现在拿下面的全局map记录 
	//private Session session;
	private static Map<String, Session> webSocketMap = new ConcurrentHashMap<String, Session>();

	/**
	 * 连接建立成功调用的方法
	 * 
	 * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(@PathParam("clientId") String clientId, Session session) {
		// 用登录用户编号和sessionId的拼接来做webSocket通信的唯一标识
		String key = getWebSocketMapKey(clientId, session);
		webSocketMap.put(key, session);
		addOnlineCount(); // 在线数加1
		System.out.println("WebSocket有新连接加入!当前在线人数为" + getOnlineCount());
	}
	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose(@PathParam("clientId") String clientId, Session session, CloseReason closeReason) {
		String key = getWebSocketMapKey(clientId, session);
		webSocketMap.remove(key, session);
		subOnlineCount(); // 在线数减1
		System.out.println("WebSocket有一连接关闭!当前在线人数为" + getOnlineCount());
	}
 
	/**
	 * 收到客户端消息后调用的方法
	 * 
	 * @param message 客户端发送过来的消息
	 * @param session 可选的参数
	 */
	@OnMessage
	public void onMessage(@PathParam("clientId") String clientId, String message, Session session) {
		System.out.println("WebSocket收到来自客户端的消息:" + message);
		sendMessageByClientId(clientId, message);
	}
 
	/**
	 * 获取webSocketMap集合的Key
	 * 
	 * @param clientId 用户编号
	 * @param session  webSocket的Session
	 * @return
	 */
	private String getWebSocketMapKey(String clientId, Session session) {
		if (clientId==null) {
			return session.getId();
 
		} else {
			return clientId + "_" + session.getId();
		}
	}
	/**
	 * 发生错误时调用
	 * 
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error) {
 
		System.out.println("WebSocket发生错误");
	}
 
 
	// 群发消息
	public static void doSend(String message) {
		if (webSocketMap.size() > 0) {
			for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
				try {
					sendMessage(entry.getValue(), message);
				} catch (IOException e) {
					System.out.println("WebSocket doSend is error:");
					continue;
				}
			}
		}
	}
 
	public static void sendMessage(Session session, String message) throws IOException {
		session.getBasicRemote().sendText(message);
	}
 
	public static int sendMessageByClientIdList(List<String> clientIdList, String message) {
		int status = 0;
		for (String clientId : clientIdList) {
			status = sendMessageByClientId(clientId, message);
		}
		return status;
	}
 
	/**
	 * 通过用户的编号来发送webSocket消息
	 * 
	 * @param clientId
	 * @param message
	 */
	public static int sendMessageByClientId(String clientId, String message) {
		int status = 0;
		if (webSocketMap.size() > 0) {
			for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
				try {
					String key = entry.getKey();
					// 判断webSocketMap中的clientId和发送的clientId是否相同
					// 若相同则进行发送消息
					String key1 = key.substring(0, key.lastIndexOf("_"));
					if (key1.equals(clientId)) {
						sendMessage(entry.getValue(), message);
						status = 200;
					}
				} catch (IOException e) {
					System.out.println("WebSocket doSend is error:");
					continue;
				}
			}
		}
		return status;
	}
 
	public static void sendSpeechMessageByClientId(String clientId, String message) {
		if (webSocketMap.size() > 0) {
			for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
				try {
					String key = entry.getKey();
					// 判断webSocketMap中的clientId和发送的clientId是否相同
					// 若相同则进行发送消息
					String key1 = key.substring(0, key.lastIndexOf("_"));
					if (key1.equals(clientId)) {
						sendMessage(entry.getValue(), message);
					}
				} catch (IOException e) {
					System.out.println("WebSocket doSend is error:");
					continue;
				}
			}
		}
	}
 
	public static synchronized AtomicInteger getOnlineCount() {
		return onlineCount;
	}
 
	public static synchronized void addOnlineCount() {
		WebSocket.onlineCount.getAndIncrement();
	}
 
	public static synchronized void subOnlineCount() {
		WebSocket.onlineCount.getAndDecrement();
	}
}

 

 

 

Published 349 original articles · won praise 161 · views 190 000 +

Guess you like

Origin blog.csdn.net/qq_42192693/article/details/103864780
Recommended