基于 WebSocket 打造聊天室

WebSocket 入门:简易聊天室 大家好,我是前端西瓜哥,今天我们用 WebSocket 来实现一个简单的聊天室。WebSocket 是一个应用层协议,有点类似 HTTP。但和 HTTP 不一样的是,它支持真正的全双工,即不仅客户端可以主动发消息给服务端,服务端也可以主动发消息给客户端。尤其是后者,让我们不用再基于 HTTP 长轮询或短轮询的低效方式来实现服务端通知。相比 HTTP,WebSocket 的服务端推送更轻量,并能减少服务端的压力。 阅读详情

一、什么是 WebSocket?

WebSocket 是一种基于TCP连接上进行 全双工 通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在这里插入图片描述

二、WebSocket 的应用场景

WebSocket 的特点,决定了他在一些 实时双向通信、低延迟、推送等功能的应用场景下的独特优势。

在WebSocket 出现之前,Web 程序想要进行实时通信是非常麻烦的,因为 HTTP 是一种无状态、无连接、单向的应用层协议,它无法实现服务器主动向客户端推送消息,并且这种单向请求的特点,注定了客户端实时获取服务端的变化就非常麻烦,通常情况下,需要客户端频繁的对服务器进行轮询,这种方式不仅效率低,实时性不好,而且这种频繁的请求和响应对服务器来说也是一种负担。

三、WebSocket API

1、客户端/浏览器 API

由于 WebSocket 协议是 HTML5开始支持的,所以想要使用 WebSocket 协议,首先保证浏览器支持 HTML5.

websocket 对象创建

let ws = new WebSocket(URL);

注:这里的 url 格式为 协议://ip地址:端口号/访问路径。协议名称固定为 ws

websocket 事件

事件事件处理程序描述
openws.onopen连接建立时触发
messagews.onmessage客户端接收服务端数据时触发
errorws.onerror通信发生错误时触发
closews.onclose连接关闭时触发

websocket 方法

方法描述
ws.send()使用连接发送数据到服务器
ws.close()关闭连接

2、服务端 API

Tomcat 从 7.0.5 版本开始支持 WebSocket,所以使用时注意 Tomcat 版本不能低于 7.0.5。在Java中,Endpoint 表示服务器 Websocket 连接的一端,可以视之为处理WebSocket消息的接口。Java 中可以通过两种方式定义Endpoint:

  1. 编程式:通过继承 javax.websocket.Endpoint并实现其方法。
  2. 注解式:通过添加 @ServerEndpoint 等相关注解。

Endpoint 实例在 WebSocket 握手时创建,并在客户端与服务端连接过程中有效,在连接关闭时结束,在Endpoint接口中就定义了其生命周期的相关方法:

方法注解描述
onOpen@OnOpen注解所标识的方法连接建立时触发
onError@OnError注解所标识的方法通信发生错误时触发
onClose@OnClose注解所标识的方法连接关闭时触发

在 onOpen 方法中,将创建一个 Session 对象,表示当前 WebSocket 连接的会话,Session对象提供了一组方法,用于管理WebSocket连接和事件。例如:

服务端向客户端发送数据:

如果使用编程式的话,可以通过 Session 添加 MessageHandler 消息处理器来接收消息;如果采用注解式,可以在定义的 Endpoint 类中添加 @OnMessage 注解指定接收消息的方法。

服务端将数据推送给客户端:

在 Session 中维护了一个 RemoteEndpoint 实例,用来向客户端推送数据,我们可以通过 Session.getBasicRemote 获取同步消息发送实例,通过 Session.getAsyncRemote 获取异步消息发送实例,然后调用实例中的 sendXXX() 方法即可发送消息。

四、使用 webSocket 实现在线聊天室

上面说了那么多理论知识,主要是为了让大家对Websocket有个最基本的认识,下面就通过一个具体的场景《在线聊天室》来体会一下 Websocket 在实际中如何使用。

1、需求分析

这里我们实现的聊天室是比较简单的,主要功能就是能够实现实时消息的发送和接收,能够实时显示用户在线状态和在线人数。下面简单演示一下成品效果:

2、实现流程

3、消息格式

这里约定前后端的消息数据格式采用 json 格式进行传输。主要分为两大类消息:

  1. 客户端–>服务器:
{"toName":"张三","message":"你好"}
  1. 服务器–>客户端:
{"isSystem":true,"fromName":null,"message":["张三","李四"] }
{"isSystem":false,"fromName":"张三","message":"你好" }

4、服务端功能实现

这部分我会详细介绍一下使用 websocket,后端如何进行api编写,至于登录功能,可以说是千篇一律,这里直接给出代码,不作为重点,也就不在展开介绍了。

(1)导入依赖
首先我们需要创建 SpringBoot 项目并导入 websocket 相关依赖:

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

(2)登录模块实现
登录功能不是本次实现的重点,所以这里只是对密码进行了一个简单的校验,不涉及到数据库交互操作。

@RestController
@RequestMapping("/user")
public class UserController {
    /**
     * 登录
     * @param user 提交的用户名和密码
     * @param httpSession 提交的
     * @return 成功信息
     */
    @PostMapping("/login")
    public Result login(User user, HttpSession httpSession) {
        if (user != null && (user.getPassword()).equals("123")) {
            // 将用户数据存储到session中
            httpSession.setAttribute("user",user.getUsername());
            System.out.println(httpSession);
            return Result.success("登录成功");
        } else {
            return Result.failed();
        }
    }

    /**
     * 获取当前登录的用户名
     * @return 用户名
     */
    @GetMapping("/getusername")
    public Result login(HttpSession httpSession) {
        String username = (String) httpSession.getAttribute("user");
        return Result.success(username);
    }

}

(3)聊天室功能实现

@Component
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class)
public class ChatEndpoint {
    // 用来存储每一个客户端对象对应的ChatEndpoint对象
    private static ConcurrentHashMap<String,ChatEndpoint> onlineClient = new ConcurrentHashMap<>();
    // 声明session对象,通过该对象可以发生消息给指定的客户端
    private Session session;
    // 声明Httpsession对象,里面记录了用户相关信息
    private HttpSession httpSession;

    /**
     * 连接建立时调用
     * @param session 当前连接的会话
     * @param config Endpoint的配置对象
     */
    @OnOpen
    public void onOpen(Session session,EndpointConfig config) {
        try {
            // 初始化当前Endpoint的链接会话
            this.session = session;
            // 初始化httpSession
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            this.httpSession = httpSession;
            // 将当前连接的ChatEndpoint对象存储到onlineClient容器中
            String username = (String) httpSession.getAttribute("user");
            onlineClient.put(username,this);
            // 将在线用户推送给所有客户端
            // 1.获取消息
            String message = MessageUtils.getMessage(true, null, getNames());
            // 2.将消息广播给所有用户
            broadcastAllUsers(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void broadcastAllUsers(String message) {
        Set<String> names = getNames();
        try {
            for (String name : names) {
                // 获取当前用户的ChatEndpoint对象
                ChatEndpoint client = onlineClient.get(name);
                // 使用session发送信息
                client.session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Set<String> getNames() {
        return onlineClient.keySet();
    }

    /**
     * 接收到客户端数据后调用
     * @param message 消息信息
     * @param session 当前连接的会话
     */
    @OnMessage
    public void onMessage(String message,Session session) {
        try {
            // 导入jackson核心类
            ObjectMapper objectMapper = new ObjectMapper();
            // 将JSON字符串序列化为Message对象
            Message mess = objectMapper.readValue(message, Message.class);
            // 取出接收人
            String toName = mess.getToName();
            // 取出信息
            String sendMess = mess.getMessage();
            // 找到发送人姓名
            String fromName = (String) httpSession.getAttribute("user");
            // 进行消息构造
            String finalMess = MessageUtils.getMessage(false, fromName, sendMess);
            // 根据接收人找到对应的连接
            ChatEndpoint chatEndpoint = onlineClient.get(toName);
            if (chatEndpoint == null) {
                // 这里可以抛出异常
                // ...
                // 打印日志
                System.out.println("找不到对应的客户端!userName = "+toName);
                return;
            }
            // 使用对应的会话Session发送给对应的客户端
            chatEndpoint.session.getBasicRemote().sendText(finalMess);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 连接过程中发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("websocket连接出错,错误原因:"+error.getMessage());
    }

    /**
     * 关闭连接时调用
     */
    @OnClose
    public void onClose(Session session) {
        try {
            // 1.清除容器中的该ChatEndpoint对象
            String username = (String) httpSession.getAttribute("user");
            onlineClient.remove(username);
            // 2.向所有客户端广播下线通知
            Set<String> names = getNames();
            // 3.构造消息
            String message = MessageUtils.getMessage(true,null,getNames());
            // 4.广播给其他用户
            broadcastAllUsers(message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

需要注意的是:

  1. 事实上当@Component注解和@ServerEndpoint(“/chat”)注解同时使用时,Spring并不会将ChatEndpoint这个类注册为服务器的端点,我们还需要需要注册一个 ServerEndpointExporter bean,它会自动扫描带有@ServerEndpoint注解的类,并将其注册为WebSocket端点,以便客户端可以连接和通信:
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
  1. 在WebSocket中,每次客户端与服务器建立连接时,都会创建一个新的ChatEndpoint实例来处理该连接。因此,每个WebSocket连接都有其自己的ChatEndpoint实例,this指向的是当前连接对应的实例,所以在ChatEndpoint类中使用了一个static类型的集合将所有的连接记录起来,便于之后的消息处理。

  2. 在客户端与服务端建立连接时,上述的静态集合中是以 键-值 方式存储每个客户端的ChatEndpoint实例的,为了区分,这里以登录的用户名作为键,所以需要获取到HttpSession,再从Httpsession中获取到用户名,但是在OnOpen方法中无法直接获取当前登录的 Httpsession,此时我们可以借助OnOpen中的另一个参数 EndpointConfig config,将Httpsession存储到EndpointConfig对象中,在从EndpointConfig对象中获取到Httpsession,因此我们需要进行如下配置:

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec,
     							HandshakeRequest request, 
     							HandshakeResponse response) {
        // 获取到httpSession
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        // 将httpSession存储到EndpointConfig中
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

5、客户端交互功能实现

客户端这里主要介绍一下前后端交互的逻辑,具体页面实现我们暂时不关心。
(1)登录页面前后端交互
登录页面的前后端交互主要采用 jQuery 发送 Ajax 请求完成,整体思路很简单,这里给出 js 代码:

<script>
    function login(){
        // 1.参数校验
        var username = jQuery("#username");
        var password = jQuery("#password");
        if (username.val().trim() == "") {
            username.focus();
            alert("请输入用户名!")
            return false;
        }
        if (password.val().trim() == "") {
            username.focus();
            alert("请输入密码!");
            return false;
        }
        // 2.将参数提交给后端
        jQuery.ajax({
            url:"/user/login",
            type:"POST",
            data:{
                "username":username.val().trim(),
                "password":password.val().trim()
            },
            success:function(res) {
            console.log(res);
                // 3.将结果返回给用户
                if (res.flag) {
                    // 登录成功!
                    // alert("登录成功!");
                    // 跳转到主页
                    location.href="main.html";
                } else {
                    // 登录失败!
                    alert("登录失败,用户名或密码错误!");
                }
            }
        });
    }
</script>

(2)聊天页面的前后端交互
在聊天页面这里,使用到 vue 进行处理,可能理解起来稍微有点复杂,大家可以自行参考:

<body>
<div class="abs cover contaniner" id="app">
    <div class="abs cover pnl">
        <div class="top pnl-head" style="padding: 20px ; color: white;">
            <div id="userName">
                用户:{{username}}
                <span style='float: right;color: green' v-if="isOnline">在线</span>
                <span style='float: right;color: red' v-else>离线</span>
            </div>
            <div id="chatMes" v-show="chatMes" style="text-align: center;color: #6fbdf3;font-family: 新宋体">
                正在和 <font face="楷体">{{toName}}</font> 聊天
            </div>
        </div>
        <!--聊天区开始-->
        <div class="abs cover pnl-body" id="pnlBody">
            <div class="abs cover pnl-left" id="initBackground" style="background-color: white; width: 100%">
                <div class="abs cover pnl-left" id="chatArea" v-show="isShowChat">
                    <div class="abs cover pnl-msgs scroll" id="show">
                        <div class="pnl-list" id="hists"><!-- 历史消息 --></div>
                        <div class="pnl-list" id="msgs" v-for="message in historyMessage">

                            <!-- 消息这展示区域 -->
                            <div class="msg guest" v-if="message.toName">
                                <div class="msg-right">
                                    <div class="msg-host headDefault"></div>
                                    <div class="msg-ball">{{message.message}}</div>
                                </div>
                            </div>
                            <div class="msg robot" v-else>
                                <div class="msg-left" worker="">
                                    <div class="msg-host photo"
                                         style="background-image: url(img/avatar/Member002.jpg)"></div>
                                    <div class="msg-ball">{{message.message}}</div>
                                </div>
                            </div>

                        </div>
                    </div>



                    <div class="abs bottom pnl-text">
                        <div class="abs cover pnl-input">
                            <textarea class="scroll" id="context_text" @keyup.enter="submit" wrap="hard" placeholder="在此输入文字信息..."
                                      v-model="sendMessage.message"></textarea>
                            <div class="abs atcom-pnl scroll hide" id="atcomPnl">
                                <ul class="atcom" id="atcom"></ul>
                            </div>
                        </div>

                        <div class="abs br pnl-btn" id="submit" @click="submit"
                             style="background-color: rgb(32, 196, 202); color: rgb(255, 255, 255);">
                            发送
                        </div>
                        <div class="pnl-support" id="copyright"><a href="https://blog.youkuaiyun.com/LEE180501?spm=1000.2115.3001.5343">仅供学习参考</a></div>
                    </div>
                </div>

                <!--聊天区 结束-->
                <div class="abs right pnl-right">
                    <div class="slider-container hide"></div>
                    <div class="pnl-right-content">
                        <div class="pnl-tabs">
                            <div class="tab-btn active" id="hot-tab">好友列表</div>
                        </div>
                        <div class="pnl-hot">
                            <ul class="rel-list unselect">
                                <li class="rel-item" v-for="friend in friendsList"><a @click='showChat(friend)'>{{friend}}</a>
                                </li>
                            </ul>
                        </div>
                    </div>

                    <div class="pnl-right-content">
                        <div class="pnl-tabs">
                            <div class="tab-btn active">系统广播</div>
                        </div>
                        <div class="pnl-hot">
                            <ul class="rel-list unselect" id="broadcastList">
                                <li class="rel-item" style="color: #9d9d9d;font-family: 宋体" v-for="name in systemMessages">您的好友
                                    {{name}} 已上线</li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="js/vue.js"></script>
<script src="js/axios-0.18.0.js"></script>
<script>
    let ws;
    new Vue({
        el: "#app",
        data() {
            return {
                isShowChat: false,
                chatMes: false,
                isOnline: true,
                username:"",
                toName: "",
                sendMessage: {
                    toName: "",
                    message: ""
                },
                inputMessage: "",
                historyMessage: [

                ],
                friendsList: [

                ],
                systemMessages : [

                ]
            }
        },
        created() {
            this.init();
        },
        methods: {
            async init() {
                await axios.get("user/getusername").then(res => {
                    this.username = res.data.message;
                });

                //创建webSocket对象
                ws = new WebSocket("ws://127.0.0.1:8080/chat");

                //给ws绑定事件
                ws.onopen = this.onopen;
                //接收到服务端推送的消息后触发
                ws.onmessage = this.onMessage;

                ws.onclose = this.onClose;
            },
            showChat(name) {
                this.toName = name;
                //清除聊天区的数据
                let history = sessionStorage.getItem(this.toName);
                if (!history) {
                    this.historyMessage = [];
                } else {
                    this.historyMessage = JSON.parse(history);
                }
                //展示聊天对话框
                this.isShowChat = true;
                //显示“正在和谁聊天”
                this.chatMes = true;
            },
            submit() {
                this.sendMessage.toName = this.toName;
                this.historyMessage.push(JSON.parse(JSON.stringify(this.sendMessage)));
                sessionStorage.setItem(this.toName, JSON.stringify(this.historyMessage));
                ws.send(JSON.stringify(this.sendMessage));
                this.sendMessage.message = "";
            },
            onOpen() {
                this.isOnline = true;
            },
            onClose() {
                sessionStorage.clear();
                this.isOnline = false;
            },
            onMessage(evt) {
                //获取服务端推送过来的消息
                var dataStr = evt.data;
                //将dataStr 转换为json对象
                var res = JSON.parse(dataStr);

                //判断是否是系统消息
                if(res.system) {
                    //系统消息  好友列表展示
                    var names = res.message;
                    this.friendsList = [];
                    this.systemMessages = [];
                    for (let i = 0; i < names.length; i++) {
                        if(names[i] != this.username) {
                            this.friendsList.push(names[i]);
                            this.systemMessages.push(names[i]);
                        }
                    }
                }else {
                    //非系统消息
                    var history = sessionStorage.getItem(res.fromName);
                    if (res.fromName == this.toName) {
                        if (!history) {
                            this.historyMessage = [res];
                        } else {
                            this.historyMessage.push(res);
                        }
                        sessionStorage.setItem(res.fromName, JSON.stringify(this.historyMessage));
                    } else {
                        if (!history) {
                            sessionStorage.setItem(res.fromName, JSON.stringify([res]));
                        } else {
                            let messages = JSON.parse(history);
                            messages.push(res);
                            sessionStorage.setItem(res.fromName, JSON.stringify(messages));
                        }
                    }

                }
            }
        }
    });
</script>

6、聊天室完整资源

完整资源请点击: Gitee 在线聊天室

WebSocket实现聊天功能 登录界面模仿QQ登录操作,支持拖动、最小化和关闭登录后的右侧显示在线用户,右下方显示在线用户的登录日志窗口支持拖动、关闭操作在线用户实现及时聊天功能,可以对指定用户发起聊天(实现点对点的消息推送功能)支持消息的本地缓存,聊天内容不会因为页面的刷新、关闭、退出登录等操作而丢失(消息只是做了简单缓存),清理浏览器缓存数据时消息数据便会丢失。点对点消息推送,实现一对一的即时通信功能。... 阅读详情

相关推荐

基于Springboot+WebSocket+Netty实现在线聊天、群聊系统

此文主要实现在好友添加、建群、聊天对话、群聊功能,使用Java作为后端语言进行支持,界面友好,开发简单。

wml_JavaKill的博客 2万+

使用WebSocket实现网页聊天室

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。

黑马程序员官方博客 7176

SpringBoot 使用WebSocket打造在线聊天室(基于注解)

推荐WebSocket的三大理由: 1、采用全双工通信,摆脱传统HTTP轮询的窘境。2、采用W3C国际标准,完美支持HTML5。3、简单高效,容易上手。 学习目标 快速学会通过WebSocket编写简单聊天功能。 快速查阅 专题阅读:《SpringBoot 布道系列》 温馨提示:1、WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速...

ITjianshuzhai的博客 881

Websocket 技术实践 实现在线聊天系统

WebSocket是一种在单个TCP连接上进行全双工通信的协议。全双工:可以同时双向传输数据(B->A,A->B可以同时进行)。推送技术是建立在客户端服务器的一种机制,就是由服务器主动将信息发往客户端的技术。就像广播电台播音。

qq_50909707的博客 3987

WebSocket实战:打造实时在线聊天室

我们应该了解的是websocket是ISO参考模型的应用层的协议之一它是一种在大呢TCP连接上进行全双工也就是双向通信的协议。有了这个协议,客户端和服务器之间就可以随时互相发送数据,不再像传统HTTP那样一问一答了也就是请求响应。当客户端和服务器的连接一旦建立,不手动关闭或者设置自动关闭就会一直保持。比如我们想要实现网上聊天室或者网上棋类在线对战游戏HTTP 是“请求-响应”模式: 客户端发起请求,服务器被动响应。但这种模式有几个问题:如果我们使用websocket连接过程如下同理客户端2的websocke

iov_a的博客 1262

WebSocket基础应用:打造实时聊天室

WebSocket是一种在单个TCP连接上进行全双工通信的协议,它为Web应用程序提供了实时通信能力。与传统的HTTP请求/响应模型不同,WebSocket允许服务器主动发送信息给客户端,因此非常适合需要快速、实时数据交换的应用,如聊天应用、实时游戏、股票交易平台等。

weixin_35696112的博客 972

springboot 打造属于你的聊天室(WebSocket)

Webscoket对浏览器有一定的要求,所以使用之前要考虑兼容性的问题…. Webscoket WebSocket是 HTML5 新增的一种在单个 TCP 连接上进行全双工通讯的协议,与 HTTP 协议没有太大关系…. 在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。 浏览器通过J...

qq_37996327的博客 3147

汇川EASY系列之以太网通讯(套接字socket做主站)

本文讲解汇川Easy系列使用以太网套接字socket通讯,本例中EasyPLC做主站。文中按步进行操作。细节明确。

这是一个长期关注工控行业技术的博客,实现0-1的突破,期待您的关注和留言 2191

【PHP开发900个实用技巧】425.WebSocket聊天室:PHP打造实时通信平台

《PHP+WebSocket打造万人级实时聊天室实战指南》摘要: 本文深入讲解如何使用PHP原生实现高性能WebSocket聊天室,突破HTTP协议的限制。主要内容包括:1)WebSocket协议握手与帧解析关键技术;2)连接池管理与心跳检测机制设计;3)万人级广播的优化策略(分组管理、零拷贝发送、消息分片);4)生产环境部署方案(Nginx配置、系统调优、压力测试)。通过SplObjectStorage管理连接、智能分片算法等核心技巧,解决内存泄漏、消息延迟等常见问题。文章包含完整源码示例和性能优化che

758

【黑马点评优化】2-Canel实现多级缓存(Redis+Caffeine)同步

【黑马点评优化】2-Canel实现多级缓存(Redis+Caffeine)同步

m0_52031708的博客 3848

提供一个微信投票刷票的js代码

最近几天,有个朋友找我去写一个脚本帮他 在微信上刷票、微信投票刷票,我大概看了一下,本次微信投票刷票其实就是根据微信的openid来限制,每个openid只能投三次票的限制。上图可以看出,其实只要openid,和toopenid即可,openid是自己的微信id,toopenid是投票对象的微信id,那我们定时修改openid即可给某一个人刷投票。把这段代码复制到浏览器F12之后的console里,直接回车即可开始微信投票刷票(刷新或关闭浏览器即可停止刷票)//生成随机的ip地址。

qq_858865388专业提供快速微信投票教程攻略和方法的博客 2万+

前端websocket打造实时聊天室

前端运用websocket打造的实时消息通知的聊天室,语言采用了JavaScript,可以进行登录系统,多用户实时聊天,后端服务器采用nodejs编写(我会把代码直接贴上)代码是用最原生的js写的,如果利用框架例如vue,angular等,代码会变的更少更简洁。...

平平无奇小码农的博客 3673

Fiori Elements 表格之 Tree Tables:从数据建模到交互行为的全链路剖析

SAP Fiori elements的Tree Table组件将分层数据以表格形式展示,支持展开/折叠操作,适用于组织结构、物料清单等多级数据场景。该组件在List Report和Object Page中均可使用,支持懒加载、多选、列冻结等标准表格功能,并与OData V4服务层级注解深度集成。后端需通过RAP或CAP建模提供层级数据,前端通过清单配置切换表类型。需注意在草稿模式下仅显示活动实体,且展开状态在编辑时不保留。性能方面建议采用懒加载策略,避免全量加载树节点。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 159

WebSocket打造在线聊天室

WebSocket介绍 WebSocket是一种网络通信协议。RFC6455定义了它的通信标准。 WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议 HTTP协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。 这种通信模型有一个弊端:HTTP协议无法实现服务器主动向客户端发起消息。 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数Web应用程序将通过频繁的异步AJAX

m0_57249797的博客 1495

半导体设备系列(2) 半导体设备与工厂控制仿真器Demo编写

基于C/S架构用C++编写半导体设备仿真器与工厂控制仿真器

追赶时代的博客 610

sphincs+ Nist Round3文档中文译版(SPHINCS+ Submission to the NIST post-quantum project, v.3)

1. 引言 哈希基础签名方案最初是由Lamport [14]在1970年代后期作为一次性签名方案开发的,并由Merkle [16]扩展为多次签名方案。这些方案的安全性易于分析,并仅依赖于所使用的哈希函数的属性。然而,Merkle基于树的签名方案需要在密钥生成时就确定要进行签名的数量,且为了保持性能这个数字要小。最重要的是,该系统需要用户记住一个状态:这个状态包含一些信息以记住密钥已经进行了多少次签名。 在Lamport方案诞生后的40年里,产生了许多想法改进了哈希基础签名的性能、实用性和理论基础,最终XMS

dedanddwb的博客 394

websocket打造在线聊天室

WebSocket是一种网络通信协议。RFC6455定义了他的标准。WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议HTTP协议是一种无状态的,无连接的,单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求作出应答处理这种通信模型有一种弊端:HTTP协议无法实现服务器主动向客户端发起消息这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数web应用程序将通过频繁的异步AJAX请求实现长轮询。

qq_73182976的博客 808

098、C3k2_Ghost:用 GhostConv 替代 C3k2 内标准卷积的轻量化改造代码

本文介绍了用GhostConv轻量化改造YOLOv11中C3k2模块的实践。作者通过线上部署案例引出问题,详细解析了GhostConv的核心实现逻辑,指出直接替换标准卷积会导致的两个关键问题:通道数不匹配和kernel_size冲突。文章提供了完整的C3k2_Ghost实现代码,包含注意事项和调试技巧。消融实验显示,该方法在小模型上可减少27%-28%参数量、34%-35%FLOPs,仅损失0.8mAP。作者总结了适用场景:小模型效果显著,大模型需谨慎;检测小目标任务不推荐;训练时需调整学习率。最后强调sh

sundong_81的博客 62

教你从零开始用WebSocket打造一个IM聊天室

之前我们在 IM即时聊天室(一):WebSocket 和 IM即时聊天室(二): Socket.io + Node.js 两篇文章中介绍了搭建一个IM的所需的技术栈和通信原理。那在这篇文章里我们就来详细说一下具体的应用并提供完整源码。 个人Blog地址:IM即时聊天室(三):项目详解及源码分析 聊天室在线地址:点此进入 PS: 这是我网络编程课的一个小作业,因为博主比较...

qq_36113598的博客 1万+

ABAP 百科全书的阅读目录(2024版)

909 篇 ABAP 技术文章的目录

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 4248

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

spring boot+websocket打造聊天室

spring boot + websocket 打造简单的聊天室

shepherd_dirk的博客 386

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

打造属于你的聊天室(WebSocket)

SpringBoot是为了简化Spring应用的创建、运行、调试、部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程 Webscoket对浏览器有一定的要求,所以使用之前要考虑兼容性的问题…. Webscoket WebSocket是 HTML5 新增的一种在单...

weixin_30681121的博客 84

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SpringBoot 使用WebSocket打造在线聊天室

在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。2、浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。3、当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。添加 WebSocketConfig 配置。

Mr. David 专栏 1005

JavaScript打造实时WebSocket聊天室

本文介绍了使用JavaScript和Node.js实现简易实时聊天室的方法。前端采用HTML+WebSocket实现界面和通信功能,包含消息发送、接收和显示模块;后端使用Node.js的ws库建立WebSocket服务器,处理客户端连接和消息广播。方案支持多用户实时聊天,并提供了自动滚动、连接状态监控等基础功能。文章还建议了扩展方向:改进消息格式为JSON、添加用户认证、实现消息存储和房间功能等。该实现只需少量代码即可搭建基础聊天功能,适合作为WebSocket技术的入门实践。

2509_92429804的博客 324

django-websocket打造简单的聊天室

4,卡点在于前后端websocket接口的用法协调性需要保持一致,后端django怎么单纯的应用vue.js,而不去用原生的jquery(非主流用法)CORS_ALLOW_CREDENTIALS = True # 允许携带Cookie。setting.py 静态文件配置。1,框架DJANGO,VUE。3,后端DJANGO代码。#channel配置。

Steven_yang_1的博客 1285
上一篇: IT 论坛测试报告
下一篇: 从零开启 JDBC 编程
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不摸鱼的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值