# 利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能 WebSocket -- 3

news2024/9/19 3:43:51

利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能 WebSocket – 3

一、Tomcat专题 - WebSocket - 案例 - OnMessage分析

1、WebSocket DEMO 案例 实现流程分析:OnMessage 分析

tomcat-72.png

2、在项目 dzs168_chat_room 中,在 websocket 类 ChatSocket.java 中,

创建 public void onMessage(String message, Session session) {…} 方法。分析流程。


    //{"fromName":"Deng","toName":"HEIMA","content":"约会呀"}
    @OnMessage
    public  void onMessage(String message, Session session) {
       
        //1. 获取客户端的信息内容, 并解析
       
        //2. 判定是否有接收人
        
        //3. 如果接收人是否是广播(all), 如果是, 则说明发送广播消息
       
	    //4. 不是all , 则给指定的用户推送消息
       
    }

二、Tomcat专题 - WebSocket - 案例 - OnMessage功能实现

1、在项目 dzs168_chat_room 中,在 websocket 类 ChatSocket.java 中,

完成 onMessage(String message, Session session) {…} 方法代码编写。


    //{"fromName":"Deng","toName":"HEIMA","content":"约会呀"}
    @OnMessage
    public  void onMessage(String message, Session session) throws IOException {
        System.out.println("onMessage : name = " + httpSession.getAttribute("username")+ ", message=" + message );
        //1. 获取客户端的信息内容, 并解析
        Map<String,String> messageMap = JSON.parseObject(message, Map.class);
        String fromName = messageMap.get("fromName");
        String toName = messageMap.get("toName");
        String content = messageMap.get("content");

        //2. 判定是否有接收人
        if(toName == null || toName.isEmpty()){
            return;
        }

        //3. 如果接收人是否是广播(all), 如果是, 则说明发送广播消息
        String messageContent = MessageUtil.getContent(MessageUtil.TYPE_MESSAGE, fromName, toName, content);
        System.out.println("服务端给客户端发送消息, 消息内容: " + messageContent);
        if("all".equals(toName)){
            //3.1 组装消息内容
            broadcastAllUsers(messageContent);
        }else{//4. 不是all , 则给指定的用户推送消息
            singlePushMessage(messageContent, fromName,toName);
        }

    }

2、在项目 dzs168_chat_room 中,在 websocket 类 ChatSocket.java 中,

创建 //给指定用户推送消息 private void singlePushMessage(String content, String fromName, String toName) throws IOException {…} 方法,并完成代码编写。


    //给指定用户推送消息
    private void singlePushMessage(String content, String fromName, String toName) throws IOException {
        boolean isOnline = false;
        //1. 判定当然接收人是否在线
        for (HttpSession hsession : onlineUsers.keySet()) {
            if(toName.equals(hsession.getAttribute("username"))){
                isOnline = true;
            }
        }

        //2. 如果存在, 发送消息
        if(isOnline){
            for (HttpSession hsession : onlineUsers.keySet()) {
                if (hsession.getAttribute("username").equals(fromName) || hsession.getAttribute("username").equals(toName)){
                    onlineUsers.get(hsession).session.getBasicRemote().sendText(content);
                }
            }
        }

    }

3、项目 dzs168_chat_room 中,websocket 类 ChatSocket.java 代码。


/**
 *   project_tomcat\dzs168_chat_room\src\djh\it\websocket\ChatSocket.java
 *
 *   2024-9-2 创建 websocket 类 ChatSocket.java
 */

package djh.it.websocket;

import djh.it.utils.MessageUtil;
import com.alibaba.fastjson.JSON;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@ServerEndpoint(value = "/websocket",configurator = GetHttpSessionConfigurator.class )
public class ChatSocket {

    private  Session session;
    private  HttpSession httpSession;

    //保存当前系统中登录的用户的HttpSession信息, 及对应的Endpoint实例信息
    private static Map<HttpSession , ChatSocket> onlineUsers = new HashMap<HttpSession, ChatSocket>();
    private static int onlineCount = 0;

    @OnOpen
    public void onOpen(Session session, EndpointConfig config){

        //1. 记录webSocket的会话信息对象Session
        this.session = session;

        //2. 获取当前登录用户HttpSession信息.
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;

        System.out.println("当前登录用户 : " + httpSession.getAttribute("username") +", Endpoint : " +hashCode());

        //3. 记录当前登录用户信息, 及对应的Endpoint实例
        if (httpSession.getAttribute("username") != null){
            onlineUsers.put(httpSession,this);
        }

        //4. 获取当前所有登录用户 --------> DZS168,dzs,TOM...
        String names = getNames();

        //5. 组装消息 ---> {"data":"dzs168,Deng,study","toName":"","fromName":"","type":"user"}
        String message = MessageUtil.getContent(MessageUtil.TYPE_USER, "", "", names);

        //6. 通过广播的形式发送消息
        //session.getBasicRemote().sendText("");
        broadcastAllUsers(message);

        //7. 记录当前用户登录数 .
        incrCount();

    }

    //{"fromName":"Deng","toName":"HEIMA","content":"约会呀"}
    @OnMessage
    public  void onMessage(String message, Session session) throws IOException {
        System.out.println("onMessage : name = " + httpSession.getAttribute("username")+ ", message=" + message );
        //1. 获取客户端的信息内容, 并解析
        Map<String,String> messageMap = JSON.parseObject(message, Map.class);
        String fromName = messageMap.get("fromName");
        String toName = messageMap.get("toName");
        String content = messageMap.get("content");

        //2. 判定是否有接收人
        if(toName == null || toName.isEmpty()){
            return;
        }

        //3. 如果接收人是否是广播(all), 如果是, 则说明发送广播消息
        String messageContent = MessageUtil.getContent(MessageUtil.TYPE_MESSAGE, fromName, toName, content);
        System.out.println("服务端给客户端发送消息, 消息内容: " + messageContent);
        if("all".equals(toName)){
            //3.1 组装消息内容
            broadcastAllUsers(messageContent);
        }else{//4. 不是all , 则给指定的用户推送消息
            singlePushMessage(messageContent, fromName,toName);
        }

    }

    //给指定用户推送消息
    private void singlePushMessage(String content, String fromName, String toName) throws IOException {
        boolean isOnline = false;
        //1. 判定当然接收人是否在线
        for (HttpSession hsession : onlineUsers.keySet()) {
            if(toName.equals(hsession.getAttribute("username"))){
                isOnline = true;
            }
        }

        //2. 如果存在, 发送消息
        if(isOnline){
            for (HttpSession hsession : onlineUsers.keySet()) {
                if (hsession.getAttribute("username").equals(fromName) || hsession.getAttribute("username").equals(toName)){
                    onlineUsers.get(hsession).session.getBasicRemote().sendText(content);
                }
            }
        }

    }


    // 发送广播消息
    private void broadcastAllUsers(String message) {

        for (HttpSession hsession : onlineUsers.keySet()) {
            try {
                onlineUsers.get(hsession).session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    //获取所有的在线用户
    private String getNames() {
        String names = "";
        if(onlineUsers.size()>0){
            for (HttpSession hsession : onlineUsers.keySet()) {
                String username = (String) hsession.getAttribute("username");
                names += username+",";
            }
        }
        return names.substring(0,names.length()-1);
    }

    public int getOnlineCount(){
        return onlineCount;
    }

    public synchronized void incrCount(){
        onlineCount ++;
    }

    public synchronized void decrCount(){
        onlineCount --;
    }

}

三、Tomcat专题 - WebSocket - 案例 - OnMessage功能测试

1、项目 dzs168_chat_room 中,前端源码 chat.jsp 页面。


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="format-detection" content="telephone=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="yes" name="apple-touch-fullscreen">
    <meta name="full-screen" content="yes">
    <meta content="default" name="apple-mobile-web-app-status-bar-style">
    <meta name="screen-orientation" content="portrait">
    <meta name="browsermode" content="application">
    <meta name="msapplication-tap-highlight" content="no">
    <meta name="x5-orientation" content="portrait">
    <meta name="x5-fullscreen" content="true">
    <meta name="x5-page-mode" content="app">
    <base target="_blank">
    <title>段子手168-聊天室</title>
    <link href="css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
    <link rel="stylesheet" href="css/chat.css">
    <script src="js/jquery-1.9.1.min.js"></script>

    <script type="text/javascript">
        <%
            String name = session.getAttribute("username")+"";
        %>
        var self = "<%= name %>";
    </script>
    <script type="text/javascript" src="js/ws.js"></script>
</head>

<body  onload="startWebSocket(self);">

<img style="width:100%;height:100%" src="img/chat_bg.jpg">

<div class="abs cover contaniner">
    <div class="abs cover pnl">
        <div class="top pnl-head" style="padding: 20px ; color: white;" id="userName"></div>
        <div class="abs cover pnl-body" id="pnlBody">
            <div class="abs cover pnl-left">
                <div class="abs cover pnl-msgs scroll" id="show">
                    <div class="pnl-list" id="hists"><!-- 历史消息 --></div>
                    <div class="pnl-list" id="msgs">
                        <!-- 消息这展示区域 -->
                    </div>
                </div>

                <div class="abs bottom pnl-text">
                    <div class="abs cover pnl-input">
                        <textarea class="scroll" id="context_text" onkeydown="sendMessage(self)" wrap="hard" placeholder="在此输入文字信息..."></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" style="background-color: rgb(32, 196, 202); color: rgb(255, 255, 255);" onclick="sendMsg(self)">
                        发送
                    </div>
                    <div class="pnl-support" id="copyright"><a href="http://www.itcast.cn">段子手168,版本所有</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" id="userlist">
                        </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">
                        </ul>
                    </div>
                </div>

            </div>
        </div>
    </div>
</div>
</body>
</html>
<!-- project_tomcat\dzs168_chat_room\web\chat.jsp -->

2、项目 dzs168_chat_room 中,前端源码 login.jsp 页面。


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="en">
<head>
    <title>段子手聊天室——登录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="keywords"
          content="Transparent Sign In Form Responsive Widget,Login form widgets, Sign up Web forms , Login signup Responsive web form,Flat Pricing table,Flat Drop downs,Registration Forms,News letter Forms,Elements"/>
    <script type="application/x-javascript">
        addEventListener("load", function () {
            setTimeout(hideURLbar, 0);
        }, false);

        function hideURLbar() {
            window.scrollTo(0, 1);
        }
    </script>

    <script src="js/jquery-1.9.1.min.js"></script>
    <link rel="icon" href="img/chat.ico" type="image/x-icon"/>
    <link rel="stylesheet" href="css/font-awesome.css"/> <!-- Font-Awesome-Icons-CSS -->
    <link rel="stylesheet" href="css/login.css" type="text/css" media="all"/> <!-- Style-CSS -->
</head>

<body class="background">
<div class="header-w3l">
    <h1>段子手168聊天室</h1>
</div>
<div class="main-content-agile">
    <div class="sub-main-w3">
        <h2>登录</h2>
        <form>

            <div class="icon1">
                <input placeholder="用户名" id="username" type="text"/>
            </div>

            <div class="icon2">
                <input placeholder="密码" id="password" type="password"/>
            </div>

            <div class="clear"></div>
            <input type="button" value="登录" onclick="login()"/>
        </form>
    </div>
</div>
<div class="footer">
    <p>段子手168 版权所有Copyright 2024-9-1  All Rights Reserved </p>
</div>
</body>
<script type="text/javascript">
    function login() {
        $.ajax({
            type: 'POST',
            url: '/login',
            dataType: 'json',
            data: {
                username: $("#username").val(),
                password: $("#password").val()
            },
            success: function (data) {
                if (data.success) {
                    window.location.href = "chat.jsp";
                } else {
                    alert(data.message);
                }
            }
        });
    }

</script>
</html>
<!-- project_tomcat\dzs168_chat_room\web\login.jsp -->

3、项目 dzs168_chat_room 中,//定义全局的webSocket对象 ws.js 源码。

…\project_tomcat\dzs168_chat_room\web\js\ws.js



//定义全局的webSocket对象
var ws = null;

function startWebSocket(self) {
    //构建WebSocket对象
    if ('WebSocket' in window) {
        ws = new WebSocket("ws://localhost:8080/websocket");
    } else if ('MozWebSocket' in window) {
        ws = new MozWebSocket("ws://localhost:8080/websocket");
    } else {
        alert("not support");
    }

    //监听消息, 有消息传递, 会触发此方法
    ws.onmessage = function (evt) {
        var _data = evt.data;
        console.log(">> : " + _data);

        var o = JSON.parse(_data);

        if (o.type == 'message') { //如果后端, 响应的是消息, 在页面展示
            setMessageInnerHTML(o, self);
        } else if (o.type == 'user') { // 如果服务端响应的是用户列表, 在界面展示用户列表
            var userArry = o.data.split(',');
            $("#userlist").empty();
            $("#userlist").append('<li class="rel-item"><input type="radio" name="toUser" value="all">广播</input></li>');
            $.each(userArry, function (n, value) {
                if (value != self && value != 'admin') {
                    $("#userlist").append('<li class="rel-item"><input type="radio"  name="toUser" value="'+value+'">'+value+'</input></li>');

                    $("#broadcastList").append('<li class="rel-item">您的好友 '+value+' 已上线</li>');
                }
            });
        }
    };

    //关闭链接时触发
    ws.onclose = function (evt) {
        $('#userName').html("用户: "+ self +"<span style='float: right;color: red'>离线</span>");
    };

    //打开时触发
    ws.onopen = function (evt) {
        $('#userName').html("用户: "+ self +"<span style='float: right;color: green'>在线</span>");
    };
}

function setMessageInnerHTML(msg, self) {

    //根据后台响应的数据, 判定是展示在左侧还是右侧.
    var str = "";

    if(msg.toName == 'all'){
        $("#broadcastList").append('<li class="rel-item"> 系统广播消息: '+msg.data+' </li>');
    }else if (msg.fromName == self) {
        str = "<div class=\"msg guest\"><div class=\"msg-right\"><div class=\"msg-host headDefault\"></div><div class=\"msg-ball\" title=\"今天 17:52:06\">" + msg.data + "</div></div></div>"
    } else if(msg.toName == self){
        str = "<div class=\"msg robot\"><div class=\"msg-left\" worker=\"" + msg.fromName + "\"><div class=\"msg-host photo\" style=\"background-image: url(../img/avatar/Member002.jpg)\"></div><div class=\"msg-ball\" title=\"今天 17:52:06\">" + msg.data + "</div></div></div>";
    }

    //获取到现有的内容, 追加新的消息内容
    var msgs = document.getElementById("msgs");
    msgs.innerHTML = msgs.innerHTML + str;


    //判定消息来源的用户 , 勾选对应的好友信息
    var a = $('input[name="toUser"]');
    for(var i=0 ; i < a.length ; i++){
        if(a[i].value == msg.fromName){
            console.log(a[i]);
            a[i].checked='checked';
        }
    }
}

// 组装消息, 发送消息
function sendMsg(self) {
    var content = $("#context_text").val();

    if(!content){
        alert('请输入消息内容');
        return ;
    }

    var message = {};
    message.fromName = self;
    message.toName = $('input:radio:checked').val();  //根据界面勾选的用户, 来决定消息发送给谁
    message.content = content; //获取输入文本框中输入的内容
    var msg = JSON.stringify(message);

    console.log(" msg: "+msg);

    ws.send(msg);//发送消息
    $("#context_text").val(''); //将输入框内容置为空
}

function sendMessage(self){
    if(event.keyCode == 13){
        sendMsg(self);
    }
}

4、运行 tomcat 服务,进行测试。

5、多个 浏览器地址栏输入:localhost:8080/ 登录几个不同用户,进行测试。

tomcat-78.png

tomcat-79.png

四、Tomcat专题 - WebSocket - 案例 - OnClose及OnError介绍

1、在项目 dzs168_chat_room 中,在 websocket 类 ChatSocket.java 中,

创建 public void onClose(Session session, CloseReason closeReason){…} 方法。


    @OnClose
    public void onClose(Session session, CloseReason closeReason){
        decrCount();
        System.out.println("客户端关闭了一个连接 , 当前在线人数 : " + getOnlineCount());
    }

2、在项目 dzs168_chat_room 中,在 websocket 类 ChatSocket.java 中,

创建 public void onError(Session session, Throwable throwable){…} 方法。


    @OnError
    public void onError(Session session, Throwable throwable){
        throwable.printStackTrace();
        System.out.println("服务异常");
    }

3、项目 dzs168_chat_room 中,websocket 类 ChatSocket.java 代码。


/**
 *   project_tomcat\dzs168_chat_room\src\djh\it\websocket\ChatSocket.java
 *
 *   2024-9-2 创建 websocket 类 ChatSocket.java
 */

package djh.it.websocket;

import djh.it.utils.MessageUtil;
import com.alibaba.fastjson.JSON;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@ServerEndpoint(value = "/websocket",configurator = GetHttpSessionConfigurator.class )
public class ChatSocket {

    private  Session session;
    private  HttpSession httpSession;

    //保存当前系统中登录的用户的HttpSession信息, 及对应的Endpoint实例信息
    private static Map<HttpSession , ChatSocket> onlineUsers = new HashMap<HttpSession, ChatSocket>();
    private static int onlineCount = 0;

    @OnOpen
    public void onOpen(Session session, EndpointConfig config){

        //1. 记录webSocket的会话信息对象Session
        this.session = session;

        //2. 获取当前登录用户HttpSession信息.
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;

        System.out.println("当前登录用户 : " + httpSession.getAttribute("username") +", Endpoint : " +hashCode());

        //3. 记录当前登录用户信息, 及对应的Endpoint实例
        if (httpSession.getAttribute("username") != null){
            onlineUsers.put(httpSession,this);
        }

        //4. 获取当前所有登录用户 --------> DZS168,dzs,TOM...
        String names = getNames();

        //5. 组装消息 ---> {"data":"dzs168,Deng,study","toName":"","fromName":"","type":"user"}
        String message = MessageUtil.getContent(MessageUtil.TYPE_USER, "", "", names);

        //6. 通过广播的形式发送消息
        //session.getBasicRemote().sendText("");
        broadcastAllUsers(message);

        //7. 记录当前用户登录数 .
        incrCount();

    }

    //{"fromName":"Deng","toName":"HEIMA","content":"约会呀"}
    @OnMessage
    public  void onMessage(String message, Session session) throws IOException {
        System.out.println("onMessage : name = " + httpSession.getAttribute("username")+ ", message=" + message );
        //1. 获取客户端的信息内容, 并解析
        Map<String,String> messageMap = JSON.parseObject(message, Map.class);
        String fromName = messageMap.get("fromName");
        String toName = messageMap.get("toName");
        String content = messageMap.get("content");

        //2. 判定是否有接收人
        if(toName == null || toName.isEmpty()){
            return;
        }

        //3. 如果接收人是否是广播(all), 如果是, 则说明发送广播消息
        String messageContent = MessageUtil.getContent(MessageUtil.TYPE_MESSAGE, fromName, toName, content);
        System.out.println("服务端给客户端发送消息, 消息内容: " + messageContent);
        if("all".equals(toName)){
            //3.1 组装消息内容
            broadcastAllUsers(messageContent);
        }else{//4. 不是all , 则给指定的用户推送消息
            singlePushMessage(messageContent, fromName,toName);
        }

    }

    //给指定用户推送消息
    private void singlePushMessage(String content, String fromName, String toName) throws IOException {
        boolean isOnline = false;
        //1. 判定当然接收人是否在线
        for (HttpSession hsession : onlineUsers.keySet()) {
            if(toName.equals(hsession.getAttribute("username"))){
                isOnline = true;
            }
        }

        //2. 如果存在, 发送消息
        if(isOnline){
            for (HttpSession hsession : onlineUsers.keySet()) {
                if (hsession.getAttribute("username").equals(fromName) || hsession.getAttribute("username").equals(toName)){
                    onlineUsers.get(hsession).session.getBasicRemote().sendText(content);
                }
            }
        }

    }


    // 发送广播消息
    private void broadcastAllUsers(String message) {

        for (HttpSession hsession : onlineUsers.keySet()) {
            try {
                onlineUsers.get(hsession).session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    //获取所有的在线用户
    private String getNames() {
        String names = "";
        if(onlineUsers.size()>0){
            for (HttpSession hsession : onlineUsers.keySet()) {
                String username = (String) hsession.getAttribute("username");
                names += username+",";
            }
        }
        return names.substring(0,names.length()-1);
    }


    @OnClose
    public void onClose(Session session, CloseReason closeReason){
        decrCount();
        System.out.println("客户端关闭了一个连接 , 当前在线人数 : " + getOnlineCount());
    }


    @OnError
    public void onError(Session session, Throwable throwable){
        throwable.printStackTrace();
        System.out.println("服务异常");
    }


    public int getOnlineCount(){
        return onlineCount;
    }

    public synchronized void incrCount(){
        onlineCount ++;
    }

    public synchronized void decrCount(){
        onlineCount --;
    }

}

4、重新运行 tomcat 服务,多个 浏览器地址栏输入:localhost:8080/ 登录几个不同用户,再退出登录,进行测试。

tomcat-80.png

tomcat-81.png

上一节关联链接请点击

# 利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能-- WebSocket – 1

# 利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能-- WebSocket – 2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2145257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件开发详解:同城O2O系统源码的架构设计与外卖跑腿APP的开发要点

随着互联网技术的发展&#xff0c;O2O&#xff08;OnlinetoOffline&#xff09;模式迅速成为了各类服务行业的核心运营模式。同城O2O系统不仅整合了线上和线下的资源&#xff0c;还可以通过智能调度和大数据分析提升用户体验和运营效率。接下来&#xff0c;我将详细探讨同城O2O…

iOS平台RTSP|RTMP直播播放器技术接入说明

技术背景 大牛直播SDK自2015年发布RTSP、RTMP直播播放模块&#xff0c;迭代从未停止&#xff0c;SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述&#xff0c;全自研内核&#xff0c;行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以iOS平台为例…

渗透测试综合靶场 DC-1 通关详解

Vulnhub是一个提供各种漏洞环境的靶场平台&#xff0c;非常适合安全爱好者和渗透测试初学者进行学习和实践。在这个平台上&#xff0c;你可以下载多种虚拟机&#xff0c;这些虚拟机预装了各种漏洞&#xff0c;让你可以在本地环境中进行渗透测试、提权、漏洞利用和代码审计等操作…

9.18日常记录

一.信号和槽机制 信号和槽:是对象之间通信的一种机制 信号classA不关心有多少槽函数与之绑定&#xff0c;它只管触发信号&#xff0c;具体要触发哪些槽函数&#xff0c;是由Qt的信号和槽机制来实现的。这样的话就充分的体现了面向对象的解耦原则了&#xff0c;因为对于classA来…

上线跨境电商商城的步骤

上线一个跨境电商商城涉及多个步骤&#xff0c;从前期准备到上线后的维护。以下是一些关键步骤&#xff1a; 1. 市场调研与规划 目标市场分析&#xff1a;研究目标市场的需求、竞争对手和消费者行为。法律法规&#xff1a;了解并遵守目标市场的法律法规&#xff0c;包括税收、…

生产环境必备:Docker 搭建 Nexus 全流程与批量上传 Jar 包实战

目录 1.创建docker-compose文件 2.域名代理无域名需求可跳过 2.1创建nginx的compose &#xff08;映射端口更具实例进行改进&#xff09; 2.2创建nginx.conf 3.访问nexus 4.创建储存库以及批量上传jar包 4.1批量上传jar包 4.2创建两个sh脚本 4.3执行脚本 4.4成功验证 …

bug的处理流程是什么?一文教你快速学会bug的处理流程

一、Bug的属性 1、Bug重现环境 这个应该是我们重现bug的一个前提&#xff0c;如果没有这个前提&#xff0c;我们可能会无法重现问题&#xff0c;或者跟本就无从下手。 2、操作系统 这个是一般软件运行的一大前提&#xff0c;基本上所有的软件都依赖于操作系统之上的&#x…

中、美、德、日制造业理念差异

合格的产品依赖稳定可靠的人机料法环&#xff0c;要求减少变量因素&#xff0c;增加稳定因素&#xff0c;避免“熵”增&#xff1b;五个因素中任何一个不可控&#xff0c;批次产品的一致性绝对差&#xff1b; 日本汽车企业&#xff0c;侧重“人”和“环”&#xff0c; 倚重是人…

点工干了三年,快要废了。。。

8年前刚进入到IT行业&#xff0c;到现在学习软件测试的人越来越多&#xff0c;所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷&#xff0c;越来越多的转行和大学生进入测试行业&#xff0c;导致软件测试已经饱和了&#xff0c;想要获得更好的待…

Android RecyclerView 缓存机制深度解析与面试题

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 引言 RecyclerView 是 Android 开发中用于展示列表和网格的强大组件。它通过高效的缓存机制&#xff0c;优化了滑动性能和内存使用。本文将深入…

nginx进阶篇(二)

文章目录 概图一、 Nginx服务器基础配置实例二、Nginx服务操作的问题三、Nginx配置成系统服务四、Nginx命令配置到系统环境五、Nginx静态资源部署5.1 Nginx静态资源概述5.2 Nginx静态资源的配置指令5.2.1. listen指令5.2.2. server_name指令配置方式匹配执行顺序 5.2.3 locatio…

Linux--守护进程与会话

进程组 概念 进程组就是一个或多个进程的集合。 一个进程组可以包含多个进程。 下面我们通过一句简单的命令行来展示&#xff1a; 为什么会有进程组&#xff1f; 批量操作&#xff1a;进程组允许将多个进程组织在一起&#xff0c;形成一个逻辑上的整体。当需要对多个进程…

js 3个事件监听器 EventListeners

起因&#xff0c; 目的: 我有2个显示器。 某视频网站&#xff0c;我想一边播放视频&#xff0c;一边搞其他。但是&#xff0c;当我把鼠标移动到浏览器外面&#xff0c;点击一下别处&#xff0c; 视频就会自动暂停. 这个叫做 事件监听&#xff01; blur, 在元素或窗口失去焦点…

I/O流(Java)

目录 1. IO概述 1.1 什么是IO 1.2 IO的分类 1.3 IO的流向说明图解 1.4 顶级父类 2. File类 2.1 概述 2.2 构造方法 2.3 常用方法 2.3.1 获取功能的方法 2.3.2 绝对路径和相对路径 2.3.3 判断功能的方法 2.3.4 创建删除功能的方法 2.3.5 目录的遍历 3. 字节流 3…

【C++ Primer Plus习题】16.7

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: #include <iostream> #include <vector> #include <…

2024CCPC网络赛

vp链接&#xff1a;Dashboard - The 2024 CCPC Online Contest - Codeforces B. 军训 II 序列 a 从小到大排列或者从大到小排列时&#xff0c;不整齐度是最小的。方案数是所有相同数字的个数的排列数的乘积。如果首尾的数字不同的话&#xff0c;还要再乘个 2。 #include <…

高级大数据开发协会

知识星球——高级大数据开发协会 协会内容: 教你参与开源项目提供新技术学习指导提供工作遇到的疑难问题技术支持参与大数据开源软件源码提升优化以互利共赢为原则&#xff0c;推动大数据技术发展探讨大数据职业发展和规划共享企业实际工作经验 感兴趣的私聊我&#xff0c;…

2022年十九届中国研究生数学建模竞赛C题——优秀论文分析

● 引言&#xff1a;因为最近要参加研究生数学建模竞赛了&#xff08;第二十一届&#xff09;&#xff0c;学习和分析一下优秀的数模论文的&#xff1a;思路、写作。 虽然我说是 “优秀论文分析”&#xff0c;但其实更多是 “搬运” 哈哈哈… ✅ NLP 研 1 选手的学习笔记 笔者…

springboot+screw反向生成数据库说明文档

前言 最近公司人员结构调整&#xff0c;被迫接受一位资深老哥哥的活&#xff0c;他也是悲催&#xff0c;太老实了&#xff0c;默默的干活老黄牛&#xff0c;不会叫。又没有山头&#xff0c;直接领导组长也是不给力。哎&#xff0c;哪里都有江湖&#xff0c;愿我码农儿女都能被善…

Android Studio Menu制作

文章目录 一、创建菜单在Activity上新建onCreateOptionsMenu新建menu目录及资源文件新建Menu一级菜单在Activity上加载Menu测试效果 二、菜单点击事件 一、创建菜单 在Activity上新建onCreateOptionsMenu Overridepublic boolean onCreateOptionsMenu(Menu menu) {return supe…