janus模块介绍-SIP Gateway

模块启动

默认的SIP GateWay也是https协议,端口为8088或者8089
如果需要在自己搭建的测试服务上测试SIP GateWay模块,则也需要修改为wss
具体改动如下: 找到/opt/janus/share/janus/demos/siptest.js

var server = "wss://" + window.location.hostname + ":8989";

在这里插入图片描述
然后重启janus服务,执行

/opt/janus/bin/janus --debug-level=5 --log-file=$HOME/janus-log

打开测试网页,在demos中找到SIP Gateway,点击start开始运行。
在这里插入图片描述
在这里插入图片描述
点击f12可以看到demo中Using WebSockets to contact Janus: wss://49.232.250.45:8989
在这里插入图片描述

模块基本说明

这是Janus的一个简单SIP插件,允许WebRTC对等方在SIP服务器(例如Asterisk)注册,并通过Janus实例调用SIP用户代理。具体地,当连接到插件时,请求对等方提供其SIP服务器证书,即SIP服务器的地址和其用户名/密码。这导致插件在SIP服务器处注册,并代表网络对等方充当SIP客户端。大多数SIP状态和生存期都被插件屏蔽,只有相关事件(例如INVITE和BYE)和功能(呼叫、挂断)可供网络对等方使用:对等方可以在SIP服务器上呼叫扩展或等待传入的INVITE,并且在呼叫期间可以发送DTMF音调。调用可以执行纯RTP或SDES-SRTP。
该插件背后的概念是允许与同一对等方相关联的不同网页,从而允许同一SIP用户同时连接到插件,但只进行一次SIP REGISTER。这同样适用于呼叫:虽然传入的呼叫将被通知给与对等方相关的所有web UI,但只有一个能够接听和接听,这与SIP分叉的工作方式非常相似,但不需要在同一个地方分叉。然而,这一特定功能尚未实现。

SIP Plugin API

我们可以在SIP插件API中发送的所有请求都是异步的,这意味着所有响应(成功和错误)都将作为同一事务的事件传递。
支持的请求包括注册、注销、调用、接受、拒绝、信息、消息、dtmf_info、订阅、取消订阅、传输、录制、保留、取消保留、更新和挂断。顾名思义,register可以用于在SIP注册器处注册用户名以调用和被调用,而unregister则注销该用户名;call用于通过插件向不同的SIP URI发送INVITE,而accept和dence用于在邀请而不是邀请的情况下接受或拒绝呼叫;接送负责有人接送和无人接送(更多详细信息,请参阅有人接送或无人接送);hold和unhold可以分别用于将呼叫挂起和恢复呼叫;info允许您发送一个通用的SIP info请求,而dtmf_info则专注于将info用于dtmf;message是用于向另一个对等方发送SIP消息的方法;subscribe和unsubscribe用于处理SIP事件,即发送subscribe请求,该请求将导致NOTIFY异步事件;相反,录音用于将对话记录到一个或多个.mjr文件中(取决于您想要录制的方向);更新允许您更新现有会话(例如,进行重新协商或强制ICE重新启动);最后,挂断可以用于在任何时候终止通信,或者挂断(BYE)正在进行的呼叫,或者取消/拒绝(cancel/BYE)尚未开始的呼叫。
无论请求如何,错误响应或事件的格式始终如下:

{
        "sip" : "event",
        "error_code" : <numeric ID, check Macros below>,
        "error" : "<error description as a string>"
}

请注意,上面的错误语法指的是插件API消息传递,而不是响应于SIP请求而获得的SIP错误代码,这些错误代码是使用不同的语法通知的:

{
        "sip" : "event",
        "result" : {
                "event" : "<name of the error event>",
                "code" : <SIP error code>,
                "reason" : "<SIP error reason>",
                "reason_header" : "<Reason header text; optional>",
                "reason_header_protocol" : "<Reason header protocol; optional>",
                "reason_header_cause" : "<Reason header cause code; optional>"
        }
}

对于可用的请求,您可以使用注册请求发送一个SIP REGISTER。更准确地说,注册请求可能会导致SIP register,因为这种方法实际上提供了无需注册即可开始使用SIP帐户的方法。例如,所谓的访客注册就是这样的情况:如果你注册为访客,这意味着你将使用From标头中提供的SIP URI进行呼叫,但实际上你不会发送SIP register;这对于向不需要注册的服务(例如,IVR系统或会议桥)发出的呼叫特别有用,但也意味着除非对等方知道您的私人SIP地址,否则您将无法接收呼叫。注册为帮助者时也不会发送SIP REGISTER:正如我们稍后将解释的,帮助者会话仅用于方便使用同一帐户设置同时SIP呼叫。
也就是说,注册请求的格式必须如下:

{
        "request" : "register",
        "type" : "<if guest or helper, no SIP REGISTER is actually sent; optional>",
        "send_register" : <true|false; if false, no SIP REGISTER is actually sent; optional>,
        "force_udp" : <true|false; if true, forces UDP for the SIP messaging; optional>,
        "force_tcp" : <true|false; if true, forces TCP for the SIP messaging; optional>,
        "sips" : <true|false; if true, configures a SIPS URI too when registering; optional>,
        "rfc2543_cancel" : <true|false; if true, configures sip client to CANCEL pending INVITEs without having received a provisional response first; optional>,
        "username" : "<SIP URI to register; mandatory>",
        "secret" : "<password to use to register; optional>",
        "ha1_secret" : "<prehashed password to use to register; optional>",
        "authuser" : "<username to use to authenticate (overrides the one in the SIP URI); optional>",
        "display_name" : "<display name to use when sending SIP REGISTER; optional>",
        "user_agent" : "<user agent to use when sending SIP REGISTER; optional>",
        "proxy" : "<server to register at; optional, as won't be needed in case the REGISTER is not goint to be sent (e.g., guests)>",
        "outbound_proxy" : "<outbound proxy to use, if any; optional>",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP REGISTER; optional>",
        "contact_params" : "<array of key/value objects, to specify custom Contact URI params to add to the SIP REGISTER; optional>",
        "incoming_header_prefixes" : "<array of strings, to specify custom (non-standard) headers to read on incoming SIP events; optional>",
        "refresh" : "<true|false; if true, only uses the SIP REGISTER as an update and not a new registration; optional>",
        "master_id" : "<ID of an already registered account, if this is an helper for multiple calls (more on that later); optional>",
        "register_ttl" : "<integer; number of seconds after which the registration should expire; optional>"
}

注册事件将被发回,因为这是一个异步请求。
如果需要,此请求将向具有正确凭据的指定服务器发起SIP REGISTER。401和407响应将被自动处理,因此错误将不会被通知回呼叫者,除非它们是决定性的(例如,错误的凭证)。注册失败将返回名称为registration_failed的错误。相反,成功注册会在注册事件中得到通知,格式如下:

{
        "sip" : "event",
        "result" : {
                "event" : "registered",
                "username" : <SIP URI username>,
                "register_sent" : <true|false, depending on whether a REGISTER was sent or not>,
                "master_id" : <unique ID of this registered session in the plugin, if a potential master>
        }
}

要注销,只需发送一个不带其他参数的注销请求:

{
        "request" : "unregister"
}

与以前一样,将发回一个注销事件。和以前一样,这也会发送一个SIP REGISTER,以防它最初被发送。在未注册的事件中通知成功注销:

{
        "sip" : "event",
        "result" : {
                "event" : "unregistered",
                "username" : <SIP URI username>,
                "register_sent" : <true|false, depending on whether a REGISTER was sent or not>
        }
}

注册后,您可以拨打电话或等待来电:请注意,如果您选择从不发送REGISTER,您将无法接听来电。
要发送SIP INVITE,您可以使用呼叫请求,其格式如下:

{
        "request" : "call",
        "call_id" : "<user-defined value of Call-ID SIP header used in all SIP requests throughout the call; optional>",
        "uri" : "<SIP URI to call; mandatory>",
        "refer_id" : <in case this is the result of a REFER, the unique identifier that addresses it; optional>,
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP INVITE; optional>",
        "srtp" : "<whether to mandate (sdes_mandatory) or offer (sdes_optional) SRTP support; optional>",
        "srtp_profile" : "<SRTP profile to negotiate, in case SRTP is offered; optional>",
        "secret" : "<password to use to call, only needed in case authentication is needed and no REGISTER was sent; optional>",
        "ha1_secret" : "<prehashed password to use to call, only needed in case authentication is needed and no REGISTER was sent; optional>",
        "authuser" : "<username to use to authenticate as to call, only needed in case authentication is needed and no REGISTER was sent; optional>",
        "autoaccept_reinvites" : <true|false, whether we should blindly accept re-INVITEs with a 200 OK instead of relaying the SDP to the application; optional, TRUE by default>
}

调用事件将被发回,因为这是一个异步请求。
请注意,此请求必须与JSEP优惠相关联:无法通过SIP插件发送无优惠INVITE。这将生成一个SIP INVITE,并根据指示进行发送。虽然100秒Trying不会通知回用户,但在振铃事件中,180秒将会响铃:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related call>",
        "result" : {
                "event" : "ringing",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

如果呼叫被拒绝或发生任何其他错误,则会发回挂断错误事件。如果呼叫被接受,则接受的事件将与被叫方发起的JSEP应答一起发送回用户:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related call>",
        "result" : {
                "event" : "accepted",
                "username" : "<SIP URI of the callee>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

在这一点上,除了PeerConnection相关的考虑因素外,可以认为该调用已建立。SIP插件会自动发送SIP ACK,因此不需要应用程序手动执行任何操作。
请注意,SIP插件通过183个响应响应支持早期媒体。在接收到183响应的情况下,在进度事件中,它将与被叫方发起的JSEP应答一起发送回用户:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related call>",
        "result" : {
                "event" : "progress",
                "username" : "<SIP URI of the callee>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

如果呼叫者收到进度事件,以下接受的事件将不包含JSEP应答,因为在“会话进度”事件中收到的应答将作为会话的SDP应答。
请注意,您只使用call来开始对话,即第一次INVITE。要通过重新邀请更新会话,例如,重新协商会话以添加/删除流或强制ICE重新启动,您不使用调用,而是使用另一个名为update的请求。这个请求不需要任何参数,因为整个上下文都是从会话的当前状态派生的。不过,作为重新谈判的一部分,它确实需要新的JSEP报价。

{
        "request" : "update"
}

更新事件将被发回,因为这是一个异步请求。
虽然呼叫请求允许您发送SIP INVITE(更新请求允许您更新现有会话),但也有一种方法可以对SIP INVITEs做出反应,即处理传入呼叫。传入呼叫通过传入呼叫事件通知应用程序:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related call>",
        "result" : {
                "event" : "incomingcall",
                "username" : "<SIP URI of the caller>",
                "displayname" : "<display name of the caller, if available; optional>",
                "callee" : "<SIP URI that was called (in case the user is associated with multiple public URIs)>",
                "referred_by" : "<SIP URI header conveying the identity of the transferor, if this is a transfer; optional>",
                "replaces" : "<call-ID of the call that this is supposed to replace, if this is an attended transfer; optional>",
                "srtp" : "<whether the caller mandates (sdes_mandatory) or offers (sdes_optional) SRTP support; optional>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

来电可能会也可能不会附带JSEP报价,这取决于来电者发送的是无报价邀请还是常规邀请。无论哪种方式,您都可以通过接受请求来接受来电:

{
        "request" : "accept",
        "srtp" : "<whether to mandate (sdes_mandatory) or offer (sdes_optional) SRTP support; optional>",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP OK; optional>"
        "autoaccept_reinvites" : <true|false, whether we should blindly accept re-INVITEs with a 200 OK instead of relaying the SDP to the browser; optional, TRUE by default>
}

由于这是一个异步请求,将发回一个接受事件。
这将导致200 OK被发送回呼叫者。接受请求必须始终附有JSEP回复(如果incomingcall事件包含要约)或要约(如果是无要约邀请)。在前一种情况下,接受的事件将被发回,只是为了确认呼叫可以被视为已建立;相反,在后一种情况下,接受事件将被发送回,而接受的事件只会在稍后,只要在呼叫者发送回的SIP ACK中有JSEP应答可用,就紧随其后。
请注意,如果您在另一个呼叫中接到来电,您将不会收到incomingcall事件,而是收到missed_call事件,这只是一个通知,因为在SIP插件中,无法在同一句柄上同时进行两个呼叫:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related call>",
        "result" : {
                "event" : "missed_call",
                "caller" : "<SIP URI of the caller>",
                "displayname" : "<display name of the caller, if available; optional>",
                "callee" : "<SIP URI that was called (in case the user is associated with multiple public URIs)>"
        }
}

此外,您只能使用accept来回答第一个INVITE。要接受重新邀请(将通过updatingcall事件通知),您不使用accept,而是使用之前引入的更新。这个请求不需要任何参数,因为整个上下文都是从会话的当前状态派生的。不过,作为重新谈判的一部分,它确实需要新的JSEP答案来提供。和以前一样,由于这是一个异步请求,因此会发回一个更新的事件。
关闭会话取决于调用状态。如果您有不想接听的来电,请使用拒绝请求;在所有其他情况下,请改用挂断请求。这两个请求都不需要额外的参数,因为整个上下文可以从插件中会话的当前状态中提取:

{
        "request" : "decline",
        "code" : <SIP code to be sent, if not set, 486 is used; optional>",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP request; optional>"
}
{
        "request" : "hangup",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP BYE; optional>"
}

由于这些都是异步请求,您将得到一个响应事件:如果使用了谢绝,则拒绝;如果使用了挂起,则挂起。
如前所述,当呼叫被拒绝或挂断时,会发送一个挂断事件,这基本上是一个SIP错误事件通知,因为它包括代码和原因。例如,常规BYE将被通知200和SIP BYE,尽管也可以提供更详细的描述。
建立会话后,可以使用不同的请求与会话进行交互。
首先,您可以使用挂起请求将呼叫挂起。默认情况下,此请求将向对等方发送一个新的INVITE,其中包含媒体的sendonly方向,但如果您想设置不同的方向(recvonly或inactive),也可以通过传递direction属性来实现:

{
        "request" : "hold",
        "direction" : "<sendonly, recvonly or inactive>"
}

持有者方不涉及WebRTC重新协商,因为这只会触发SIP方的重新INVITE。要从挂起状态移除调用,只需向插件发送一个取消挂起的请求,该请求不需要其他属性:

{
        "request" : "unhold"
}

并且将恢复在保持呼叫之前在SDP中设置的媒体方向。
消息请求允许您向对等方发送SIP message。默认情况下,在活动呼叫期间,它在对话框中发送。但是,如果用户已经注册,它也可能被发送到对话框之外。在这种情况下,uri参数是必需的。

{
        "request" : "message",
        "call_id" : "<user-defined value of Call-ID SIP header used to send the message; optional>",
        "content_type" : "<content type; optional>"
        "content" : "<text to send>",
        "uri" : "<SIP URI of the peer; optional; if set, the message will be sent out of dialog>",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP MESSAGE; optional>"
}

messagesend事件将被发回。相反,传入的SIP消息会在消息事件中得到通知:

{
        "sip" : "event",
        "result" : {
                "event" : "message",
                "sender" : "<SIP URI of the message sender>",
                "displayname" : "<display name of the sender, if available; optional>",
                "content_type" : "<content type of the message>",
                "content" : "<content of the message>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

传递后,消息传递事件将与SIP服务器响应一起发送回。用于跟踪邮件的传递状态。

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related message>",
        "result" : {
                "event" : "messagedelivery",
                "code" : "<SIP error code>",
                "reason" : "<SIP error reason>",
        }
}

SIP INFO的工作方式几乎相同,只是您使用一个对一个对等体的信息请求:

{
        "request" : "info",
        "type" : "<content type>"
        "content" : "<message to send>",
        "headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP INFO; optional>"
}

将发回一个infosend事件。相反,传入的SIP info会在信息事件中得到通知:

{
        "sip" : "event",
        "result" : {
                "event" : "info",
                "sender" : "<SIP URI of the message sender>",
                "displayname" : "<display name of the sender, if available; optional>",
                "type" : "<content type of the message>",
                "content" : "<content of the message>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

正如预期的那样,使用SUBSCRIBE和NOTIFY机制也支持SIP事件。要做到这一点,您需要使用订阅请求,该请求的格式如下:

{
        "request" : "subscribe",
        "call_id" : "<user-defined value of Call-ID SIP header used in all SIP requests throughout the subscription; optional>",
        "event" : "<the event to subscribe to, e.g., 'message-summary'; mandatory>",
        "accept" : "<what should be put in the Accept header; optional>",
        "to" : "<who should be the SUBSCRIBE addressed to; optional, will use the user's identity if missing>",
        "subscribe_ttl" : "<integer; number of seconds after which the subscription should expire; optional>",
        "headers" : "<array of key/value objects, to specify custom headers to add to the SIP SUBSCRIBE; optional>"
}

如果subscribe请求被接受,则会发回订阅事件,然后是subscribe_succeed,如果事务失败,则会返回subscribe_failed。相反,传入的SIP NOTIFY事件在通知事件中得到通知:

{
        "sip" : "event",
        "call_id" : "<value of SIP Call-ID header for related subscription>",
        "result" : {
                "event" : "notify",
                "notify" : "<name of the event that the user is subscribed to, e.g., 'message-summary'>",
                "substate" : "<substate of the subscription, e.g., 'active'>",
                "content-type" : "<content-type of the message>"
                "content" : "<content of the message>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

您还可以录制SIP调用,它的工作原理与VideoCall插件的工作原理几乎相同。具体来说,您可以使用录制请求来启动或停止录制,使用以下语法:

{
        "request" : "recording",
        "action" : "<start|stop, depending on whether you want to start or stop recording something>"
        "audio" : <true|false; whether or not our audio should be recorded>,
        "video" : <true|false; whether or not our video should be recorded>,
        "peer_audio" : <true|false; whether or not our peer's audio should be recorded>,
        "peer_video" : <true|false; whether or not our peer's video should be recorded>,
        "send_peer_pli" : <true|false; whether or not send PLI to request keyframe from peer>,
        "filename" : "<base path/filename to use for all the recordings>"
}

正如您所看到的,这意味着对话的双方是分开录制的,音频和视频流也是如此(如果可用)。您可以选择要记录的内容,以防您只对子集感兴趣。文件名部分只是一个前缀,指示将用于最多四个可能需要启用的录制的实际文件名。
如果请求成功,则会发回已记录的更新事件。

Simultaneous SIP calls using the same account

如前几节所述,使用Janus句柄附加到SIP插件意味着代表用户或应用程序创建SIP堆栈:这通常意味着注册帐户,并能够启动或接收调用、处理订阅等。这也意味着,由于在Janus中,每个核心句柄只能与单个PeerConnection相关联,因此每个SIP帐户每次仅限于一个调用:如果用户已经在SIP会话中,并且另一个调用传入,则会自动拒绝,并显示486 Busy。
虽然通常不是什么大问题,但在某些用例中,支持多个并发调用是有意义的,并且可以从一个无缝地切换到另一个。这在使用所谓的助手会话的SIP插件中是可能的。具体来说,助手会话在假设存在正常注册的主会话(即“常规”SIP插件句柄)的情况下工作,并且这些助手会话可以简单地与之关联:任何时候需要另一个并发调用,如果主会话繁忙,则可以使用其中一个助手;可用的助手会话越多,可以建立的同时调用就越多。
其工作方式很简单:
您以通常的方式创建SIP会话,并在那里发送常规注册表;这将是主会话,并在成功注册时返回master_id;
对于要添加的每个助手,您将新的Janus句柄附加到SIP插件,并发送一个类型为“helper”的寄存器,并提供与主会话相同的用户名,以及引用主会话的master_id属性;
此时,新的助手与主机相关联,这意味着它可以用于启动新的调用或接收与主会话完全相同的调用,并使用相同的帐户信息、凭据等。
请注意,一旦主服务器注销,或者它所在的Janus句柄被分离,与它相关的所有助手会话也会自动删除。具体来说,插件将强制分离相关句柄。如果您需要再次注册,并希望在那里也有一些助手,您将不得不再次添加他们。
如果您想在实践中看到这一点,SIP插件演示有一个“隐藏”函数,您可以从JavaScript控制台调用该函数来玩助手:调用addHelper()函数将添加一个新的助手,并显示其他控件。您可以根据需要添加任意数量的辅助对象。

Attended and blind transfers

Janus SIP插件支持有人参与和盲传输,并且这样做主要依赖于多个调用功能:因此,请确保您已经阅读并熟悉关于使用相同帐户进行同步SIP调用的部分。
大多数与传输相关的功能都基于上一节中已经记录的现有消息和事件,但有几个方面需要注意。首先,如果您是传输者,则需要使用名为transfer的新请求,该请求允许您向受让者发送SIPREFER,以便到达不同的目标。传输请求的格式必须如下所示:

{
        "request" : "transfer",
        "uri" : "<SIP URI to send the transferee too>",
        "replace" : "<call-ID of the call this attended transfer is supposed to replace; default is none, which means blind/unattended transfer>"
}

无论是盲传输(无替换调用)还是有人参与传输,都将发回传输事件,因为这是一个异步请求。进一步的更新将以NOTIFY相关事件的形式出现,因为REFER隐式创建订阅。
相反,REFER的接收者将接收一个名为transfer的异步事件,其中包含它需要知道的信息。事实上,SIP插件不会自动执行任何操作:传入的REFER被通知给应用程序,以便它可以决定是否跟踪传输。事件的语法如下:

{
        "sip" : "event",
        "result" : {
                "event" : "transfer",
                "refer_id" : <unique ID, internal to Janus, of this referral>,
                "refer_to" : "<SIP URI to call>",
                "referred_by" : "<SIP URI SIP URI header conveying the identity of the transferor; optional>",
                "replaces" : "<call-ID of the call this transfer is supposed to replace; optional, and only present for attended transfers>",
                "headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
        }
}

该列表中最重要的属性是refer_id,因为如果传输被接受,则该值必须包含在调用请求中以调用目标:事实上,这是SIP插件必须将新的传出调用与前一个传输请求相关联的唯一方法,从而能够通过notify事件通知传输者调用的进展情况。请注意,如果受让人决定跟踪转移请求,并且他们已经在通话中(例如,与转发器),则他们必须为此目的使用不同的句柄,例如,通过使用相同帐户部分的同步SIP呼叫中描述的助手。
传输目标将完全像前面讨论的那样接收调用,不同的是,出于信息目的,它可能包含或不包含referred_by属性。就像受让人一样,如果他们已经在调用中,则应由应用程序创建一个助手来设置一个新的Janus句柄以接受传输。
请注意,插件不会将涉及的调用置于等待状态,或自动关闭打算由传输替换的调用。所有这些都是应用程序的责任,因此开发人员应该相应地对事件做出反应。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python与数据库连接

新建表boss create table 创建表 Code import pymysqlcon pymysql.connect(hostlocalhost,\userroot,\password,\port3306,\dbbusiness) cursorcon.cursor() cursor.execute(create table if not exists boss(id int auto_increment primary key,name varchar(20)not null…

打包的意义 作用等前端概念集合 webpack基础配置等

基础网页是什么&#xff1f; 在学校最基础的三剑客 原生JS CSS H5就可以开发静态网页了 对于浏览器而言也能识别这些基础的文件和语法&#xff0c;真正的所见即所得&#xff0c;非常直接。 为什么要使用框架库&#xff1f; 对于常用的前端框架而言&#xff0c;无论是Vue Rea…

什么样的内外网文档摆渡,可以实现安全高效传输?

内外网文档摆渡通常指的是在内网&#xff08;公司或组织的内部网络&#xff09;和外网&#xff08;如互联网&#xff09;之间安全地传输文件的过程。这个过程需要特别注意安全性&#xff0c;因为内网往往包含敏感数据&#xff0c;直接连接内网和外网可能会带来安全风险。因此会…

为什么深度学习模型在 GPU 上运行得更快:CUDA 编程简介

如今,当我们谈论深度学习时,通常会将其实现与利用 GPU 来提高性能联系起来。 GPU(图形处理单元)最初设计用于加速图像、2D 和 3D 图形的渲染。然而,由于它们能够执行许多并行操作,因此它们的实用性超出了深度学习等应用程序。 GPU 在深度学习模型中的使用始于 2000 年代…

保姆级银河麒麟V10高级服务器离线安装mysql5.7数据库

离线在银河麒麟高级操作系统v10安装mysql5.7 下载mysql5.7 MySQL :: Download MySQL Community Server (Archived Versions) 2、把下载好的包上传到服务器 3、解压 [root1-0001 ~]# cd /data/mysql[root1-0001 mysql]# tar -zxvf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz…

Beego框架学习:深入指南

文章目录 Beego框架学习&#xff1a;深入指南安装与设置创建控制器自定义路由使用中间件使用模板引擎使用ORM Beego框架学习&#xff1a;深入指南 Beego是一个快速开发Go语言应用的开源框架&#xff0c;它基于MVC模式设计&#xff0c;提供了一系列的工具和库&#xff0c;使得开…

C++ 之 string类的模拟实现

这学习我有三不学 昨天不学&#xff0c;因为昨天是个过去 明天不学&#xff0c;因为明天还是个未知数 今天不学&#xff0c;因为我们要活在当下&#xff0c;我就是玩嘿嘿~ –❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–…

Docker基础学习(3.Docker架构)

⭐ 作者简介&#xff1a;码上言 ⭐ 代表教程&#xff1a;Spring Boot vue-element 开发个人博客项目实战教程 ⭐专栏内容&#xff1a;个人博客系统 ⭐我的文档网站&#xff1a;http://xyhwh-nav.cn/ ⭐微信公众号&#xff1a;码上言 文章目录 Docker基本概念1、镜像&…

JavaWeb-自学JSP组件报告

自学JSP组件报告 一、组件资源及作用 1. commons-fileupload-1.2.2.jar 组件作用&#xff1a;用于处理HTTP文件上传请求&#xff0c;提供了文件上传的解析和存储功能。它允许开发者在Web应用中轻松实现文件上传功能。 2. commons-io-2.4.jar 组件作用&#xff1a;提供了一…

springboot+vue新疆肉牛智慧牧场养殖系统

系统涉及的对象是奶牛。 系统使用员工有管理员和普通员工。 管理员有修改的权限&#xff0c;普通员工没有。 系统需要包含奶牛的编号&#xff0c;种类&#xff0c;体重&#xff0c;健康情况、生长情况、牛奶产量&#xff0c;以及上次更新数据时间等信息&#xff0c;管理员可以对…

Perfect Clear WorkBench 智能修图黑科技,你尽管拍剩下的交给我(v4.6.0.2653)

01 Perfect Clear Perfect Clear WorkBench是EyeQlmaging推出的先进图片处理工具&#xff0c;旨在自动优化和简化图像校正。它通过智能技术提高图片的清晰度、颜色保真度&#xff0c;并增强视觉效果&#xff0c;确保高品质输出。 它的核心优势是利用高级算法和AI技术&#xff…

第59篇:创建Nios II工程之控制LED<一>

Q&#xff1a;还记得第1篇吗&#xff1f;设计简单的逻辑电路&#xff0c;控制DE2-115开发板上LED的亮与熄灭&#xff0c;一行Verilog HDL的assign赋值语句即可实现。本期开始创建Nios II工程&#xff0c;用C语言代码控制DE2-115开发板上的LED实现流水灯效果。 A&#xff1a;在…

win下安装desktop及使用desktop安装k8s

1、下载desktop安装包 Docker Desktop: The #1 Containerization Tool for Developers | Docker 2、点击exe文件进行安装 3、安装完需要在启用或关闭windows功能中勾选如下三个选项 4、在desktop中配置Docker Engine { "registry-mirrors": [ "https:/…

Linux创建YUM仓库

在rhel-8.5中的/mnt/目录下是有AppStream和BaseOS这两个软件包的&#xff0c;里面有可安装的一些软件。 /mnt/BaseOS/Packages/ 普通安装 1.使用rpm命令安装&#xff08;rpm -i 程序名称&#xff09; 查看&#xff0c;已经有了这个程序&#xff08;rpm -qa | grep 程序名&…

Footprint Analytics 与 GalaChain 达成战略合作

​ Footprint Analytics 宣布与 GalaChain 达成战略合作。GalaChain 是 Gala 旗下的 Layer 1 区块链。此次合作标志着双方在游戏&#xff08;包括 Gala Games) 、娱乐和金融等多个行业的区块链生态系统革新方面迈出了重要的一步。 GalaChain 致力于满足企业级项目的广泛需求&…

【电路笔记】-Colpitts振荡器

Colpitts振荡器 文章目录 Colpitts振荡器1、概述2、基本Colpitts 振荡器电路3、示例14、使用运算放大器的Colpitts振荡器5、总结Colpitts 振荡器设计使用两个中心抽头电容器与并联电感器串联,形成产生正弦振荡的谐振储能电路。 1、概述 在许多方面,Colpitts 振荡器与我们在上…

GO语言写Prometheus自定义node-exporter的Docker容器测试

1. 安装docker-compose 执行以下命令&#xff0c;安装docker-compose到CentOS7.9环境中&#xff1a; # 下载二进制文件 sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.7/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/d…

不懂就问!现货黄金和实物黄金如何选择?

近期金价大涨&#xff0c;很多投资者就将资金从股票等其他投资品种抽调出来&#xff0c;而投入到黄金市场中。然而&#xff0c;整个黄金投资市场中拥有这么多不同的黄金投资品种&#xff0c;像现货黄金和实物黄金&#xff0c;投资者根本不知道该选哪种&#xff0c;下面我们就来…

[数据结构]——排序——插入排序

目录 ​编辑 1 .插入排序 1.基本思想&#xff1a; 2.直接插入排序&#xff1a; ​编辑 1.代码实现 2.直接插入排序的特性总结&#xff1a; 3.希尔排序( 缩小增量排序 ) 1.预排序 2.预排序代码 3.希尔排序代码 4.希尔排序的特性总结&#xff1a; 1 .插入排序 1.基本思…

2023年全国消费金融财务数据挖掘-投资回报率最高的竟是!

作者Toby&#xff0c;来源公众号Python风控模型&#xff0c;2023年全国消费金融财务统计 大家好&#xff0c;Toby老师汇总了2023年全国消费金融财务数据。这份数据可以用来分析各个消费金融公司在2023年的财务表现&#xff0c;包括资产状况、营业收入、净利润以及投资回报率等…