tomcat websocket的实现

1.客户端发送一个握手包告诉服务端它想升级成WebSocket,不知道服务端是否同意。这时服务端支持WebSocket协议,则会返回一个握手包告诉客户端没问题,升级已确认,然后,就成功建立了一条WebSocket连接,该连接支持双向通信,并且使用WebSocket协议的数据帧格式发送消息。


握手过程需要说明,为了让WebSocket协议能和现有HTTP协议Web架构互相兼容,WebSocket协议的握手要基于HTTP协议.

WebSocket特点如下:

  1. 单一TCP长连接,采用全双工通信模式
  2. 对代理、防火墙透明
  3. 无头部信息、消息更明确
  4. 通过ping/pong来保活
  5. 服务器可以主动推送消息给客户端,不在需要客户轮询


通过javaScript 中的API可以直接操作WebSocket对象,其示例如下:

var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()// 建⽴成功之后触发的事件
{
    console.log(“打开连接”);
    ws.send("ddd"); // 发送消息
};
ws.onmessage = function(evt) { // 接收服务器消息
    console.log(evt.data);
};
ws.onclose = function(evt) {
    console.log(“WebSocketClosed!”); // 关闭连接
};
ws.onerror = function(evt) {
    console.log(“WebSocketError!”); // 连接异常
};
 
 

websocket请求头:

其中包含Upgrade:websocket就告诉服务器端客户端想升级协议。

服务端响应、并建立连接

此时如果服务端支持websocket协议,则它会发送一个同意客户端升级协议的报文。其中"Upgrade:websocket" 就告诉 客户端服务器同意客户端的升级协议。

连接状态查看:

通过ws.readyState可查看当前连接状态可选值如下:

1.CONNECT(0):表示还没建立连接

2.OPEN(1):已经建立连接,可以进行通讯

3.CLISING(2):通过关闭握手,正在关闭连接

4.CLOSED(3):连接已经关闭或无法打开



客户端JS 的实现:

if (!window.WebSocket && window.MozWebSocket)
	window.WebSocket=window.MozWebSocket;
if (!window.WebSocket)
	alert("No Support ");
var ws;

$(document).ready(function(){
	 $("#sendbutton").attr("disabled", false);
	 $("#sendbutton").click(sendMessage);
	startWebSocket();
})

/**
 * 发送消息按钮
 * @returns
 */
function sendMessage()
{
	var msg="wyy:"+$("#message").val();
	send(msg);
}
function send(data)
{
	console.log("Send:"+data);
	ws.send(data);
}
function startWebSocket()
{	
	ws = new WebSocket("ws://" + location.host + "/WebSocket/websocket/1");
    ws.onopen = function(){
    	console.log("success open");
        $("#sendbutton").attr("disabled", false);
    };
    //接收后端的消息并处理
	 ws.onmessage = function(event)
	 {
		 alert("RECEIVE:"+event.data);
		 handleData(event.data); 
	 };
	  ws.onclose = function(event) { 
         console.log('Client notified socket has closed',event); 
      }; 
  
}

function handleData(data)
{
   $("#msg").html(data);
	
}


客户端JS 的实现:

if (!window.WebSocket && window.MozWebSocket)
	window.WebSocket=window.MozWebSocket;
if (!window.WebSocket)
	alert("No Support ");
var ws;

$(document).ready(function(){
	 $("#sendbutton").attr("disabled", false);
	 $("#sendbutton").click(sendMessage);
	startWebSocket();
})

/**
 * 发送消息按钮
 * @returns
 */
function sendMessage()
{
	var msg="wyy:"+$("#message").val();
	send(msg);
}
function send(data)
{
	console.log("Send:"+data);
	ws.send(data);
}
function startWebSocket()
{	
	ws = new WebSocket("ws://" + location.host + "/WebSocket/websocket/1");
    ws.onopen = function(){
    	console.log("success open");
        $("#sendbutton").attr("disabled", false);
    };
    //接收后端的消息并处理
	 ws.onmessage = function(event)
	 {
		 alert("RECEIVE:"+event.data);
		 handleData(event.data); 
	 };
	  ws.onclose = function(event) { 
         console.log('Client notified socket has closed',event); 
      }; 
  
}

function handleData(data)
{
   $("#msg").html(data);
	
}

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 *                 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 * @ServerEndpoint 可以把当前类变成websocket服务类
 */
@ServerEndpoint("/websocket/{userno}")
public class WebSocketTest {
	// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
	private static int onlineCount = 0;
	// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
	private static ConcurrentHashMap<String, WebSocketTest> webSocketSet = new ConcurrentHashMap<String, WebSocketTest>();
	// 与某个客户端的连接会话,需要通过它来给客户端发送数据
	private Session session;
	// 当前发消息的人员编号
	private String userno = "";

	/**
	 * 连接建立成功调用的方法
	 *
	 * @param session
	 *            可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(@PathParam(value = "userno") String param, Session session, EndpointConfig config) {
		System.err.println(this);
		userno = param;// 接收到发送消息的人员编号
		this.session = session;
		webSocketSet.put(param, this);// 加入map中
		addOnlineCount(); // 在线数加1
		System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
	}

	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose() {
		if (!userno.equals("")) {
			webSocketSet.remove(userno); // 从set中删除
			subOnlineCount(); // 在线数减1
			System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
		}
	}

	/**
	 * 收到客户端消息后调用的方法
	 *
	 * @param message
	 *            客户端发送过来的消息
	 * @param session
	 *            可选的参数
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		System.out.println("来自客户端的消息:" + message);
		// session.get
		// 群发消息
		sendAll(message);
	}

	/**
	 * 给指定的人发送消息 可以在项目中直接调用这个方法时
	 * 
	 * @param message
	 */
	private void sendToUser(String message) {
		// 给指定的人推送消息,推送消息之前,肯定知道userno
		String now = getNowTime();
		try {
			if (webSocketSet.get(1) != null) {
				webSocketSet.get(1).sendMessage(now + "用户" + userno + "发来消息:" + " <br/> " + message);
			} else {
				System.out.println("当前用户不在线");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 给所有人发消息
	 * 
	 * @param message
	 */
	private void sendAll(String message) {
		String now = getNowTime();
		String sendMessage = message.split("[|]")[0];
		// 遍历HashMap
		for (String key : webSocketSet.keySet()) {
			try {
				// 判断接收用户是否是当前发消息的用户
				if (!userno.equals(key)) {
					webSocketSet.get(key).sendMessage(now + "用户" + userno + "发来消息:" + " <br/> " + sendMessage);
					System.out.println("key = " + key);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 获取当前时间
	 *
	 * @return
	 */
	private String getNowTime() {
		Date date = new Date();
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String time = format.format(date);
		return time;
	}

	/**
	 * 发生错误时调用
	 *
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error) {
		System.out.println("发生错误");
		error.printStackTrace();
	}

	/**
	 * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
	 *
	 * @param message
	 * @throws IOException
	 */
	public void sendMessage(String message) throws IOException {
		this.session.getBasicRemote().sendText(message);
		// this.session.getAsyncRemote().sendText(message);
	}

	public static synchronized int getOnlineCount() {
		return onlineCount;
	}

	public static synchronized void addOnlineCount() {
		WebSocketTest.onlineCount++;
	}

	public static synchronized void subOnlineCount() {
		WebSocketTest.onlineCount--;
	}

}




猜你喜欢

转载自blog.csdn.net/fd2025/article/details/80037748