WebSocket協議是H5的一個基於TCP的新協議。WebSocket協議被設計來取代用HTTP作爲傳輸層的雙向通訊技術,這些技術只能犧牲其中一方來提高另一方的效率和可依賴性。
效果展示
chat
優點
在實現websocket連線過程中,需要通過瀏覽器發出websocket連線請求,然後服務器發出迴應,這個過程通常稱爲“握手” 。在 WebSocket API,瀏覽器和服務器只需要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。在此WebSocket 協議中,爲我們實現即時服務帶來了兩大好處:
1. Header互相溝通的Header是很小的-大概只有 2 Bytes
2. Server Push服務器的推送,服務器不再被動的接收到瀏覽器的請求之後才返回數據,而是在有新數據時就主動推送給瀏覽器。
應用
WebSocket可以解決很多需要實時通訊的問題,比如:聊天功能、社交訂閱、股票基金變動、體育實況更新等等,都可以採用websocket進行解決。
協議內容
1544674093972
其中:
Upgrade: websocket
Connection: Upgrade
這等於告訴web服務器,發起的請求是Websocket協議的,要用Websocket協議方式來處理請求。在請求頭裏,還增加了:
Sec-WebSocket-Key: p+uL7rm/spX33U/iZ/yeZg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Version: 13
- Sec-WebSocket-Key 是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:驗證是不是真的是Websocket助理;
- Sec-WebSocket-Extensions: 擴展參數;
- Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本)
聊天室編寫
1、環境搭建
IDEA + MAVEN + JDK1.8 + Tomcat7,主要的jar包依賴如下:
<!-- Tomcat has it, so no need to package into the war file -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>${websocket.version}</version>
<scope>provided</scope>
</dependency>
<!-- Sevlet jars for compilation, provided by Tomcat -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>${tomcat.version}</version>
<scope>provided</scope>
</dependency>
<!-- Used to serialize the message from the browser -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
</dependency>
2、前端js編寫
思路:當用戶點擊登錄時,使用websocket協議自動連接後臺,大致流程如下:
官網地址:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
客戶端的流程:
-
創建Websocket實例:
var ws = new WebSocket("ws://127.0.0.1:8080/chat");
a. 服務器IP:ws://127.0.0.1
b. 服務器端口:8080
c. 服務程序:chat
- 建立連接後回調函數:ws.onopen = function(event){}
- 收到服務端消息後回調函數:ws.onmessage = function(event){}
- 關閉連接後回調函數:ws.onclose = function(event){}
- 連接錯誤後回調函數:ws.onerror = function(event)
- 發送數據:ws.send('服務器,您好');
- 查看當前連接狀態:alert(ws.readyState)
var websocket, name;
// 建立連接
function connection() {
name = $("#name").val();
if (name == null || name.trim().length === 0) {
return;
}
$("#username-page").addClass("hidden");
$("#chat-page").removeClass("hidden");
//判斷當前瀏覽器是否支持WebSocket
if('WebSocket' in window){
websocket = new WebSocket("ws://127.0.0.1:8080/chat");
}else{
alert('Not support websocket');
return;
}
// 連接成功回調
websocket.onopen = function (event) {
play();
$("#connecting").addClass("hidden");
send('JOIN');
}
//連接發生錯誤的回調方法
websocket.onerror = function(){
console.error("連接異常。。。")
}
// 接收到消息的回調方法
websocket.onmessage = function(event){
var text = event.data;
play();
onMessageReceived(text)
}
//連接關閉的回調方法
websocket.onclose = function(){
console.log("連接關閉。。。");
}
}
// 發送消息
function send(type) {
var text = $("#message").val();
if (websocket == null) {
return;
}
if ( type == 'CHAT' && (text ==null || text.trim().length == 0)) {
return;
}
var content = {"sender":name, content: text, type: type};
websocket.send(JSON.stringify(content));
$("#message").val('');
play();
}
// 聲音
function play() {
var audio = new Audio("/audio/beep.wav");
audio.play();
}
// 處理接收的消息
function onMessageReceived(text) {
var message = JSON.parse(text);
var messageElement = $('<li></li>');
if(message.type === 'JOIN' || message.type === 'LEAVE') {
messageElement.addClass('event-message');
} else {
messageElement.addClass('chat-message');
// 創建頭像
var avatarElement = $('<i></i>');
avatarElement.html(message.sender[0]);
var color = getAvatarColor(message.sender);
avatarElement.css('background-color', color);
messageElement.append(avatarElement);
var usernameElement = $('<span></span>');
usernameElement.html(message.sender);
messageElement.append(usernameElement);
}
var textElement = $('<p></p>');
textElement.html(message.content);
messageElement.append(textElement);
$('#messageArea').append(messageElement);
$('#messageArea')[0].scrollTop = $('#messageArea')[0].scrollHeight;
}
var colors = [
'#2196F3', '#32c787', '#00BCD4', '#ff5652',
'#ffc107', '#ff85af', '#FF9800', '#39bbb0'
];
// 隨機獲取顏色
function getAvatarColor(messageSender) {
var hash = 0;
for (var i = 0; i < messageSender.length; i++) {
hash = 31 * hash + messageSender.charCodeAt(i);
}
var index = Math.abs(hash % colors.length);
return colors[index];
}
$(document).keydown(function(event){
if (event.keyCode == 13) {
send('CHAT');
}
});
3、後臺Java代碼編寫
Java關於WebSocket API官網地址:http://www.oracle.com/technetwork/cn/articles/java/jsr356-1937161-zhs.html
2011 年,IETF 將 WebSocket 協議標準化爲 RFC 6455。從那時起,大多數 Web 瀏覽器都在實現支持 WebSocket 協議的客戶端 API。而且,還開發了一些實現 WebSocket 協議的 Java 庫。
@ServerEndpoint("/chat")
public class ChatEndPoint {
private static Logger logger = Logger.getLogger(ChatEndPoint.class.getName());
private static List<Session> sessions = new ArrayList<>();
@OnOpen
public void onOpen (Session session, EndpointConfig config) {
logger.info("開啓連接。。。");
sessions.add(session);
}
@OnMessage
public void onMessage(Session session, String text) throws IOException {
logger.info("收到消息。。。");
// 構建發送內容
ChatMessage chatMessage = JSON.parseObject(text, ChatMessage.class);
// 發送
sendMessage(chatMessage.getSender(), chatMessage.getType(), chatMessage.getContent());
// 把用戶存入property
Map<String, Object> properties = session.getUserProperties();
properties.put("name", chatMessage.getSender());
}
@OnClose
public void onClose(Session session, CloseReason reason) throws IOException {
logger.info("關閉連接,關閉原因:" + reason.getReasonPhrase());
sessions.remove(session);
Map<String, Object> properties = session.getUserProperties();
String name = (String)properties.get("name");
sendMessage(name, MessageType.LEAVE, null);
}
@OnError
public void onError(Session session, Throwable throwable) {
logger.info("異常處理:" + throwable.getMessage());
throwable.printStackTrace();
}
/**
* 消息推送
*/
private void sendMessage(String name, MessageType type, String content) {
if (type == MessageType.JOIN) {
content = name + "-加入聊天室!";
} else if (type == MessageType.LEAVE) {
content = name + "-離開聊天室!";
}
ChatMessage chatMessage = new ChatMessage();
chatMessage.setContent(content);
chatMessage.setSender(name);
chatMessage.setType(type);
for (Session session: sessions) {
if (!session.isOpen()) {
continue;
}
try {
session.getBasicRemote().sendText(JSON.toJSONString(chatMessage));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
4、執行tomcat7:run啓動
如果需要完整源碼,請添加碼歌悠悠q: 1811119218 直接獲取,親自體驗。