websocket是解决客户端与服务器端实时通信而产生的技术。
websocket协议允许在客户端和服务端之间建立一条双向传递信息的通道,它是建立在TCP协议之上的,首先通过”握手“来确认和建立通道,之后客户端和服务端可以通过这个通道传递信息,而不需要再次发起请求,而且客户端和服务端都可以主动的发送消息。这种技术不依赖于HTTP连接(比如XMLHttpRequest,iframe),可以实现实时消息传递。
与Socket区别:
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
WebSocket则是一个典型的应用层协议。
Socket是传输控制层协议,WebSocket是应用层协议。
WebSocket 客户端 API :
//创建websocket对象 var ws = new WebSocket(“ws://echo.websocket.org”); //建立连接 ws.onopen = function(){ws.send(“Test!”); }; //收到服务器消息,提取 ws.onmessage = function(evt){console.log(evt.data);ws.close();}; //关闭连接 ws.onclose = function(evt){console.log(“WebSocketClosed!”);}; //产生异常 ws.onerror = function(evt){console.log(“WebSocketError!”);}; |
WebSocket 服务端 API :
我们将使用到websocket-api,这个JAR包则在Tomcat的lib下面。
//WebSocket 服务端运行在 ws://[Server 端 IP 或域名]:[Server 端口]/websockets 的访问端点,客户端浏览器已经可以对 WebSocket 客户端 API 发起 HTTP 长连接了。
@ServerEndpoint("/websocket")
public class EchoEndpoint {
//一个新的连接建立时被调用
//Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念
@OnOpen
public void onOpen(Session session) throws IOException {
//以下代码省略...
}
//收到消息触发事件
//用于接收传入的 WebSocket 信息,这个信息可以是文本格式,也可以是二进制格式。
@OnMessage
public String onMessage(String message) {
//以下代码省略...
}
//MaxMessageSize 属性可以被用来定义消息字节最大限制,如果超过 6 个字节的信息被接收,就报告错误和连接关闭。
@Message(maxMessageSize=6)
public void receiveMessage(String s) {
//以下代码省略...
}
//传输消息错误触发事件
@OnError
public void onError(Throwable t) {
//以下代码省略...
}
//OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。
@OnClose
public void onClose(Session session, CloseReason reason) {
//以下代码省略...
}
} |
聊天室:
服务器端:
package com.research.WebSocketChatRoom;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import com.google.gson.Gson;
//用于创建和配置服务端点
@ServerEndpoint("/websocket")
public class Socket {
public static Map<String, Session> sessionMap = new HashMap<String, Session>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 一个新的连接建立时被调用
@OnOpen
public void startSocket(Session session) {
this.session = session;
System.out.println("链接成功");
if (sessionMap.size() == 0) {
return;
}
Set userIds = sessionMap.keySet();
StringBuffer sBuffer = new StringBuffer();
for (Object str : userIds) {
sBuffer.append(str.toString() + ":");
}
Gson gson = new Gson();
try {
Message message = new Message();
message.setFrom("系统");
message.setMsg(sBuffer.toString());
session.getBasicRemote().sendText(gson.toJson(message), true);
} catch (IOException e) {
e.printStackTrace();
}
}
// 收到消息触发事件
@OnMessage
public void getMessgae(Session session, String str, boolean last) {
if (session.isOpen()) {
try {
System.out.println(str);
Gson gson = new Gson();
Message msg = gson.fromJson(str, Message.class);
Message toMessage = msg;
toMessage.setFrom(msg.getId());
toMessage.setTo(msg.getTo());
if (msg.getMsg().equals("newUser")) {
if (sessionMap.containsKey(msg.getId())) {
sessionMap.remove(msg.getId());
}
sessionMap.put(msg.getId(), session);
} else {
Session toSession = sessionMap.get(msg.getTo());
if (toSession != null && toSession.isOpen()) {
toSession.getBasicRemote().sendText(gson.toJson(toMessage).toString(), last);
} else {
toMessage.setMsg("用户不存在");
toMessage.setFrom("系统");
session.getBasicRemote().sendText(gson.toJson(toMessage).toString(), last);
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("session is closed");
}
}
// 关闭连接触发事件
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println(session.getId() + "关闭连接");
}
// 传输消息错误触发事件
@OnError
public void onError(Throwable error) {
System.out.println(error);
}
}
|
客户端:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>Test WebSocket</title>
<script type="text/javascript" src="/resource/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(function () {
//服务器地址
var url = "ws://localhost:8080/websocket";
var ws = "";
var message = {"id": "", "msg": "", "form": "", "to": ""};
function connection() {
//创建WebSocket对象
ws = new WebSocket(url);
console.log("connection.......");
//收到服务器消息,e.data提取
ws.onmessage = function (e) {
var json = eval('(' + e.data.toString() + ')');
showMessage(json.from + ":" + json.msg);
}
//已经关闭连接
ws.onclose = function () {
showMessage("close");
}
//产生异常
ws.onerror = function (e) {
showMessage("error");
}
//已经建立连接
ws.onopen = function () {
showMessage("链接成功")
message.id = $(".identity").val();
message.msg = "newUser";
console.log(JSON.stringify(message));
//向服务器发送消息
ws.send(JSON.stringify(message));
message.msg = "";
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
ws.close();
}
}
$(".start-conn-btn").click(function () {
connection();
});
$(".send-btn").click(function () {//send message
message.to = $(".to-user").val();
message.msg = $(".msg-context").val();
$(".msg-context").val("");
ws.send(JSON.stringify(message));
showMessage("我:" + message.msg);
message.msg = "";
});
function showMessage(msg) {
$(".show-message").append(msg + "<br/>");
}
});
</script>
</head>
<body>
<div class="container">
<div calss="item">
<span>ID:</span>
<input type="text" class="identity">
<button class="start-conn-btn">链接</button>
<span>toUser:</span>
<input type="text" class="to-user">
</div>
<div class="show-message">
</div>
<div calss="item">
<span>内容:</span>
<textarea class="msg-context"></textarea>
</div>
<div>
<button class="send-btn">send</button>
</div>
</div>
</body>
</html>
|
启动:
/*
* 文 件 名: ChanatRoom.java
* 版 权:
* 描 述: <描述>
* 修 改 人: sun
* 修改时间: 2018年7月18日
*/
package com.home.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* <一句话功能简述>
* <功能详细描述>
*
* @author sun
* @version [版本号, 2018年7月18日]
*/
@Controller
@RequestMapping("/chanat-room")
public class ChanatRoom
{
@RequestMapping("/start")
public String start()
{
return "/chatRoom/chat";
}
}
|