AndroidTV端:酒店扫码认证投屏DLNA

news2024/12/24 9:43:24

被老板叼了几次了,最近实在忍不了,准备离职;

但是担心离职后长时间没有办法找到工作

就想贡献一套平时琢磨出来的程序,请各位有能力的话带我熬过这凛冽的寒冬。

目前写出来的,有三个端:安卓TV端,安卓手机端,Windows-Qt端

这篇是TV端的部分代码,当然要理解一下,我并不能粘贴出完整的代码,请见谅;

想要获取完整源码可以直接私信我,万分感激

那下面就开始粘贴部分源码:

1.SSDP的发送

DLNA是通过监听网络当中的广播进行解析的,也就是在循环发送几个消息

这些消息,有的是关于设备信息的,有些是关于控制命令的,总之得根据自己的来


    public String toString(int i) {

        //都是些固定格式,NT和USN是两辆相关
        StringBuilder content = new StringBuilder();
        content.append("NOTIFY * HTTP/1.1").append("\r\n");
        content.append("SERVER: Linux/3.14.29 UPnP/1.0 Cling/2.0").append("\r\n");
        content.append("CACHE-CONTROL: max-age=1800").append("\r\n");//指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在
        content.append("LOCATION: "+_LOCATION).append("\r\n");
        //乐播投屏加了下面三行,加了就搜索不到了
//        content.append("OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01; ds=0").append("\r\n");
//        content.append("01-NLS: 0dcebdf8-1dd2-11b2-8b72-f07170d94291").append("\r\n");
//        content.append("X-User-Agent: redsonic").append("\r\n");
        content.append("NTS: ssdp:alive").append("\r\n");
        if(i==0){
            content.append("NT: upnp:rootdevice").append("\r\n");
            content.append("USN: uuid:"+ MainActivity._UUID+"::upnp:rootdevice").append("\r\n");
        }else if(i==1){
            content.append("NT: uuid:"+MainActivity._UUID).append("\r\n");
            content.append("USN: uuid:"+MainActivity._UUID).append("\r\n");
        } else if(i==2){
            content.append("NT: urn:schemas-upnp-org:device:MediaRenderer:1").append("\r\n");
            content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:device:MediaRenderer:1").append("\r\n");
        }else if(i==3){
            content.append("NT: urn:schemas-upnp-org:service:RenderingControl:1").append("\r\n");
            content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:RenderingControl:1").append("\r\n");
        }else if(i==4){
            content.append("NT: urn:schemas-upnp-org:service:ConnectionManager:1").append("\r\n");
            content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:ConnectionManager:1").append("\r\n");
        }else if(i==5){
            content.append("NT: urn:schemas-upnp-org:service:AVTransport:1").append("\r\n");
            content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:AVTransport:1").append("\r\n");
        }
        content.append("HOST: 239.255.255.250:1900").append("\r\n");
        content.append("\r\n");
        //Log.e("SSDPSendMsg",MainActivity._UUID+"\r\n\r\n");
        return content.toString();

        //https://blog.csdn.net/thebestleo/article/details/50800781
    }

安卓端,发送成功后,其实并不能在各大平台的设备上正常显示

这时候就需要在安卓端搭建一个web服务,web服务里面指向一个描述的xml,有关这个投屏设备的一些可以使用的信息,比如酒店某个“房间号”,“品牌+房间号”都可以动态设置,比如我就设置的MyDLNA

2.搭建一个web服务

//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-server:8.1.21.v20160908'
//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-servlet:8.1.21.v20160908'
//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-client:8.1.21.v20160908'
api 'org.nanohttpd:nanohttpd:2.3.1'

我这边就不粘贴很多

粘贴一下认证部分的内容,认证说起来也简单,就是电视端给一个链接让手机端可以访问,电视端把这个链接转换成二维码;

手机要先连接和电视机端同样的网络下面,手机扫码后直接就请求电视机了

扫码后请求电视机会拿到请求的头部,也就请求来的手机ip地址,把这个ip缓存一下,做一下判断,只给这个手机ip发送广播,这样就可以实现扫码后单一的投屏,就不会在同一个酒店下面显示所有房间的电视机了

public class ControlHandler extends org.eclipse.jetty.server.handler.DefaultHandler {

    private Activity myactivity;
    private String PlayerURL = "";
    private String DesiredVolume = "";
    private String PlayTarget = "";
    private String PlayState = "NO_MEDIA_PRESENT";
    private String PlayerURLTemp = "xxx";

    public ControlHandler(Activity activity) {
        super();
        this.myactivity = activity;
    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        if (response.isCommitted() || baseRequest.isHandled()) {
            return;
        }

        Log.e("RequestShare", request.getRequestURI());
        //认证过就返回
        if (request.getRequestURI().contains("/certification")) {
            Constants.IPDEVICE = request.getRemoteAddr();
            Log.e("RequestShare", "正在认证设备");
            baseRequest.setHandled(true);
            ServletMsg.certificationMsg(response);
        }

        //这个是读取认证的设备的(可以使得投屏设备具有唯一性)
        if (request.getRequestURI().contains("/upnp/dev/" + MainActivity._UUID + "/desc")) {//验证可以通过对比IP实现指定设备投屏
            if (Constants.ALLDEVICES) {
                Log.e("RequestShare", "所有设备都可以投屏");
                baseRequest.setHandled(true);
                ServletMsg.defaultMsg(response);
            } else {
                if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏
                    Log.e("RequestShare", "指定:" + request.getRemoteAddr() + "设备符合投屏条件");
                    baseRequest.setHandled(true);
                    response.addHeader("Content-type: ", "text/xml");
                    response.addHeader("SERVER: ", "Linux/3.14.29 UPnP/1.0 Cling/2.0");
                    response.addHeader("Cache-control: ", "max-age=1800");
//                    response.addHeader("X-User-Agent: ","redsonic");

                    ServletMsg.defaultMsg(response);
                }
            }
        }

        //请求分析
        if (Constants.ALLDEVICES) {
            getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作
            getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据,response还是加了一下,避免以后自己做控制端
            //爱奇艺和腾讯会请求里面链接,单纯回复可能不管用,加的一项
            ServletRequest(request, baseRequest, response);
        } else {
            if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏
                Log.e("RequestShare1", request.getRemoteAddr());//IP地址
                Log.e("RequestShare2", request.getRequestURI());//请求的路径
                getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作
                getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据,response还是加了一下,避免以后自己做控制端
                //爱奇艺和腾讯会请求里面链接,单纯回复可能不管用,加的一项
                ServletRequest(request, baseRequest, response);
            }
        }
    }

3.投射视频

讲到投射视频,就很简单了,可以理解为手机向电视机发送了一个请求,只不过这个请求头请求体都需要根据特定格式来

例如:

<?xml version="1.0" encoding="UTF-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><CurrentURI>http://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?e=ig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_&amp;ua=tvproj&amp;uipk=5&amp;nbs=1&amp;deadline=1693797974&amp;gen=playurlv2&amp;os=bcache&amp;oi=3728755326&amp;trid=00002c92df64c42a43fa9d53a3d4ca9263eaT&amp;mid=434537794&amp;upsig=a446d61c8f79924e141b2973d94a0715&amp;uparams=e%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmid&amp;cdnid=22222&amp;bvc=vod&amp;nettype=0&amp;bw=251536&amp;orderid=0%2C3&amp;buvid=Y947C4998063ABA44C5595C15C70401D62DF&amp;build=70100100&amp;mobi_app=iphone&amp;f=T_0_0&amp;logo=80000000&amp;_nva_ext_=</CurrentURI><CurrentURIMetaData>&lt;DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"&gt;&lt;item id="0" parentID="-1" restricted="1"&gt;&lt;dc:title&gt;我花1万块重庆打车到上海,却被全国网友说成老赖?&lt;/dc:title&gt;&lt;upnp:storageMedium&gt;UNKNOWN&lt;/upnp:storageMedium&gt;&lt;upnp:writeStatus&gt;UNKNOWN&lt;/upnp:writeStatus&gt;&lt;upnp:longDescription&gt;8ZPKtX3aKq2ata7O0mJBw8X-jfpm6X7rvBzgi6zEJLdfXlUYjO_zomV6xIkLrNS8Y97j6EBY4i-BmrgA2WWIqDn9Gd6p83nZCVDTdbhDOos4x_QGAByL3FXnYPdYvFYaHluTvVQ5MKCckyKCQIKKxdYatoPoEqsgeVHUPN4WtaAChNi5UaHQfwtwRNaZO_SSa9jnMEYFDhN4kpzC4A0TmmqKUbUQvdIR9fh7acHLpKz3Rt7XmnEu-o9jfV7km3nwBpPT8il6Ycxp5Gw0k-kEa192ccOzzA0V11Bg7EntZzm7aJKn2QVwqg5t8hFPxpdS32W6yzM1W-7usjfm2SOESC4guqmfBlaOxdVL8hnG3mdl_BGU3kmcVhKz8u0u79UsDTWEElD1088XFetEb--WevzgLqcQF53VlmwPzCF1hoIXID5L4UaTmLu6cSzi3dKW&lt;/upnp:longDescription&gt;&lt;res protocolInfo="http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_SP_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"&gt;http://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?e=ig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_&amp;amp;ua=tvproj&amp;amp;uipk=5&amp;amp;nbs=1&amp;amp;deadline=1693797974&amp;amp;gen=playurlv2&amp;amp;os=bcache&amp;amp;oi=3728755326&amp;amp;trid=00002c92df64c42a43fa9d53a3d4ca9263eaT&amp;amp;mid=434537794&amp;amp;upsig=a446d61c8f79924e141b2973d94a0715&amp;amp;uparams=e%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmid&amp;amp;cdnid=22222&amp;amp;bvc=vod&amp;amp;nettype=0&amp;amp;bw=251536&amp;amp;orderid=0%2C3&amp;amp;buvid=Y947C4998063ABA44C5595C15C70401D62DF&amp;amp;build=70100100&amp;amp;mobi_app=iphone&amp;amp;f=T_0_0&amp;amp;logo=80000000&amp;amp;_nva_ext_=&lt;/res&gt;&lt;upnp:class&gt;object.item.videoItem&lt;/upnp:class&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;</CurrentURIMetaData></u:SetAVTransportURI></s:Body></s:Envelope>

 这就是发过来的视频连接,适当解析后就可以正常播放了

4.监听播放与控制进度

手机端会不断给电视端发送实时数据,比如音量啊,进度啊,这些;手机监听到系统音量后,会给DLNA也发一条关于音量的数据,电视机拿到就处理一下即可

大部分视频厂家都是可以正常上报,优酷和芒果倒是加了些机制,需要电视机端给设备发消息,说我已经开始播放了,然后优酷才会继续播,不然播了两秒,优酷没有收到电视机的消息,会立刻把链接关掉云云..

 //获取body信息
    private void getBody(HttpServletRequest request, Request baseRequest, HttpServletResponse response) throws IOException {
        Log.e("getBody", "------getBody分割线start------");
        BufferedReader br = request.getReader();
        String str, wholeStr = "";
        while ((str = br.readLine()) != null) {
            wholeStr += str;
        }
        Log.e("getBody", wholeStr);

        //拿到播放的链接
        if (wholeStr.contains("u:SetAVTransportURI")) {
            Log.e("55m5m53", wholeStr);
            String temp = wholeStr.substring(0, wholeStr.indexOf("</CurrentURI>"));
            PlayerURL = temp.substring(temp.indexOf("<CurrentURI>"))
                    .replace("<CurrentURI>", "")
                    .replace("&amp;", "&")
                    .replace("&quot;", "\"")
                    .replace("&lt;", "<")
                    .replace("&gt;", "<")
                    .replace("&nbsp;", " ")
                    .replace("&apos;", "'");
            Log.e("55m5m54", PlayerURL);
            PlayState = "PLAYING";
            //Response响应一下
            baseRequest.setHandled(true);
            ServletMsg.SetAVTransportURIResponseMsg(response);
            //发送并打开页面给播放器
//            Intent intent = new Intent(myactivity, PlayerActivity.class);
//            intent.putExtra("PlayerURL", PlayerURL);
//            myactivity.startActivity(intent);
            //芒果TV一次性发了5个
//            if (!PlayerURL.equals(PlayerURLTemp)) {
//                PlayerURLTemp = PlayerURL;
                countDownTimer.start();
//                intent.putExtra("PlayerURL", PlayerURL);
//                myactivity.startActivity(intent);
//            }
        } else if (wholeStr.contains("u:SetVolume")) {
            //摘取出要我设置的音量
            String Volume = wholeStr.substring(0, wholeStr.indexOf("</DesiredVolume>"));
            DesiredVolume = Volume.substring(Volume.indexOf("<DesiredVolume>")).replace("<DesiredVolume>", "");
            Log.e("SetVolume", DesiredVolume);
            //Response响应一下
            baseRequest.setHandled(true);
            ServletMsg.SetVolumeResponseMsg(response);
            //到这边的意思就是和播放器说要控制一下设备的音频
            SendBroadcast("Volume#" + DesiredVolume);
        } else if (wholeStr.contains("u:Seek")) {
            //摘取出要我设置的进度
            String Target = wholeStr.substring(0, wholeStr.indexOf("</Target>"));
            PlayTarget = Target.substring(Target.indexOf("<Target>")).replace("<Target>", "");
            Log.e("PlayTarget", PlayTarget);
            SendBroadcast("Seek#" + PlayTarget);//告诉播放器,我要设置进度了
        } else if (wholeStr.contains("u:GetTransportInfo")) {
            //告诉控制端,我还活着
            Log.e("u:GetTransportInfo", "电视系统的State:" + PlayState);
            baseRequest.setHandled(true);
            ServletMsg.GetTransportInfoMsg(response, PlayState, "OK");
        } else if (wholeStr.contains("u:GetVolume")) {
            //腾讯bilibili会要求获取一下音量,就给呗
            //bilibili给了后,会一直索要~好奇怪
            Log.e("u:GetVolume", "电视系统的Volume:" + PlayerActivity.playerData.get_volume());
            baseRequest.setHandled(true);
            ServletMsg.GetVolumeMsg(response, PlayerActivity.playerData.get_volume());
        } else if (wholeStr.contains("u:GetPositionInfo")) {
            //告诉控制端我当前的播放状态
            GetPositionInfo(response, baseRequest);
        } else if (wholeStr.contains("u:GetMediaInfo")) {
            //爱奇艺单独列了这个~,尝试发了,但没反应,找了几个数据,应该是正确的
            Log.e("getBody", "我拿到GetMediaInfo了");
            GetMediaInfo(response, baseRequest);
        } else if (wholeStr.contains("u:Stop")) {
            //有些东西需要响应的,就响应一下
            Log.e("getBody", "我拿到Stop了");
            baseRequest.setHandled(true);
            ServletMsg.StopResponseMsg(response);
        }
        Log.e("getBody", "------getBody分割线end------");
    }


    private CountDownTimer countDownTimer = new CountDownTimer(1000, 1000) {
        @Override
        public void onTick(long millisUntilFinished) {
        }

        @Override
        public void onFinish() {
            //PlayerURLTemp="Fowlet";

            if(isTopActivity(Constants.PLAYACTIVITY)){
                SendBroadcast("State#Stop");
            }

            Intent intent = new Intent(myactivity, PlayerActivity.class);
            //Intent intent = new Intent(myactivity, Player2Activity.class);
            intent.putExtra("PlayerURL", PlayerURL);
            myactivity.startActivity(intent);
        }
    };




    //获取的头部信息
    private void getHeader(HttpServletRequest request, Request baseRequest, HttpServletResponse response) {
        Log.e("getHeader", "------getHeader分割线start------");
        Enumeration<String> em = request.getHeaderNames();
        while (em.hasMoreElements()) {
            String name = (String) em.nextElement();
            String value = request.getHeader(name);
            //System.out.println(name + "=" + value);
            Log.e("getHeader", name + "=" + value);
            if (name.equals("SOAPAction") | name.equals("SOAPACTION")) {
                String[] split = value.split("#");
                String result = split[1];
                //获取头部的一些快捷操作,如果说有一些要参数要回调的话,还是要走body
                String Transport = result.substring(0, result.indexOf("\""));
                Log.e("yongge", Transport);
                if (Transport.equals("Play")) {
                    //播放
                    PlayState = "PLAYING";
                    Log.e("Header__", "我拿到Play了:" + PlayState);
                    //开始播放了就让播放器开始收集信息
                    //SendBroadcast("GetInfo#"+Transport);
                    //暂停播放也会用的到
                    SendBroadcast("State#" + Transport);
                } else if (Transport.equals("Stop")) {
                    //停止
                    PlayState = "STOPPED";
                    Log.e("Header", "我拿到Stop了:" + PlayState);
                    SendBroadcast("State#" + Transport);
                } else if (Transport.equals("Pause")) {
                    //暂停
                    PlayState = "PAUSED_PLAYBACK";
                    Log.e("Header__", "我拿到Pause了:" + PlayState);
                    SendBroadcast("State#" + Transport);
                } else if (Transport.equals("SetVolume")) {
                    //设置音量
                    Log.e("Header", "我拿到SetVolume了" + DesiredVolume);
                } else if (Transport.equals("GetPositionInfo")) {
                    SendBroadcast("GetInfo#" + Transport);//告诉播放器,你要开始收集播放器信息了
                    //获取进度
                    Log.e("Header", "我拿到GetPositionInfo了");
                } else if (Transport.equals("GetTransportInfo")) {
                    //获取传输状态
                    //安卓端的腾讯是拿的GetTransportInfo
                    Log.e("Header", "我拿到GetTransportInfo了");
                } else if (Transport.equals("SetMute")) {
                    //静音/取消
                    Log.e("Header", "我拿到SetMute了");
                } else if (Transport.equals("Seek")) {
                    //设置进度
                    Log.e("Header", "我拿到Seek了" + PlayerActivity.playerData.get_seek());
                } else if (Transport.equals("GetMediaInfo")) {
                    //爱奇艺单独列了这个~
                    Log.e("Header", "我拿到GetMediaInfo了");
                } else if (Transport.equals("GetVolume")) {
                    Log.e("Header", "我拿到GetVolume了");
                }
            }
            //优酷是要走订阅
            if (name.equals("CALLBACK")) {
                String callbackUrl = value.replace("<", "").replace(">", "");
                Log.e("callbackUrl", callbackUrl);
                String str[] = callbackUrl.replace("/","").split(":");
                try {
                    Socket socket = new Socket(str[1], Integer.parseInt(str[2]));
                    Log.e("callbackUrl2", str[1]+"---"+str[2]);
                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    bw.write("HTTP/1.1 200 OK\r\n");
                    bw.write("Server: Linux/3.14.29 UPnP/1.0 Cling/2.0\r\n");
                    bw.write("SID: uuid:"+MainActivity._UUID+"\r\n");
                    bw.write("Content-Type: text/html; charset=\"utf-8\""+"\r\n");
                    bw.write("TIMEOUT: Second-3600"+"\r\n\r\n");
                    bw.flush();

                    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    while(true) {
                        String readLine = null;
                        if ((readLine = br.readLine()) != null) {
                            Log.e("socket", readLine);
                        } else {
                            break;
                        }
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
//                YoukuMsg youkuMsg = new YoukuMsg();
//                //要返回一下订阅成功
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, youkuMsg.toString());

//                SubResponse subRespons = new SubResponse();
//                subRespons.getTest(callbackUrl,MainActivity._UUID);

//                onUnicastSend("172.16.0.121", 49152);//源IP和源端口
//
//                response.setStatus(SC_OK);
//                response.addHeader("Server: ","Linux/3.14.29 UPnP/1.0 Cling/2.0");
//                response.addHeader("SID: ","uuid:"+MainActivity._UUID);
//                //response.addHeader("Content-Type: ","text/html; charset=\"utf-8\"");
//                response.addHeader("TIMEOUT: ","Second-3600");
//                response.setContentType("text/html; charset=\"utf-8\"");
//
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, TencentMsg.RenderingControl_desc);
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, "");

//                response.setHeader();


            }
        }
        Log.e("getHeader", "------getHeader分割线end:" + PlayState + "------");
    }

处理返回的进度

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

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

相关文章

Kali之BurpSuite_pro安装配置

文章目录 配置jdk环境安装BurpSuitePro设置快捷方式启动方式 BurpSuite2021专业版本地址&#xff1a; 下载链接&#xff1a;https://pan.baidu.com/s/1PjzcukRDoc_ZFjrNxI8UjA 提取码&#xff1a;nwm7 我的安装工具都在/home/kali/tools/ 解压后我放在burpsuite_pro目录下 把j…

Harbor 私人(企业)docker镜像仓库部署

目录 一、资源下载二、配置前准备三、配置harbor.yml文件四、安装五、上传镜像到Harbor docker镜像仓库 一、资源下载 在GitHub上可以找到相关的下载&#xff1a;https://github.com/goharbor/harbor/releases 或者Linux直接wget&#xff1a; ┌──(root㉿kali)-[~] └─# w…

飞行动力学 - 第17节-航向静稳定性 之 基础点摘要

飞行动力学 - 第17节-航向静稳定性 之 基础点摘要 1. 航向静稳定性2. 航向静稳定性的组成3. 参考资料 1. 航向静稳定性 飞机受到侧滑扰动后&#xff0c;如果能产生使机头指向原来方向的力矩&#xff0c;抵消侧滑&#xff0c;则称为航向静稳定性。 2. 航向静稳定性的组成 飞机…

vue3+ts+vite项目引入echarts

概述 技术栈&#xff1a;Vue3 Ts Vite Echarts 简介&#xff1a; 图文详解&#xff0c;教你如何在Vue3项目中引入Echarts&#xff0c;封装Echarts组件&#xff0c;并实现常用Echarts图例 文章目录 概述一、先看效果1.1 静态效果1.2 动态效果 二、话不多数&#xff0c;引入 …

三步搭建个人网站并发布上线【内网穿透】

三步搭建个人网站并发布上线【内网穿透】 文章目录 三步搭建个人网站并发布上线【内网穿透】前言一、在本地电脑上制作一个网站二、使用WordPress建立网站三、通过cpolar建立的数据隧道发布到公网上 前言 在这个个性飞扬的时代&#xff0c;每个人都希望拥有表现自我的平台&…

C++(QT)画图行车

通过鼠标在窗口上点击形成多个点的连线&#xff0c;绘制一辆汽车沿着绘制的连线轨迹前进。要求连线点数大于20.可以通过清除按钮清除已经绘制的连线&#xff0c;并可以重新绘制一条轨迹连线。当车辆行驶到轨迹终点时&#xff0c;自动停止。&#xff08;汽车实在可用方块代替&am…

[羊城杯 2023] web

文章目录 D0nt pl4y g4m3!!! D0n’t pl4y g4m3!!! 打开题目&#xff0c;可以判断这里为php Development Server 启动的服务 查询得知&#xff0c;存在 PHP<7.4.21 Development Server源码泄露漏洞(参考文章) 抓包&#xff0c;构造payload 得到源码 class Pro{private $ex…

Linux创建新文件的几种方式

第一种是 vi 文件名&#xff0c;然后进入vi编辑&#xff0c;完了之后保存退出&#xff1b;然后ls看一下&#xff0c;文件有了&#xff1b; 在终端输入 cat > 文件名&#xff0c;这没用过&#xff1b;输入以后回车&#xff0c;不会退出命令&#xff1b;输入一行文字&#xff…

基于Jetson Nano的并行图像滤波算法优化与部署

基于 Jetson Nano 的并行图像滤波算法优化 我们实现了基于 GPU (CUDA) 和 CPU (PThread) 的卷积算法&#xff0c;在此基础之上&#xff0c;我们进一步实现 Sobel、Laplacian、均值和高斯滤波器&#xff0c;它们可以被用于图像处理&#xff0c;一个典型的应用场景就是图像降噪。…

nc细节点

nc细节点 1.每个新home都要替换这个xml文件 2.新建的自定义档案所在的路径 3.在已经发布好的单据上新加一个表体时生成Java源代码的注意事项 可以重新生成Java源代码到其他路径位置上&#xff0c;然后再去生成的路径上把新加的表体VO拿到工作空间里面就可以了 4.rest.proper…

什么是手术麻醉系统?

一、手术麻醉系统的主要作用 手术麻醉系统能更好、更准确地记录和管理手术与麻醉的临床信息&#xff0c;提高医生和麻醉师的工作效率。它主要用于病人手术与麻醉的申请、审批、安排&#xff0c;精确记录病人在手术过程中的术中医嘱、术中费用等信息&#xff0c;追踪生命体征等…

新唐nuc980笔记3-led指示灯测试

记录测试新唐nuc980的指示灯功能。 1、新唐nuc980开发板的指示灯电路如下&#xff1a; 使用的是PB8。 2、新建led工程&#xff0c;测试工程代码如下&#xff1a; #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h>//GP…

01. Kubernetes基础入门

目录 1、前言 2、Kubernetes介绍 2.1、什么是Kubernetes 2.2、主要功能 2.3、与Docker的关系 2.4、Kubernetes集群架构体系 3、Kubernetes组件 3.1、核心组件 3.2、附加组件 4、Kubernetes对象 4.1、对象管理 4.2、命名空间 4.3、标签 1、前言 Docker 容器技术将…

静态路由:配置和使用详解

文章目录 一、静态路由的配置和使用详解1. 配置要点1.1 点到点接口配置1.2 以太网接口配置 2. 默认路由3. 静态路由的配置命令4. 静态路由实现路由备份和负载分担 二、静态路由的优先级和比较1. 静态路由的优先级设置2. 静态路由与动态路由的比较2.1 静态路由优缺点2.2 动态路由…

《阿里巴巴 Java 开发手册》(一)命名风格

《阿里巴巴 Java 开发手册》 一、编程规约(一)命名风格 一、编程规约 (一)命名风格

【U盘】实现U盘清空并重置恢复存储

打开电脑&#xff0c;将U盘插入USB端口&#xff0c;点按快捷键【WinR】&#xff0c;弹出运行对话框&#xff0c;输入命令 diskpart 进入命令提示符窗口 输入指令 list disk 查看现在的硬盘 这里显示的U盘编号是“1”&#xff0c;因此输入select disk “1”&#xff0c;就是选择…

leetcode56. 合并区间(java)

合并区间 题目描述贪心算法代码演示 题目描述 难度 - 中等 leetcode56. 合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好…

微服务[Nacos]

CAP 1&#xff09;一致性(Consistency) (所有节点在同一时间具有相同的数据) 2&#xff09;可用性(Availability)(保证每个请求不管成功或者失败都有响应) 3&#xff09;分区容错(Partition tolerance)(系统中任意信息的丢失或失败不会影响系统的继续运作) 一、虚拟机镜像准备 …

知识储备--基础算法篇-滑动窗口

1.滑动窗口 1.1第3题-无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 心得&#…

2023年海运行业研究报告

第一章 行业概况 1.1 定义 海运行业&#xff0c;按照全球行业分类标准&#xff08;GICS&#xff09;的定义&#xff0c;是交通运输行业的一个重要子集。这个行业包括那些提供以海洋为主要运输途径的货物和乘客运输服务的公司。这些公司可以运营各种类型的船只&#xff0c;包括…