http、socket以及websocket的区别(websocket使用举例)

发布时间 2023-07-17 19:12:22作者: spiderMan1-1

一、http、socket、websocket介绍

1、HTTP(Hypertext Transfer Protocol):HTTP是一种应用层协议,用于在客户端和服务器之间传输超文本数据。它是基于请求-响应模型的,通过发送HTTP请求从服务器获取数据,并通过HTTP响应返回数据给客户端。HTTP是无状态的,每个请求和响应都是独立的,不保留状态信息。

2、Socket:Socket是一种底层的网络通信协议,用于实现进程间的通信。它提供了一种机制,使得应用程序能够通过网络进行数据传输。Socket通常用于实现客户端和服务器之间的双向通信,可以在两个终端之间建立持久连接,允许数据的双向流动。

3、WebSocket:WebSocket是一种基于TCP的全双工通信协议,它在HTTP基础上扩展而来。与HTTP不同,WebSocket允许服务器主动向客户端推送数据,而不需要客户端发起请求。WebSocket连接一旦建立,就可以保持长时间的双向通信,类似于Socket连接,但更加灵活和容易使用。

主要的区别:

  • HTTP是一种请求-响应协议,每个请求对应一个响应。而Socket和WebSocket是基于TCP的通信协议,支持全双工通信,允许实时的双向数据传输。

  • HTTP是无状态的协议,每个请求和响应都是独立的,不保留状态信息。而Socket和WebSocket可以保持连接状态,并允许在连接上持续传输数据。

  • HTTP通常用于客户端通过发送请求从服务器获取数据。Socket和WebSocket通常用于建立客户端和服务器之间的双向通信,支持实时的消息传递。

  • WebSocket是在HTTP协议基础上的扩展,使用了握手的方式建立连接,在连接建立后,双方可以随时互相发送消息。而HTTP和Socket需要在每次通信前先建立连接。

总结来说,HTTP适用于一次性请求和响应的场景,Socket适用于需要长时间、双向通信的场景,而WebSocket适用于实时通信、推送消息的场景。

二、工作流程

websocket的工作流程:

  1. 客户端通过向服务器发送 WebSocket 升级请求来启动连接,通常是通过 HTTP 连接。升级请求包含一个特殊的标头,表明客户端想要升级到 WebSocket 通信。
  2. 服务器响应升级请求,状态码为101,表示连接已成功升级为WebSocket通信。
  3. 从这一点开始,连接以全双工模式运行,这意味着客户端和服务器都可以随时向对方发送数据。WebSocket 协议提供了一种基于消息的通信模型,其中数据作为消息传输,而不是作为字节流传输。
  4. 消息使用紧凑的二进制格式传输,这比传统 HTTP 或 TCP 使用的基于文本的格式更有效。WebSocket 协议提供了一种机制,可以自动将大消息分成较小的数据包,并在另一端将数据包重新组合成原始消息。
  5. WebSocket 连接一直保持打开状态,直到客户端或服务器决定关闭它。关闭连接是一个简单的两步过程,包括发送关闭消息,然后等待另一端确认关闭消息。
    socket工作流程:
  6. 初始化:客户端和服务器应用程序各自创建一个套接字并将其绑定到特定的端口号。端口号用于标识网络上的应用程序。
  7. 连接建立:客户端通过向服务器的IP地址和端口号发送请求来建立与服务器的连接。服务器接受连接并为客户端创建一个新套接字。
  8. 数据交换:一旦建立连接,客户端和服务器就可以使用套接字相互交换数据。数据以数据包的形式发送,数据包是通过网络传输的小数据单元。
  9. 优雅关闭:当客户端和服务器完成交换数据时,它们通过向另一方发送消息以指示它们正在关闭连接来优雅地关闭连接。
  10. 终止:连接关闭后,客户端和服务器可以终止各自的套接字。

三、websocket代码实现

1、引入依赖

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

2、后端实现

2.1创建 WebSocketConfig 配置类,注入 websocket 服务

/**
 * 开启WebSocket支持
 **/
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

2.2新建WebSocketServer服务端:WebSocket.java


/**
 * @Description :
 * @Date : 2023/7/17
 * @Author :
 */
@ServerEndpoint(value = "/websocket/{userId}")
@Slf4j
@Component
@Data
public class WebSocket {

    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
    private static ConcurrentHashMap<String, Session> webSocketSet = new ConcurrentHashMap<>();

    //指定的sid,具有唯一性
    private static String sid = "";

    
    


    /**
     * 连接建立成功调用的方法
     *
     * @param session 会话
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) throws Exception {
        sid = userId;
        webSocketSet.put(sid, session);     //加入set中
        log.info("【websocket消息】有新的连接,总数为:" + webSocketSet.size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        log.info("参数信息:{}", message);
        //群发消息
        try {
            log.info("【收到】,客户端{}的信息:{}", session, message);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("参数有误");

        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        webSocketSet.remove(this);
        if (session != null) {
            try {
                session.close();
                log.info("【websocket消息】关闭了一个连接,总数为:" + webSocketSet.size());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session 会话
     * @param error   错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("连接异常!");
        error.printStackTrace();
    }

    /**
     * 发送信息
     *
     * @param message 消息
     */
    public void sendMessage(String message,String userId) throws IOException {
        // 防止频繁断连问题引发的报错: The WebSocket session [3d] has been closed and no method (apart from close()) may be called on a closed session
        Session session = webSocketSet.get(userId);
        if (session.isOpen()) {
            session.getBasicRemote().sendText(message);
        }
    }

    /**
     * 自定义消息推送、可群发、单发
     *
     * @param message 消息
     */
    public  boolean sendAllInfo(String message, List<String> userIds) {
        log.info("给用户" + userIds.toString() + "发送消息:" + message);
        if (CollectionUtils.isEmpty(userIds)){return false;}
        for (String userId:userIds){
            try {
                sendMessage(message,userId);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

}

注:@ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。
onOpen 和 onClose 方法分别被@OnOpen和@OnClose 所注解。这两个注解的作用不言自明:他们定义了当一个新用户连接和断开的时候所调用的方法。
onMessage 方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。
2.3 测试controller类

  @Resource
    private WebSocket socketService;
    @PostMapping("/test/socket")
    public void testSocket(String msg){
        try {
            int i = 0;
            while (true){
                i++;
                socketService.sendMessage("ces","1");
                if (i == 100){
                    log.info("i到一百了");
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3、前端实现

<!DOCTYPE html>
<html>
 
	<head>
		<meta charset="utf-8" />
		<title></title>
	</head>
 
	<body>
	    <button onclick="send()">发送消息</button>
	</body>

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
    </script>
	<script>
		var websocket = null;
		//判断浏览器是否支持websocket
		if('WebSocket' in window) {
			//实现化WebSocket对象
			websocket = new WebSocket("ws://localhost:8088/websocket/1");
		} else {
			alert('该浏览器不支持websocket')
		}
 
        //打开事件  
		websocket.onopen = function(event) {
			console.log('建立连接');
		}
		//关闭事件  
		websocket.onclose = function(event) {
			console.log("连接关闭");
		}
		//获得消息事件  
		websocket.onmessage = function(event) {
			console.log("收到消息:" + event.data);
		}
		
		//发生了错误事件  
		websocket.onerror = function(event) {
			console.log("websocket 通信发生错误");
		}
		window.onbeforeunload = function(event) {
			websocket.close();
		}
 
		//发送消息
		function send() {
		
            $.ajax({
                type: "post",
                url: "http://localhost:8088/test/socket",
                data: {
                    msg: "23"
                },
                dataType: "json",
                async: false,
                success: function(data) {
                    console.log(data);
                },
                error: function(e) {
                    console.log(e);
                }
            });
		}
	</script>
 
</html>

请求的时候出现跨域问题,解决,在后端加配置类


/**
 * AJAX请求跨域
 * @author Mr.W
 * @time 2018-08-13
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedHeaders(CorsConfiguration.ALL)
                .allowedMethods(CorsConfiguration.ALL)
                .allowCredentials(true)
                .maxAge(3600);
    }
}