在webSocket还未引入前,许多开发人员通过各种非正规手段来完成更新网站的最新信息和到所有当前访问者的任务,其中一种手段就是通过浏览器向服务器轮询更新,但这种手段的网络延迟比较明显,其用户体验比较差。而webSocket协议的引入比较好的解决这种问题,webSocket是一种网络协议,它允许两个相连的端在一个TCP连接上进行全双工通讯。它主要用来作为托管在Web服务器上的Web应用和浏览器之间的通讯机制,同样,WebSocket可以在网络间的任意两端建立,而不一定要在浏览器和服务器上。Java Web Socket API 是JavaEE7平台的核心特性。端点是Java WebSocket API组件模型的中心,而创建端点的方式有注解式和编程式。下面的内容都是关于WebSocket注解式编程。
WebSocket端点的4个生命周期事件
- 打开事件
此事件发生在端点上建立新连接时并且在任何其他事件之前 - 消息事件
此事件接收WebSocket对话的另外一端发送的消息。它可以发生在WebSocket端点接收了打开事件后并且在接收关闭事件关闭连接之前的任意时刻 - 错误事件
此事件在WebSocket连接或者端点发生错误时产生 - 关闭事件
此事件表示WebSocket端点的连接目前正在部分的关闭,它可以由参与连接的任意一个端点发出
注解式端点事件处理
将Java类声明成WebSocket端点,在服务器端端点用@ServerEndpoint来注解,在客户端可以使用@ClientEndpoint来注解。对于端点的四个生命周期事件:
打开事件@OnOpen
@OnOpen
public void init(Session session,EndpointConfig config){
/*
*方法名任意,参数两个任选,可要可不要,其他事件也一样
**/
}
消息事件@OnMessage
@OnMessage
public void message(String textMessage,Session session){
//处理文本信息,Session参数可选
}
@OnMessage
public void message(byte[] messageData,Session session){
//处理二进制信息,Session参数可选
}
@OnMessage
public void message(String textMessage,boolean isLast){
//处理分片段的文本信息,isLast 为false表示信息没有接收完整,true则表示最后一条信息
}
//所有的@OnMessage 也可以有返回值相当于发送消息的功能
@OnMessage
public String message(String textMessage,Session session){
//处理文本信息,Session参数可选
return "I got it";
}
错误事件@OnError
@OnError
public void errorHandler(Throwable t){
//log error here
}
关闭事件@OnClose
@OnClose
public void goodbye(){
//
}
发送信息
1.发送字符串消息
RemoteEndpoint.Basic发送文本信息:public void sendText(String text) throws IOEcxeption
RemoteEndpoint.Basic发送文本信息到流:public Writer getSendStream() throws IOEcxeption
RemoteEndpoint以片段形式发送文本消息:public void sendText(String partialMessage,boolean isLast) throws IOException
以小片段序列的形式发送大的字符串消息,调用时isLast参数一般设为false,直到最后一个片段才设为true,表明消息发送完毕。
2.发送二进制消息
对于大多数应用,发送文本形式消息已经足够,对于有特殊格式例如小图像文件,以二进制形式发送消息则更合适
RemoteEndpoint发送二进制消息:
public void sendBinary(ByteBuffer data) throws IOException
public void sendBinary(ByteBuffer partialByte,boolean isLast) throws IOException 以分片的形式发送,调用时isLast参数一般设为false,直到最后一个片段才设为true,表明消息发送完毕。
RemoteEndpoint.Basic使用流发送二进制消息:
public OutputStream getSendStream() throws IOException
基于Java WebSocket 编写的简易聊天室
服务端采用注解式编写
package server;
import java.io.IOException;
import java.util.HashMap;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/echo")//注解使得此Java类声明成WebSocket的端点
public class EchoServer {
private boolean first = true;
private String name;//用户昵称
//connect key为session的ID,value为此对象this
private static final HashMap<String,Object> connect = new HashMap<String,Object>();
//userMap key为session的ID,value为用户名
private static final HashMap<String,String> userMap = new HashMap<String,String>();
private Session session;
@OnOpen
public void start(Session session){
this.session = session; //获取Seession,存入SashMap
connect.put(session.getId(),this);
}
@OnMessage
public void echo( String incomingMessage,Session session){
EchoServer client = null ;
//first 判断是否第一次传值,第一次的值是昵称,由web端的OnOpen传入
if(first){
this.name = incomingMessage;
String message ="系统:欢迎"+name;
//昵称和session的Id一一对应存储在HashMap
userMap.put(session.getId(), name);
//将message广播给所有用户
for (String key : connect.keySet()) {
try {
client = (EchoServer) connect.get(key);
synchronized (client) {
//给对应的Web端发送一个文本消息
client.session.getBasicRemote().sendText(message);
}
} catch (IOException e) {
connect.remove(client);
try {
client.session.close();
} catch (IOException e1) {
}
}
}
//输入昵称后,往后的交互传值都不是第一次
first = false;
}else{
/**
* incomingMessage的值为xxx@xxxxx的形式xxx为要发给的用户昵称,all则表示发给所有人
* incomingMessage.split("@",2);以@为分隔符把字符串分为xxx和xxxxx两部分
*/
String [] list = incomingMessage.split("@",2);
if(list[0].equalsIgnoreCase("all")){ //all广播全部人
sendAll(list[1],session);
}else{
boolean you = false;//标记是否找到发送的用户
for(String key : userMap.keySet()){
if(list[0].equalsIgnoreCase(userMap.get(key))){
client = (EchoServer) connect.get(key);
synchronized (client) {
try {
//发送信息给指定的用户
client.session.getBasicRemote().sendText(userMap.get(session.getId())+"对你说:"+list[1]);
} catch (IOException e) {
e.printStackTrace();
}
}
you = true;//找到指定用户标记为true
break;
}
}
//you为true则在自己页面显示自己对xxx说xxxxx,否则显示系统:无此用户
if(you){
try {
session.getBasicRemote().sendText("自己对"+ list[0]+"说:"+list[1]);
} catch (IOException e) {
e.printStackTrace();
}
}else{
try {
session.getBasicRemote().sendText("系统:无此用户");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@OnClose
public void close(Session session){
//当一用户退出时,对其他用户进行广播
String message ="系统:"+userMap.get(session.getId()) +"退出群聊";
userMap.remove(session.getId());
connect.remove(session.getId());
for (String key : connect.keySet()) {
EchoServer client = null ;
try {
client = (EchoServer) connect.get(key);
synchronized (client) {
client.session.getBasicRemote().sendText(message);
}
} catch (IOException e) {
connect.remove(client);
try {
client.session.close();
} catch (IOException e1) {
}
}
}
}
//对信息进行全体广播
public static void sendAll(String mess,Session session){
String who = null;
for (String key : connect.keySet()) {
EchoServer client = null ;
try {
client = (EchoServer) connect.get(key);
if(key.equalsIgnoreCase(session.getId())){
who = "自己对大家说 : ";
}else{
who = userMap.get(session.getId())+"对大家说 : ";
}
synchronized (client) {
client.session.getBasicRemote().sendText(who+mess);
}
} catch (IOException e) {
connect.remove(client);
try {
client.session.close();
} catch (IOException e1) {
}
}
}
}
public String getName(){
return this.name;
}
}
web端
<!DOCTYPE html>
<html>
<head>
<title>简易聊天室</title>
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var ws;
var wsUri = "ws://localhost:8080/Socket/echo";
ws = new WebSocket(wsUri);
ws.onopen = function(){
n=prompt('请给自己取一个昵称:');
n=n.substr(0,16);
ws.send(n);//在服务端必须由OnMessage接收此消息
};
//处理连接后的信息处理
ws.onmessage = function(message){
writeToScreen(message.data);
};
//对发送按钮进行监听,获取发送的信息和发送对象
function button(){
message = document.getElementById('in').value;
towho = document.getElementById('towho').value + "@";
ws.send(towho+message);
}
//发生错误时,处理错误
ws.onerror = function (evt){
writeToScreen('<span style="color:red;">ERROR:</span>'+evt.data);
ws.close();
};
//把信息显示到当前屏幕
function writeToScreen(message){
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
//当关闭页面时执行ws.close
window.onbeforeunload=function (){
ws.close();
};
</script>
</head>
<body>
<h1>简易聊天室</h1>
<div style="width:400px;height:260px; overflow:scroll; border:3px solid; " id="output"> </div> <br>
<div style="text-align:left;">
<form action="">
<input id="in" name="message" value="" type="text" style="width:400px;height:60px; border:3px solid; " >
<br><br>
<input onclick="button()" value="发送" type="button"/>
发送对象:
<input id="towho" name="towho" value="all">
<br>
</form>
</div>
</body>
</html>
部署程序,Tomcat从7.0.27开始支持WebSocket,从7.0.47开始支持JSR-356,上述代码也是需要部署在Tomcat7.0.47上,JDK1.7以上才能运行。当浏览器或者Tomcat或者jdk不支持的时候会报undefined的错误