WebSocket 可以让服务器端和浏览器请求响应的模型改为建立实时通信的Socket。

WebSocket 属于HTML5规范,它是HTML5新增的类,创建时要指定 WebSocket 服务器的地址。

一个简易的聊天室如下

开发服务器就就是在普通的java类上加一个 @ServerEndpoint 注解

然后在此类中定义几个方法     @OnOpen 修饰的方法 @OnClose修饰的方法 @OnMessage修饰的方法 @OnError 修饰的方法。

下面这个类就是基于WebSocket实现多人实时聊天的服务器程序。

package lee;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.*;
import javax.websocket.server.*;


@ServerEndpoint(value = "/websocket/chat")
public class ChatEntpoint
{
	private static final String GUEST_PREFIX = "访客";
	//记录是第几个用户而已,每次有新的连接时数值加一,用于区分用户
	private static final AtomicInteger connectionIds = new AtomicInteger(0);
	//将连接客户的那个连接放入 WebSocket客户端
	private static final Set<ChatEntpoint> clientSet =
		new CopyOnWriteArraySet<>();
	//定义一个成员变量,记录WebSocket客户端的聊天昵称
	private final String nickname;
	//定义一个成员变量,记录与WebSocket之间的会话
	private Session session;
	public ChatEntpoint()
	{
		nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
	}
	//刚建立一个连接调用此方法
	@OnOpen
	public void start(Session session)
	{
		this.session = session;
		// 将这个新建的session放入客户端中
		clientSet.add(this);
		String message = String.format("[%s %s]"
			, nickname, "进入聊天室");
		// 将此消息发送给其它用户
		broadcast(message);
	}
	// 连接断开时调用此方法
	@OnClose
	public void end()
	{
		clientSet.remove(this);
		String message = String.format("[%s %s]"
			, nickname, "离开聊天室");
		broadcast(message);
	}
	// 当服务端收到浏览器发送来的消息时,调用此方法
	@OnMessage
	public void incoming(String message)
	{
		String filteredMessage = String.format("%s: %s"
			, nickname, filter(message));
		broadcast(filteredMessage);
	}
	// 出现错误时调用此方法
	@OnError
	public void onError(Throwable t) throws Throwable
	{
		System.out.println("WebSocket出现错误 " + t);
	}
	// 将信息发送给其他所有用户
	private static void broadcast(String msg)
	{
		// 遍历所有客户端
		for (ChatEntpoint client : clientSet)
		{
			try
			{
				synchronized (client)
				{
					//发送消息
					client.session.getBasicRemote().sendText(msg);
				}
			}
			catch (IOException e)
			{
				System.out.println("聊天错误 " + client + " 发送信息错误");
				clientSet.remove(client);
				try
				{
					client.session.close();
				}
				catch (IOException e1){}
				String message = String.format("[%s %s]",
					client.nickname, "已经关闭了连接");
				broadcast(message);
			}
		}
	}
	// 将html中的特殊的字符转义
	private static String filter(String message)
	{
		if (message == null)
			return null;
		char content[] = new char[message.length()];
		message.getChars(0, message.length(), content, 0);
		StringBuilder result = new StringBuilder(content.length + 50);
		for (int i = 0; i < content.length; i++)
		{
			switch (content[i])
			{
				case '<':
					result.append("<");
					break;
				case '>':
					result.append(">");
					break;
				case '&':
					result.append("&");
					break;
				case '"':
					result.append(""");
					break;
				default:
					result.append(content[i]);
			}
		}
		return (result.toString());
	}
}

然后就是客户端程序,使用 javascript 开发

一个html页面

<!DOCTYPE html>
<html>
<head>
	<meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" />
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title> 使用WebSocket通信 </title>
	<script type="text/javascript">
	 	// 创建WebSocket对象 ,这个WebSocket不是指本项目,是指html5中新增的一个类。
	 	//创建时要指定服务器端的地址。
		var webSocket = new WebSocket("ws://localhost:8080/WebSocket/websocket/chat");
		var sendMsg = function()
		{
			var inputElement = document.getElementById('msg');
			// 发送消息
			webSocket.send(inputElement.value);
			// 清空单行文本框
			inputElement.value = "";
		}
		var send = function(event)
		{
			if (event.keyCode == 13)
			{
				sendMsg();
			}
		};
		//客户端与服务器建立连接时,触发此方法
		webSocket.onopen = function()
		{
			// WebSocket客户端收到服务器端的消息时,触发此方法。
			webSocket.onmessage= function(event)
			{
				var show = document.getElementById('show')
				// 接收、并显示消息
				show.innerHTML += event.data + "<br/>";
				show.scrollTop = show.scrollHeight;
			}
			document.getElementById('msg').onkeydown = send;
			document.getElementById('sendBn').onclick = sendMsg;
		};
		//当服务器端与客户端WebSocket之间的连接关闭时,调用此方法。
		webSocket.onclose = function ()
		{
			document.getElementById('msg').onkeydown = null;
			document.getElementById('sendBn').onclick = null;
			Console.log('WebSocket已经被关闭。');
		};
	</script>
</head>
<body>
<div style="width:600px;height:240px;
	overflow-y:auto;border:1px solid #333;" id="show"></div>
<input type="text" size="80" id="msg" name="msg" placeholder="输入聊天内容"/>
<input type="button" value="发送" id="sendBn" name="sendBn"/>
</body>
</html>

需要说明的是这里的ChatEndpoint并不是真正的服务器,它只是具备了 WebSocket 服务器的基本要素,Tomcat会调用它的方法作为WebSocket的服务端。因此Tomcat会为每一个WebSocket客户端创建一个ChatEndpoint 对象,所有创建的对象都被放入了 set 集合中,而这个set是静态的,所以每一个对象都可以使用这个set。set中每一个对象对应一个WebSocket客户端。

编译ChatEntpoint 类,并将生成的class文件放在WEB-INF中,该类可以作为服务器端使用。