java+html实现webSoket通讯

本文内容是基于腾讯云TRTCCalling API 实现的简单webSoketDemo,内容包含客户端、服务端消息互动,客户端重连、心跳机制。
腾讯云TRTCSDK 腾讯云TRTCCalling
API(桌面浏览器)

腾讯云LocalStream

效果图

在这里插入图片描述

Controller类

package com.cgjd.web.controller.system;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 *  webSoket demo
 * @author : ljl
 * @date : 14:35 2021/3/19
 */
@Controller
public class TestWebSoketController {

    @GetMapping("/soket")
    public String soket(){
        return "webSoketDemo";
    }
}

Service类

package com.cgjd.socket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocketService 通讯
 *
 * @author : ljl
 * @date : 14:06 2021/2/24
 */
@Component
@Slf4j
@ServerEndpoint(value = "/app/websocketServer/{identityId}", configurator = MySpringConfigurator.class)
public class WebSocketService {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    // 线程安全全局变量
    private static ThreadLocal<Integer> t1 = new ThreadLocal<Integer>();

    //connect key为身份ID,value为此对象this
    public static Map<String, Object> clients = new ConcurrentHashMap<String, Object>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    public Session session;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam("identityId") String identityId, Session session) {
        this.session = session;
        clients.put(identityId, this); // this = WebSocketService对象
        log.info("=========={}连接成功!当前在线人数为{}", identityId, clients.size());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("identityId") String identityId) {
        clients.remove(identityId);
        log.info("=========={}退出!当前在线人数为{}", identityId, clients.size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param identityId 客户端发送过来的消息
     * @param message 可选的参数
     */
    @OnMessage
    public void onMessage(@PathParam("identityId") String identityId, String message) {
        if ("all".equals(message)){
            String str = "系统管理员对所有人说你好";
            sendToAll(str);
        }else {
            String str = "系统管理员" + "对你【" + identityId + "】说:已收到消息【" + message + "】";
            sendToUser(identityId, str);
        }
    }

    /**
     * 发生错误时调用
     *
     * @param identityId
     * @param error
     */
    @OnError
    public void onError(@PathParam("identityId") String identityId, Throwable error) {
        log.error("=========={}发送错误{}", identityId, error);
    }


    /**
     * 给所有人发送消息
     *
     * @param msg 发送的消息
     */
    public static void sendToAll(String msg) {
        //群发消息
        for (String key : clients.keySet()) {
            WebSocketService client = (WebSocketService) clients.get(key);
            if (client == null){
                log.info("==========连接中不存在【{}】用户",key);
                break;
            }
            synchronized (client) {
                try {
                    client.session.getBasicRemote().sendText(msg);
                    log.info("==========已群发消息:{}", msg);
                } catch (IOException e) {
                    clients.remove(client);
                    log.error("{}", e);
                    try {
                        client.session.close();
                    } catch (IOException e1) {
                        log.error("{}", e);
                    }
                }
            }
        }
    }

    /**
     * 发送给指定用户
     *
     * @param user 用户名
     * @param msg  发送的消息
     */
    public static void sendToUser(String user, String msg) {
        WebSocketService client = (WebSocketService) clients.get(user);
        if (client == null){
            log.info("==========连接中不存在【{}】用户",user);
            return;
        }
        synchronized (client) {
            try {
                client.session.getBasicRemote().sendText(msg);
                log.info("==========已发送消息:{}", msg);
            } catch (IOException e) {
                clients.remove(user);
                log.error("{}", e);
                try {
                    client.session.close();
                } catch (IOException e1) {
                    log.error("{}", e);
                }
            }
        }
    }
}

Config配置类

package com.cgjd.socket;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.websocket.server.ServerEndpointConfig;

/**
 * @author : ljl
 * @date : 9:41 2021/2/25
 */

/**
 *  以websocketConfig.java注册的bean是由自己管理的,需要使用配置托管给spring管理
 */
public class MySpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {

    private static volatile BeanFactory context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MySpringConfigurator.context = applicationContext;
    }

    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return context.getBean(clazz);
    }

}

package com.cgjd.socket;

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;

/**
 * @author : ljl
 * @date : 9:35 2021/2/25
 */

@Configuration
@ConditionalOnWebApplication
public class WebSocketConfig {
    /**
     * ServerEndpointExporter 作用
     *
     * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    @Bean
    public MySpringConfigurator mySpringConfigurator() {
        return new MySpringConfigurator();
    }

}

Html

<!DOCTYPE html>
<html  lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>WebSoket Demo</title>
    <script th:src="@{/js/jquery.min.js}"></script>
    <script type="text/JavaScript">
        var lockReconnect = false;//避免重复连接
        var ws = null; //WebSocket的引用
        var wsUrl = null; //这个要与后端提供的相同
        var log = null; //记录客户端与服务器的通话
        $(function() {
            wsUrl = document.getElementById("wsUrlId").value;
            createWebSocket(wsUrl);/**启动连接**/
            // 强制退出
            window.onunload = function() {
                ws.close();
            }
        });
        //创建WebSocket连接,如果不确定浏览器是否支持,可以使用socket.js做连接
        function createWebSocket(url){
            url = document.getElementById("wsUrlId").value;
            try {
                if ('WebSocket' in window) {
                    ws = new WebSocket( url);
                } else {
                    // ws = new SockJS("http://" + url);
                }
                initEventHandle();
            } catch (e) {
                reconnect(wsUrl);
            }
        }

        function reconnect(url) {
            if(lockReconnect) return;
            lockReconnect = true;
            //没连接上会一直重连,设置延迟避免请求过多
            setTimeout(function () {
                createWebSocket(url);
                console.log("正在重连,当前时间"+new Date())
                lockReconnect = false;
            }, 5000); //这里设置重连间隔(ms)
        }

        // 记录客户端与服务器的通话
        log = function (s) {
            if (document.readyState !== "complete") {
                log.buffer.push(s);
            } else {
                document.getElementById("contentId").value += (s + "\n");
            }
        }
        /*********************初始化开始**********************/
        function initEventHandle() {
            // 连接成功建立后响应
            ws.onopen = function() {
                console.log("成功连接到" + wsUrl);
                //心跳检测重置
                heartCheck.reset().start();
            }
            // 收到服务器消息后响应
            ws.onmessage = function(e) {
                log("onmessage(), 接收到服务器消息: " + e.data);
                console.log("onmessage(), 接收到服务器消息: " + e.data);
                //如果获取到消息,心跳检测重置
                //拿到任何消息都说明当前连接是正常的
                heartCheck.reset().start();
                //Json转换成Object
                // var msg = eval('(' + e.data + ')');
                //
                // if(msg.message == "heartBeat"){
                //     //忽略心跳的信息,因为只要有消息进来,断线重连就会重置不会触发
                // }else{
                //     //处理消息的业务逻辑
                // }
            }

            // 连接关闭后响应
            ws.onclose = function() {
                console.log("关闭连接");
                reconnect(wsUrl);//重连
            }
            ws.onerror = function () {
                reconnect(wsUrl);//重连
            };
        }
        /***************初始化结束***********************/
            //心跳检测
        var heartCheck = {
                timeout: 3000,//毫秒
                timeoutObj: null,
                serverTimeoutObj: null,
                reset: function(){
                    clearTimeout(this.timeoutObj);
                    clearTimeout(this.serverTimeoutObj);
                    return this;
                },
                start: function(){
                    var self = this;
                    this.timeoutObj = setTimeout(function(){
                        //这里发送一个心跳,后端收到后,返回一个心跳消息,
                        //onmessage拿到返回的心跳就说明连接正常
                        var msg = document.getElementById("messageId");
                        ws.send(msg.value);
                        console.log("发送心跳:"+msg.value);
                        self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                            ws.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
                        }, self.timeout)
                    }, this.timeout)
                }
            }
        //发送字符串消息
        function SendMsg() {
            var msg = document.getElementById("messageId");
            console.log("SendMsg(), msg: " + msg.value);
            if (ws != null) {
                log("发送 Socket 消息: " + msg.value);
                console.log("发送 Socket 消息: " + msg.value);
                ws.send(msg.value);
            }
            else {
                log("Socket 还未创建!, msg: " + msg.value);
                console.log("Socket 还未创建!, msg: " + msg.value);
            }
        }
        function createWebSocket(){
             wsUrl = document.getElementById("wsUrlId").value;
            try {
                if ('WebSocket' in window) {
                    ws = new WebSocket( wsUrl);
                } else {
                    // ws = new SockJS("http://" + url);
                }
                initEventHandle();
            } catch (e) {
            }
        }

        // 关闭连接
        function CloseConnect () {
            console.log("CloseConnect()");
            if (ws != null) {
                ws.close();
            }
        }
    </script>
</head>

<body οnlοad="Display()">
<div id="valueLabel"></div>
<textarea rows="20" cols="30" id="contentId"></textarea>
<br/>
<input name="wsUrl" id="wsUrlId" value="ws://localhost:8080/app/websocketServer/1001"/>
<button id="createButton" onClick="javascript:createWebSocket()">Create</button>
<button id="closeButton" onClick="javascript:CloseConnect()">Close</button>
<br/>
<input name="message" id="messageId" value="1001@Hello, Server!"/>
<button id="sendButton" onClick="javascript:SendMsg()">Send</button>
<p>ws://localhost:8080/app/websocketServer/1001</p>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现即时通讯可以使用Java语言和Web技术的结合,具体可以分为以下几个步骤: 1. 服务器端实现 采用Java语言编写,可使用开源的Netty、Tomcat或Spring Boot等框架。主要功能是处理客户端的连接请求、消息传递和用户认证等操作。服务器端需要实现一个WebSocket服务器,以便客户端可以通过WebSocket协议与服务器进行实时通信。 2. 客户端实现 可采用Web技术,如HTML、CSS、JavaScript等,使用WebSocket API来实现与服务器之间的实时通信。可以使用Vue.js、React.js等框架来简化开发。 3. 数据库设计 需要设计用户信息表、好友关系表、聊天记录表等表结构,以便存储用户信息、好友列表和聊天记录等数据。 4. 消息传递实现 当客户端发送消息时,服务器端需要接收该消息并将消息转发给目标用户。可以使用Redis、RabbitMQ等消息队列技术来实现消息的传递。 参考代码: 服务器端: ``` // 使用Netty实现WebSocket服务器 public class WebSocketServer { public void run() { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler()); pipeline.addLast(new HttpObjectAggregator(64 * 1024)); pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TextWebSocketFrameHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } // 实现WebSocket消息处理 public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { channels.add(ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { channels.remove(ctx.channel()); } @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("Received text: " + msg.text()); // 解析客户端发送的消息,进行转发操作 // ... } } ``` 客户端: ``` // 使用WebSocket API实现客户端与服务器之间的通信 var webSocket = new WebSocket("ws://localhost:8080/ws"); webSocket.onopen = function(event) { console.log("WebSocket opened"); } webSocket.onmessage = function(event) { console.log("Received message: " + event.data); } webSocket.onerror = function(event) { console.log("WebSocket error: " + event); } webSocket.onclose = function(event) { console.log("WebSocket closed"); } // 向服务器发送消息 webSocket.send("Hello, server!"); ``` 以上代码仅为示例代码,实际开发中需要根据具体需求进行修改和完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值