Application scenarios

barrage
News subscription
Multi-player games
Collaborative editing
Stock fund real-time quotes
Video conference
online education
The chat room

conclusion

  1. Both WebSocket and HTTP are based on TCP. Two completely different application layer protocols
  2. WebSocket relies on HTTP connections for the first handshake
  3. Socket is not a protocol, it is an interface encapsulation of the transport layer protocol at the program level, which can be understood as a call interface (API) that can provide end-to-end communication.

The instance

springboot

Rely on

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
Copy the code

Built-in Tomcat injection beans

import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @ Configuration @ ConditionalOnWebApplication public class WebSocketConfig {/ / note: @bean public ServerEndpointExporter ServerEndpointExporter() {return new ServerEndpointExporter(); }}Copy the code

Websocket implementation class

import cn.hutool.core.collection.CollectionUtil; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @slf4j @Serverendpoint (value = "/ Interface name /{userId}") @Component Public class MyWebSocketService {private Static Final String  NULL_KEY = "null"; */ private static final Long BEAT_HEART_DURATION_TIME_MILLIS = 10 * 60 * 1000L; Private static AtomicInteger onlineCount = new AtomicInteger(0); private static AtomicInteger onlineCount = new AtomicInteger(0); /** * A thread-safe Map of the Concurrent package, used to hold Session objects corresponding to each client. */ public static Map<String, Session> clients = new ConcurrentHashMap<String, Session>(); /** * A thread-safe Map of the Concurrent package, used to hold Session objects corresponding to each client. */ private static Map<Session, String> sessionMap = new ConcurrentHashMap<Session, String>(); private static Map<String, Session> oldClients = new ConcurrentHashMap<String, Session>(); private static Map<Session, Long> sessionBeatheartMap = new ConcurrentHashMap<Session, Long>(); @Autowired private MessageService messageService; @OnOpen public void onOpen(@PathParam("userId") String userId, Session session) { if (StringUtils.isEmpty(userId) || NULL_KEY.equalsIgnoreCase(userId)) { try { Log.warn ("[key={}] illegal, forbidden connection!!" , userId); session.close(); } Catch (IOException e) {}} if (client.containsKey (userId)) {// destroyOldSession(userId); } addOnlineCount(); clients.put(userId, session); sessionMap.put(session, userId); sessionBeatheartMap.put(session, System.currentTimeMillis()); Log.info (" New connection [userId={}] join! Current number of online connections {}", userId, getOnlineCount()); } @OnClose public void onClose(Session session) { String key = sessionMap.get(session); if (StringUtils.isNotEmpty(key)) { if (clients.containsKey(key)) { clients.remove(key); SubOnlineCount (); } sessionMap.remove(session); sessionBeatheartMap.remove(session); Log.info (" Connection [userId={}] closed! {}", key, getOnlineCount()); /** Notify the system to disconnect **/ destroyOldSession(key); } } @Scheduled(cron = " */5 * * * * ?" ) public void processTerminalInformation() { if (clients.isEmpty()) { return; } clients.forEach((k, v) -> { try { List<Message> messages = messageService.getMessageLists(k); if (CollectionUtil.isNotEmpty(messages)) { v.getAsyncRemote().sendText(JSON.toJSONString(messages)); } } catch (Exception e) { destroyOldSession(k); }}); } @Scheduled(cron = "0 */1 * * * ?" ) public void processOnlineTime() { oldClients.forEach((k, v) -> { try { Long lastBeatTime = sessionBeatheartMap.get(v); if (lastBeatTime == null || (System.currentTimeMillis() - lastBeatTime) > BEAT_HEART_DURATION_TIME_MILLIS) { DestroyOldSession (k); // If no null message is received after 90 seconds, the KEY device is disconnected. }} catch (Exception e) {// destroyOldSession(k); }}); oldClients = clients; } private void destroyOldSession(String key) { Session oldSession = clients.get(key); if (oldSession ! = null) { if (clients.containsKey(key)) { subOnlineCount(); clients.remove(key); if (oldSession ! = null) { sessionMap.remove(oldSession); sessionBeatheartMap.remove(oldSession); } to try {oldSession. Close (new CloseReason (CloseReason. CloseCodes. NORMAL_CLOSURE, disconnected! "" )); } catch (IOException e) { } } } } public static synchronized AtomicInteger getOnlineCount() { return onlineCount; } / * * * * the number of connection/public static synchronized void addOnlineCount () {onlineCount. IncrementAndGet (); } / reduce the connection number * * * * / public static synchronized void subOnlineCount () {onlineCount. DecrementAndGet (); }}Copy the code

Vue

usage

created() { this.initWebSocket(); }, methods: { initWebSocket() { let token = localStorage.getItem('token'); Const url = 'ws://127.0.0.1: port number/interface name /' + token; this.websocket = new WebSocket(url); this.websocket.onopen = this.websockOpen; this.websocket.onmessage = this.websocketonmessage; this.websocket.onclose = this.websocketclose; }, websockOpen() {console.log("WebSocket connected successfully "); }, webSocketonMessage (e) {// Data receive console.log(e); }, webSocketClose (e) {// Close console.log("close.." )}, logout () {/ / the end of the part is closed websocket link window. The localStorage. RemoveItem (" token "); this.$router.push({path: '/login'}) this.websocket.close(); }},Copy the code

Conclusion at

Ajax, Long Poll, Websocket schema

The keep-alive connection is enabled by default in HTTP1.1, which maintains the TCP channel so that multiple requests can be sent and multiple responses can be received in an HTTP connection, but only one Response can be received in a Request. Moreover, this response is also passive and cannot be initiated actively

Protocol,

Each WebSocket connection starts with an HTTP request. When the WebSocket protocol shakes the connection for the first time, it transmits the WebSocket supported version number, the protocol version number, the original address, the host address and some column fields to the server through HTTP:
GET/chat HTTP / 1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Origin: xxx
Sec-WebSocket-Version: 13

The Upgrade header is used to Upgrade current HTTP requests to the WebSocket protocol, which is part of the HTTP protocol itself and is extended to support other communication protocols. If the server supports the new protocol, you must return 101:

HTTP / 1.1 101 Switching separate Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

At this point, the HTTP request is used, if the onopen event is successfully launched, otherwise the onError event is raised, and the subsequent transmission no longer depends on the HTTP protocol.

Why rely on HTTP

  1. WebSocket is designed to be HTTP by nature To enhance Communication (full duplex communication, etc.), so it is a natural thing to do on the basis of HTTP protocol connection, and therefore can get a lot of HTTP convenience.
  2. One of these conveniences is important, and http-based connections will get the biggest one Compatible with Support, such as setting up HTTP communication even if the server does not support WebSocket but returns onError, is obviously better than no server response.

Refer to the article

Clarify the relationship between WebSocket and HTTP

Zero contact with websocket


Click here to like 👇 to share more technical articles and help more people, here is all my knowledge ~ 🐌www.yuque.com/yingwenerji…