Chromium 中HTML5 WebSocket实现分析c++(一)

一、WebSocket前端接口定义:

WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。

使用 WebSocket() 构造函数来构造一个 WebSocket

构造函数

WebSocket(url[, protocols])

返回一个 WebSocket 对象。

常量

ConstantValue
WebSocket.CONNECTING0
WebSocket.OPEN1
WebSocket.CLOSING2
WebSocket.CLOSED3

属性

WebSocket.binaryType

使用二进制的数据类型连接。

WebSocket.bufferedAmount 只读

未发送至服务器的字节数。

WebSocket.extensions 只读

服务器选择的扩展。

WebSocket.onclose

用于指定连接关闭后的回调函数。

WebSocket.onerror

用于指定连接失败后的回调函数。

WebSocket.onmessage

用于指定当从服务器接受到信息时的回调函数。

WebSocket.onopen

用于指定连接成功后的回调函数。

WebSocket.protocol 只读

服务器选择的下属协议。

WebSocket.readyState 只读

当前的链接状态。

WebSocket.url 只读

WebSocket 的绝对路径。

方法

WebSocket.close([code[, reason]])

关闭当前链接。

WebSocket.send(data)

对要传输的数据进行排队。

事件

使用 addEventListener() 或将一个事件监听器赋值给本接口的 oneventname 属性,来监听下面的事件。

close

当一个 WebSocket 连接被关闭时触发。 也可以通过 onclose 属性来设置。

error

当一个 WebSocket 连接因错误而关闭时触发,例如无法发送数据时。 也可以通过 onerror 属性来设置。

message

当通过 WebSocket 收到数据时触发。 也可以通过 onmessage 属性来设置。

open

当一个 WebSocket 连接成功时触发。 也可以通过 onopen 属性来设置。

更多参考:WebSocket - Web API | MDN (mozilla.org)

二、websocket c++接口实现和定义

1、websocket.idl接口定义:

third_party\blink\renderer\modules\websockets\websocket.idl


// https://html.spec.whatwg.org/C/#the-websocket-interfaceenum BinaryType { "blob", "arraybuffer" };[ActiveScriptWrappable,Exposed=(Window,Worker),ImplementedAs=DOMWebSocket
] interface WebSocket : EventTarget {[CallWith=ExecutionContext, RaisesException] constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols);readonly attribute USVString url;// ready stateconst unsigned short CONNECTING = 0;const unsigned short OPEN = 1;const unsigned short CLOSING = 2;const unsigned short CLOSED = 3;readonly attribute unsigned short readyState;readonly attribute unsigned long long bufferedAmount;// networkingattribute EventHandler onopen;attribute EventHandler onerror;attribute EventHandler onclose;readonly attribute DOMString extensions;readonly attribute DOMString protocol;[RaisesException] void close(optional [Clamp] unsigned short code, optional USVString reason);// messagingattribute EventHandler onmessage;attribute BinaryType binaryType;[RaisesException] void send(USVString data);[RaisesException] void send(Blob data);[RaisesException] void send(ArrayBuffer data);[RaisesException] void send(ArrayBufferView data);
};

 2、websocket.idl接口实现blink:

third_party\blink\renderer\modules\websockets\dom_websocket.h

third_party\blink\renderer\modules\websockets\dom_websocket.cc


namespace blink {class Blob;
class DOMArrayBuffer;
class DOMArrayBufferView;
class ExceptionState;
class ExecutionContext;
class V8UnionStringOrStringSequence;class MODULES_EXPORT DOMWebSocket: public EventTarget,public ActiveScriptWrappable<DOMWebSocket>,public ExecutionContextLifecycleStateObserver,public WebSocketChannelClient {DEFINE_WRAPPERTYPEINFO();public:// These definitions are required by V8DOMWebSocket.static constexpr auto kConnecting = WebSocketCommon::kConnecting;static constexpr auto kOpen = WebSocketCommon::kOpen;static constexpr auto kClosing = WebSocketCommon::kClosing;static constexpr auto kClosed = WebSocketCommon::kClosed;// DOMWebSocket instances must be used with a wrapper since this class's// lifetime management is designed assuming the V8 holds a ref on it while// hasPendingActivity() returns true.static DOMWebSocket* Create(ExecutionContext*,const String& url,ExceptionState&);static DOMWebSocket* Create(ExecutionContext* execution_context,const String& url,const V8UnionStringOrStringSequence* protocols,ExceptionState& exception_state);explicit DOMWebSocket(ExecutionContext*);~DOMWebSocket() override;void Connect(const String& url,const Vector<String>& protocols,ExceptionState&);void send(const String& message, ExceptionState&);void send(DOMArrayBuffer*, ExceptionState&);void send(NotShared<DOMArrayBufferView>, ExceptionState&);void send(Blob*, ExceptionState&);// To distinguish close method call with the code parameter from one// without, we have these three signatures. Use of// Optional=DefaultIsUndefined in the IDL file doesn't help for now since// it's bound to a value of 0 which is indistinguishable from the case 0// is passed as code parameter.void close(uint16_t code, const String& reason, ExceptionState&);void close(ExceptionState&);void close(uint16_t code, ExceptionState&);const KURL& url() const;WebSocketCommon::State readyState() const;uint64_t bufferedAmount() const;String protocol() const;String extensions() const;String binaryType() const;void setBinaryType(const String&);DEFINE_ATTRIBUTE_EVENT_LISTENER(open, kOpen)DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage)DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError)DEFINE_ATTRIBUTE_EVENT_LISTENER(close, kClose)// EventTarget functions.const AtomicString& InterfaceName() const override;ExecutionContext* GetExecutionContext() const override;// ExecutionContextLifecycleStateObserver functions.void ContextDestroyed() override;void ContextLifecycleStateChanged(mojom::FrameLifecycleState) override;// ScriptWrappable functions.// Prevent this instance from being collected while it's not in CLOSED// state.bool HasPendingActivity() const final;// WebSocketChannelClient functions.void DidConnect(const String& subprotocol, const String& extensions) override;void DidReceiveTextMessage(const String& message) override;void DidReceiveBinaryMessage(const Vector<base::span<const char>>& data) override;void DidError() override;void DidConsumeBufferedAmount(uint64_t) override;void DidStartClosingHandshake() override;void DidClose(ClosingHandshakeCompletionStatus,uint16_t code,const String& reason) override;void Trace(Visitor*) const override;private:// FIXME: This should inherit blink::EventQueue.class EventQueue final : public GarbageCollected<EventQueue> {public:static EventQueue* Create(EventTarget* target) {return MakeGarbageCollected<EventQueue>(target);}explicit EventQueue(EventTarget*);~EventQueue();// Dispatches the event if this queue is active.// Queues the event if this queue is suspended.// Does nothing otherwise.void Dispatch(Event* /* event */);bool IsEmpty() const;void Pause();void Unpause();void ContextDestroyed();bool IsPaused();void Trace(Visitor*) const;private:enum State {kActive,kPaused,kUnpausePosted,kStopped,};// Dispatches queued events if this queue is active.// Does nothing otherwise.void DispatchQueuedEvents();void UnpauseTask();State state_;Member<EventTarget> target_;HeapDeque<Member<Event>> events_;};enum class WebSocketSendType {kString,kArrayBuffer,kArrayBufferView,kBlob,kMaxValue = kBlob,};enum BinaryType { kBinaryTypeBlob, kBinaryTypeArrayBuffer };// This function is virtual for unittests.virtual WebSocketChannel* CreateChannel(ExecutionContext* context,WebSocketChannelClient* client) {return WebSocketChannelImpl::Create(context, client,CaptureSourceLocation(context));}// Adds a console message with JSMessageSource and ErrorMessageLevel.void LogError(const String& message);// Handle the JavaScript close method call. close() methods on this class// are just for determining if the optional code argument is supplied or// not.void CloseInternal(int, const String&, ExceptionState&);// Updates |buffered_amount_after_close_| given the amount of data passed to// send() method after the state changed to CLOSING or CLOSED.void UpdateBufferedAmountAfterClose(uint64_t);// Causes |buffered_amount_| to be updated asynchronously after returning to// the event loop. Uses |buffered_amount_update_task_pending_| to avoid// posting multiple tasks simultaneously.void PostBufferedAmountUpdateTask();// Updates |buffered_amount_| and resets// |buffered_amount_update_task_pending_|.void BufferedAmountUpdateTask();// Updates |buffered_amount_| provided the object is not currently paused.void ReflectBufferedAmountConsumption();void ReleaseChannel();// Called on web socket message activity (sending or receiving a message) that// the execution context may want to handle, such as to extend its own// lifetime.void NotifyWebSocketActivity();Member<WebSocketChannel> channel_;WebSocketCommon common_;String origin_string_;uint64_t buffered_amount_;// The consumed buffered amount that will be reflected to |buffered_amount_|// later. It will be cleared once reflected.uint64_t consumed_buffered_amount_;uint64_t buffered_amount_after_close_;BinaryType binary_type_;// The subprotocol the server selected.String subprotocol_;String extensions_;Member<EventQueue> event_queue_;bool buffered_amount_update_task_pending_;
};}  // namespace blink

 3、websocket.idl接口实现blink/v8:

out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_websocket.h

out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_websocket.cc

截取部分 websocket.send,更多参考v8_websocket.cc

void SendOperationOverload1(const v8::FunctionCallbackInfo<v8::Value>& info) {v8::Isolate* isolate = info.GetIsolate();
const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "WebSocket";
const char* const property_name = "send";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 1)) {exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
return;
}v8::Local<v8::Object> v8_receiver = info.This();
DOMWebSocket* blink_receiver = V8WebSocket::ToWrappableUnsafe(isolate, v8_receiver);
auto&& arg1_data = NativeValueTraits<IDLUSVString>::ArgumentValue(isolate, 0, info[0], exception_state);
if (UNLIKELY(exception_state.HadException())) {return;
}
blink_receiver->send(arg1_data, exception_state);
if (UNLIKELY(exception_state.HadException())) {return;
}}

三、websocket网络进程实现接口:

  1、websocket.mojom接口定义:

     services\network\public\mojom\websocket.mojom

    作用:用来与render进程window.socket前端接口通信收发数据等。

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.module network.mojom;import "url/mojom/url.mojom";
import "services/network/public/mojom/network_param.mojom";
import "services/network/public/mojom/ip_endpoint.mojom";enum WebSocketMessageType {CONTINUATION,TEXT,BINARY,LAST = BINARY
};// TODO(darin): Move to a more general location.
struct HttpHeader {string name;string value;
};// TODO(darin): Remove redundancy b/w |headers| and |headers_text|.struct WebSocketHandshakeRequest {url.mojom.Url url;array<HttpHeader> headers;string headers_text;
};struct WebSocketHandshakeResponse {url.mojom.Url url;HttpVersion http_version;int32 status_code;string status_text;IPEndPoint remote_endpoint;array<HttpHeader> headers;string headers_text;// Sub-protocol the server selected, or empty if no sub-protocol was selected.string selected_protocol;// The list of extensions negotiated for the connection.string extensions;
};// This interface is for HTTP Authentication.
interface WebSocketAuthenticationHandler {// Returns null credentials when it wants to cancel authentication, and// returns a non-null credentials when it wants to use the credentials for// authentication.OnAuthRequired(AuthChallengeInfo info,HttpResponseHeaders headers,IPEndPoint remote_endpoint) => (AuthCredentials? credentials);
};// This interface is for client-side WebSocket handshake. Used to initialize
// the WebSocket Connection.
interface WebSocketHandshakeClient {// Notify the renderer that the browser has started an opening handshake.OnOpeningHandshakeStarted(WebSocketHandshakeRequest request);// Called when the WebSocket connection has failed.// |message| may contain a human-readable explanation of the error, but may be// empty.// |net_error| contains a network error code, which will be |ERR_FAILED| for// WebSocket-level protocol errors that do not have their own error code.// |response_code| contains the HTTP status code that caused the failure, if// it was caused by an unexpected status code, or else is -1.OnFailure(string message, int32 net_error, int32 response_code);// Called when the connection is established.// |response| may contain cookie-related headers when the client has// an access to raw cookie information.// |readable| is readable datapipe to receive data from network service.// |writable| is writable datapipe used to transfer the actual content of the// message(data) to the network service. The network services later sends out// the actual message by framing each message from the meta-info given from// the renderer side with |SendMessage()|.OnConnectionEstablished(pending_remote<WebSocket> socket,pending_receiver<WebSocketClient> client_receiver,WebSocketHandshakeResponse response,handle<data_pipe_consumer> readable,handle<data_pipe_producer> writable);
};// The interface for the client side of WebSocket. Implemented by renderer
// processes to receive messages from the network service.
interface WebSocketClient {// Receive a non-control frame from the remote server.// - |fin| indicates that this frame is the last in the current message.// - |type| is the type of the message. On the first frame of a message, it//   must be set to either WebSocketMessageType.TEXT or//   WebSocketMessageType.BINARY. On subsequent frames, it must be set to//   WebSocketMessageType.CONTINUATION, and the type is the same as that of//   the first message.// - |data_length| is the length of data and actual data is read via//   |readable| on WebSocketHandshakeClient.OnConnectionEstablished.//   If |type| is WebSocketMessageType.TEXT, then the concatenation of all//   the frames in the message must be valid UTF-8.//   If |fin| is not set, |data_length| must be non-zero.OnDataFrame(bool fin,WebSocketMessageType type,uint64 data_length);// Drop the channel.//// When sent by the renderer, this will cause a Close message will be sent and// the TCP/IP connection will be closed.//// When sent by the browser, this indicates that a Close has been received,// the connection was closed, or a network or protocol error occurred.//// - |code| is one of the reason codes specified in RFC6455.// - |reason|, if non-empty, is a UTF-8 encoded string which may be useful//   for debugging but is not necessarily human-readable, as supplied by the//   server in the Close message.// - If |was_clean| is false, then the WebSocket connection was not closed//   cleanly.OnDropChannel(bool was_clean, uint16 code, string reason);// Notify the renderer that a closing handshake has been initiated by the// server, so that it can set the Javascript readyState to CLOSING.OnClosingHandshake();
};// The interface for the server side of WebSocket. Implemented by the network
// service. Used to send out data to the network service.
interface WebSocket {// Sends a message via mojo datapipe to the remote server.// - |type| is the type of the message. It must be set to either//   WebSocketMessageType.TEXT or WebSocketMessageType.BINARY.// - |data_length| is the actual length of message. The message is written to//   the datapipe named |writable| in the//   WebSocketHandshakeClient.OnConnectionEstablished message.////   If |type| is WebSocketMessageType.TEXT, then the message must be//   valid UTF-8.SendMessage(WebSocketMessageType type, uint64 data_length);// Let browser to start receiving WebSocket data frames from network stream.// TODO(yoichio): Remove this by move Connect() after checking throttle at// WebSocketChannelImpl::Connect so that OnAddChannelResponse is// actual signal to start receive data frame.StartReceiving();// Close the channel gracefully.//// When sent by the renderer, this will cause a Close message will be sent and// the TCP/IP connection will be closed.//// - |code| is one of the reason codes specified in RFC6455.// - |reason|, if non-empty, is a UTF-8 encoded string which may be useful for//   debugging but is not necessarily human-readable, as supplied by the//   server in the Close message.StartClosingHandshake(uint16 code, string reason);
};

2、websocket.mojom接口实现:

out\Debug\gen\services\network\public\mojom\websocket.mojom.cc

out\Debug\gen\services\network\public\mojom\websocket.mojom.h

2.1)、数据发送接口SendMessage:

 2.1.1)、数据发送接口发送端WebSocketProxy::SendMessage

 //message.set_method_name("SendMessage");c

void WebSocketProxy::SendMessage(WebSocketMessageType in_type, uint64_t in_data_length) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)TRACE_EVENT1("mojom", "Send network::mojom::WebSocket::SendMessage", "input_parameters",[&](perfetto::TracedValue context){auto dict = std::move(context).WriteDictionary();perfetto::WriteIntoTracedValueWithFallback(dict.AddItem("type"), in_type,"<value of type WebSocketMessageType>");perfetto::WriteIntoTracedValueWithFallback(dict.AddItem("data_length"), in_data_length,"<value of type uint64_t>");});
#endifconst bool kExpectsResponse = false;const bool kIsSync = false;const bool kAllowInterrupt = true;const bool is_urgent = false;const uint32_t kFlags =((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |((kIsSync) ? mojo::Message::kFlagIsSync : 0) |((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);mojo::Message message(internal::kWebSocket_SendMessage_Name, kFlags, 0, 0, nullptr);mojo::internal::MessageFragment<::network::mojom::internal::WebSocket_SendMessage_Params_Data> params(message);params.Allocate();mojo::internal::Serialize<::network::mojom::WebSocketMessageType>(in_type, &params->type);params->data_length = in_data_length;#if defined(ENABLE_IPC_FUZZER)message.set_interface_name(WebSocket::Name_);message.set_method_name("SendMessage");
#endif// This return value may be ignored as false implies the Connector has// encountered an error, which will be visible through other means.::mojo::internal::SendMojoMessage(*receiver_, message);
}
 2.1.2)、数据发送接口接收端实现:

      impl->SendMessage(std::move(p_type),std::move(p_data_length));

// static
bool WebSocketStubDispatch::Accept(WebSocket* impl,mojo::Message* message) {switch (message->header()->name) {case internal::kWebSocket_SendMessage_Name: {DCHECK(message->is_serialized());internal::WebSocket_SendMessage_Params_Data* params =reinterpret_cast<internal::WebSocket_SendMessage_Params_Data*>(message->mutable_payload());bool success = true;WebSocketMessageType p_type{};uint64_t p_data_length{};WebSocket_SendMessage_ParamsDataView input_data_view(params, message);if (success && !input_data_view.ReadType(&p_type))success = false;if (success)p_data_length = input_data_view.data_length();if (!success) {ReportValidationErrorForMessage(message,mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,WebSocket::Name_, 0, false);return false;}// A null |impl| means no implementation was bound.DCHECK(impl);impl->SendMessage(
std::move(p_type), 
std::move(p_data_length));return true;}.........................................
}

2.2)、数据接收接口OnDataFrame

2.2.1)、数据接收接口 发送端OnDataFrame

WebSocketClientProxy::OnDataFrame

 message.set_method_name("OnDataFrame");

void WebSocketClientProxy::OnDataFrame(bool in_fin, WebSocketMessageType in_type, uint64_t in_data_length) {
#if BUILDFLAG(MOJO_TRACE_ENABLED)TRACE_EVENT1("mojom", "Send network::mojom::WebSocketClient::OnDataFrame", "input_parameters",[&](perfetto::TracedValue context){auto dict = std::move(context).WriteDictionary();perfetto::WriteIntoTracedValueWithFallback(dict.AddItem("fin"), in_fin,"<value of type bool>");perfetto::WriteIntoTracedValueWithFallback(dict.AddItem("type"), in_type,"<value of type WebSocketMessageType>");perfetto::WriteIntoTracedValueWithFallback(dict.AddItem("data_length"), in_data_length,"<value of type uint64_t>");});
#endifconst bool kExpectsResponse = false;const bool kIsSync = false;const bool kAllowInterrupt = true;const bool is_urgent = false;const uint32_t kFlags =((kExpectsResponse) ? mojo::Message::kFlagExpectsResponse : 0) |((kIsSync) ? mojo::Message::kFlagIsSync : 0) |((kAllowInterrupt) ? 0 : mojo::Message::kFlagNoInterrupt) |((is_urgent) ? mojo::Message::kFlagIsUrgent : 0);mojo::Message message(internal::kWebSocketClient_OnDataFrame_Name, kFlags, 0, 0, nullptr);mojo::internal::MessageFragment<::network::mojom::internal::WebSocketClient_OnDataFrame_Params_Data> params(message);params.Allocate();params->fin = in_fin;mojo::internal::Serialize<::network::mojom::WebSocketMessageType>(in_type, &params->type);params->data_length = in_data_length;#if defined(ENABLE_IPC_FUZZER)message.set_interface_name(WebSocketClient::Name_);message.set_method_name("OnDataFrame");
#endif// This return value may be ignored as false implies the Connector has// encountered an error, which will be visible through other means.::mojo::internal::SendMojoMessage(*receiver_, message);
}
2.2.2)、数据接收接口 接收端OnDataFrame

      impl->OnDataFrame(std::move(p_fin), std::move(p_type), std::move(p_data_length));

// static
bool WebSocketClientStubDispatch::Accept(WebSocketClient* impl,mojo::Message* message) {switch (message->header()->name) {case internal::kWebSocketClient_OnDataFrame_Name: {DCHECK(message->is_serialized());internal::WebSocketClient_OnDataFrame_Params_Data* params =reinterpret_cast<internal::WebSocketClient_OnDataFrame_Params_Data*>(message->mutable_payload());bool success = true;bool p_fin{};WebSocketMessageType p_type{};uint64_t p_data_length{};WebSocketClient_OnDataFrame_ParamsDataView input_data_view(params, message);if (success)p_fin = input_data_view.fin();if (success && !input_data_view.ReadType(&p_type))success = false;if (success)p_data_length = input_data_view.data_length();if (!success) {ReportValidationErrorForMessage(message,mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,WebSocketClient::Name_, 0, false);return false;}// A null |impl| means no implementation was bound.DCHECK(impl);impl->OnDataFrame(
std::move(p_fin), 
std::move(p_type), 
std::move(p_data_length));return true;}。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

3、websocket.mojom接口 network实现[net进程]

services\network\websocket.h

services\network\websocket.cc

WebSocket::SendMessage和WebSocket::WebSocketEventHandler::OnDataFrame

与render进程的WebSocketProxy::SendMessage()和WebSocketClientStubDispatch::OnDataFrame相互对应。

class GURL;namespace base {
class Location;
}  // namespace basenamespace net {
class IOBuffer;
class IsolationInfo;
class SSLInfo;
class SiteForCookies;
class WebSocketChannel;
}  // namespace netnamespace network {class WebSocketFactory;// Host of net::WebSocketChannel.
class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket {public:using HasRawHeadersAccess =base::StrongAlias<class HasRawHeadersAccessTag, bool>;WebSocket(WebSocketFactory* factory,const GURL& url,const std::vector<std::string>& requested_protocols,const net::SiteForCookies& site_for_cookies,bool has_storage_access,const net::IsolationInfo& isolation_info,std::vector<mojom::HttpHeaderPtr> additional_headers,const url::Origin& origin,uint32_t options,net::NetworkTrafficAnnotationTag traffic_annotation,HasRawHeadersAccess has_raw_cookie_access,mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client,mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver>url_loader_network_observer,mojo::PendingRemote<mojom::WebSocketAuthenticationHandler> auth_handler,mojo::PendingRemote<mojom::TrustedHeaderClient> header_client,absl::optional<WebSocketThrottler::PendingConnection>pending_connection_tracker,base::TimeDelta delay,const absl::optional<base::UnguessableToken>& throttling_profile_id);WebSocket(const WebSocket&) = delete;WebSocket& operator=(const WebSocket&) = delete;~WebSocket() override;// mojom::WebSocket methods:void SendMessage(mojom::WebSocketMessageType type,uint64_t data_length) override;void StartReceiving() override;void StartClosingHandshake(uint16_t code, const std::string& reason) override;// Whether to allow sending/setting cookies during WebSocket handshakes for// |url|. This decision is based on the |options_| and |origin_| this// WebSocket was created with.bool AllowCookies(const GURL& url) const;// These methods are called by the network delegate to forward these events to// the |header_client_|.int OnBeforeStartTransaction(const net::HttpRequestHeaders& headers,net::NetworkDelegate::OnBeforeStartTransactionCallback callback);int OnHeadersReceived(net::CompletionOnceCallback callback,const net::HttpResponseHeaders* original_response_headers,scoped_refptr<net::HttpResponseHeaders>* override_response_headers,absl::optional<GURL>* preserve_fragment_on_redirect_url);// Gets the WebSocket associated with this request.static WebSocket* ForRequest(const net::URLRequest& request);static const void* const kUserDataKey;private:class WebSocketEventHandler;struct CloseInfo;// This class is used to set the WebSocket as user data on a URLRequest. This// is used instead of WebSocket directly because SetUserData requires a// std::unique_ptr. This is safe because WebSocket owns the URLRequest, so is// guaranteed to outlive it.class UnownedPointer : public base::SupportsUserData::Data {public:explicit UnownedPointer(WebSocket* pointer) : pointer_(pointer) {}UnownedPointer(const UnownedPointer&) = delete;UnownedPointer& operator=(const UnownedPointer&) = delete;WebSocket* get() const { return pointer_; }private:const raw_ptr<WebSocket> pointer_;};struct DataFrame final {DataFrame(mojom::WebSocketMessageType type,uint64_t data_length,bool do_not_fragment): type(type),data_length(data_length),do_not_fragment(do_not_fragment) {}mojom::WebSocketMessageType type;uint64_t data_length;const bool do_not_fragment;};void OnConnectionError(const base::Location& set_from);void AddChannel(const GURL& socket_url,const std::vector<std::string>& requested_protocols,const net::SiteForCookies& site_for_cookies,bool has_storage_access,const net::IsolationInfo& isolation_info,std::vector<mojom::HttpHeaderPtr> additional_headers);void OnSSLCertificateErrorResponse(std::unique_ptr<net::WebSocketEventInterface::SSLErrorCallbacks>callbacks,const net::SSLInfo& ssl_info,int net_error);void OnAuthRequiredComplete(base::OnceCallback<void(const net::AuthCredentials*)> callback,const absl::optional<net::AuthCredentials>& credential);void OnBeforeSendHeadersComplete(net::NetworkDelegate::OnBeforeStartTransactionCallback callback,int result,const absl::optional<net::HttpRequestHeaders>& headers);void OnHeadersReceivedComplete(net::CompletionOnceCallback callback,scoped_refptr<net::HttpResponseHeaders>* out_headers,absl::optional<GURL>* out_preserve_fragment_on_redirect_url,int result,const absl::optional<std::string>& headers,const absl::optional<GURL>& preserve_fragment_on_redirect_url);void Reset();enum class InterruptionReason {// Not interrupted or not resuming after interruptions (but processing a// brand new frame)kNone,// Interrupted by empty Mojo pipe or resuming afterwardskMojoPipe,// Interrupted by the interceptor or resuming afterwardskInterceptor,};// Datapipe functions to receive.void OnWritable(MojoResult result, const mojo::HandleSignalsState& state);void SendPendingDataFrames(InterruptionReason resume_reason);void SendDataFrame(base::span<const char>* data_span);// Datapipe functions to send.void OnReadable(MojoResult result, const mojo::HandleSignalsState& state);void ReadAndSendFromDataPipe(InterruptionReason resume_reason);// This helper method only called from ReadAndSendFromDataPipe.// Note that it may indirectly delete |this|.// Returns true if the frame has been sent completely.bool ReadAndSendFrameFromDataPipe(DataFrame* data_frame);void ResumeDataPipeReading();// |factory_| owns |this|.const raw_ptr<WebSocketFactory> factory_;mojo::Receiver<mojom::WebSocket> receiver_{this};mojo::Remote<mojom::URLLoaderNetworkServiceObserver>url_loader_network_observer_;mojo::Remote<mojom::WebSocketHandshakeClient> handshake_client_;mojo::Remote<mojom::WebSocketClient> client_;mojo::Remote<mojom::WebSocketAuthenticationHandler> auth_handler_;mojo::Remote<mojom::TrustedHeaderClient> header_client_;absl::optional<WebSocketThrottler::PendingConnection>pending_connection_tracker_;// The channel we use to send events to the network.std::unique_ptr<net::WebSocketChannel> channel_;// Delay used for per-renderer WebSocket throttling.const base::TimeDelta delay_;const uint32_t options_;const net::NetworkTrafficAnnotationTag traffic_annotation_;// The web origin to use for the WebSocket.const url::Origin origin_;// For 3rd-party cookie permission checking.net::SiteForCookies site_for_cookies_;bool handshake_succeeded_ = false;const HasRawHeadersAccess has_raw_headers_access_;InterruptionReason incoming_frames_interrupted_ = InterruptionReason::kNone;InterruptionReason outgoing_frames_interrupted_ = InterruptionReason::kNone;// Datapipe fields to receive.mojo::ScopedDataPipeProducerHandle writable_;mojo::SimpleWatcher writable_watcher_;base::queue<base::span<const char>> pending_data_frames_;// Datapipe fields to send.mojo::ScopedDataPipeConsumerHandle readable_;mojo::SimpleWatcher readable_watcher_;base::queue<DataFrame> pending_send_data_frames_;bool blocked_on_websocket_channel_ = false;// True if we should preserve the old behaviour where <=64KB messages were// never fragmented.// TODO(ricea): Remove the flag once we know whether we really need this or// not. See https://crbug.com/1086273.const bool reassemble_short_messages_;// Temporary buffer for storage of short messages that have been fragmented by// the data pipe. Only messages that are actually fragmented are copied into// here.scoped_refptr<net::IOBuffer> message_under_reassembly_;// Number of bytes that have been written to |message_under_reassembly_| so// far.size_t bytes_reassembled_ = 0;// Set when StartClosingHandshake() is called while// |pending_send_data_frames_| is non-empty. This can happen due to a race// condition between the readable signal on the data pipe and the channel on// which StartClosingHandshake() is called.std::unique_ptr<CloseInfo> pending_start_closing_handshake_;const absl::optional<base::UnguessableToken> throttling_profile_id_;uint32_t net_log_source_id_ = net::NetLogSource::kInvalidId;std::unique_ptr<WebSocketInterceptor> frame_interceptor_;base::WeakPtrFactory<WebSocket> weak_ptr_factory_{this};
};}  // namespace network

4、websocket.mojom接口 network实现[render进程]

third_party\blink\renderer\modules\websockets\websocket_channel_impl.h

third_party\blink\renderer\modules\websockets\websocket_channel_impl.cc

HeapMojoRemote<network::mojom::blink::WebSocket> websocket_;//WebSocketProxy对象定义:

WebSocketProxy::SendMessage()和WebSocketClientStubDispatch::OnDataFrame

是与net进程 发送和接收数据的具体实现。


namespace v8 {
class Isolate;
}  // namespace v8namespace blink {class BaseFetchContext;
enum class FileErrorCode;
class WebSocketChannelClient;
class WebSocketHandshakeThrottle;// This is an implementation of WebSocketChannel. This is created on the main
// thread for Document, or on the worker thread for WorkerGlobalScope. All
// functions must be called on the execution context's thread.
class MODULES_EXPORT WebSocketChannelImpl final: public WebSocketChannel,public network::mojom::blink::WebSocketHandshakeClient,public network::mojom::blink::WebSocketClient {USING_PRE_FINALIZER(WebSocketChannelImpl, Dispose);public:// Public for use in tests.static constexpr size_t kMaxWebSocketsPerRenderProcess = 255u;// You can specify the source file and the line number information// explicitly by passing the last parameter.// In the usual case, they are set automatically and you don't have to// pass it.static WebSocketChannelImpl* Create(ExecutionContext* context,WebSocketChannelClient* client,std::unique_ptr<SourceLocation> location);static WebSocketChannelImpl* CreateForTesting(ExecutionContext*,WebSocketChannelClient*,std::unique_ptr<SourceLocation>,std::unique_ptr<WebSocketHandshakeThrottle>);WebSocketChannelImpl(ExecutionContext*,WebSocketChannelClient*,std::unique_ptr<SourceLocation>);~WebSocketChannelImpl() override;// WebSocketChannel functions.bool Connect(const KURL&, const String& protocol) override;SendResult Send(const std::string& message,base::OnceClosure completion_callback) override;SendResult Send(const DOMArrayBuffer&,size_t byte_offset,size_t byte_length,base::OnceClosure completion_callback) override;void Send(scoped_refptr<BlobDataHandle>) override;// Start closing handshake. Use the CloseEventCodeNotSpecified for the code// argument to omit payload.void Close(int code, const String& reason) override;void Fail(const String& reason,mojom::ConsoleMessageLevel,std::unique_ptr<SourceLocation>) override;void Disconnect() override;void CancelHandshake() override;void ApplyBackpressure() override;void RemoveBackpressure() override;// network::mojom::blink::WebSocketHandshakeClient methods:void OnOpeningHandshakeStarted(network::mojom::blink::WebSocketHandshakeRequestPtr) override;void OnFailure(const WTF::String& message,int net_error,int response_code) override;void OnConnectionEstablished(mojo::PendingRemote<network::mojom::blink::WebSocket> websocket,mojo::PendingReceiver<network::mojom::blink::WebSocketClient>client_receiver,network::mojom::blink::WebSocketHandshakeResponsePtr,mojo::ScopedDataPipeConsumerHandle readable,mojo::ScopedDataPipeProducerHandle writable) override;// network::mojom::blink::WebSocketClient methods:void OnDataFrame(bool fin,network::mojom::blink::WebSocketMessageType,uint64_t data_length) override;void OnDropChannel(bool was_clean,uint16_t code,const String& reason) override;void OnClosingHandshake() override;void Trace(Visitor*) const override;private:struct DataFrame final {DataFrame(bool fin,network::mojom::blink::WebSocketMessageType type,uint32_t data_length): fin(fin), type(type), data_length(data_length) {}bool fin;network::mojom::blink::WebSocketMessageType type;uint32_t data_length;};// Used by BlobLoader and Message, so defined here so that it can be shared.class MessageDataDeleter {public:// This constructor exists to permit default construction of the MessageData// type, but the deleter cannot be called when it was used.MessageDataDeleter() : isolate_(nullptr), size_(0) {}MessageDataDeleter(v8::Isolate* isolate, size_t size): isolate_(isolate), size_(size) {}MessageDataDeleter(const MessageDataDeleter&) = default;MessageDataDeleter& operator=(const MessageDataDeleter&) = default;void operator()(char* p) const;private:v8::Isolate* isolate_;size_t size_;};using MessageData = std::unique_ptr<char[], MessageDataDeleter>;static MessageData CreateMessageData(v8::Isolate*, size_t);friend class WebSocketChannelImplHandshakeThrottleTest;FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,ThrottleSucceedsFirst);FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,HandshakeSucceedsFirst);FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,ThrottleReportsErrorBeforeConnect);FRIEND_TEST_ALL_PREFIXES(WebSocketChannelImplHandshakeThrottleTest,ThrottleReportsErrorAfterConnect);class BlobLoader;class Message;struct ConnectInfo;enum MessageType {kMessageTypeText,kMessageTypeBlob,kMessageTypeArrayBuffer,kMessageTypeClose,};struct ReceivedMessage {bool is_message_text;Vector<char> data;};class Message final {DISALLOW_NEW();public:using DidCallSendMessage =base::StrongAlias<class DidCallSendMessageTag, bool>;// Initializes message as a stringMessage(v8::Isolate*,const std::string&,base::OnceClosure completion_callback,DidCallSendMessage did_call_send_message);// Initializes message as a blobexplicit Message(scoped_refptr<BlobDataHandle>);// Initializes message from the contents of a blobMessage(MessageData, size_t);// Initializes message as a ArrayBufferMessage(v8::Isolate*,base::span<const char> message,base::OnceClosure completion_callback,DidCallSendMessage did_call_send_message);// Initializes a Blank messageMessage(MessageType type,base::span<const char> message,base::OnceClosure completion_callback);// Close messageMessage(uint16_t code, const String& reason);Message(const Message&) = delete;Message& operator=(const Message&) = delete;Message(Message&&);Message& operator=(Message&&);MessageType Type() const;scoped_refptr<BlobDataHandle> GetBlobDataHandle();DidCallSendMessage GetDidCallSendMessage() const;uint16_t Code() const;String Reason() const;base::OnceClosure CompletionCallback();// Returns a mutable |pending_payload_|. Since calling code always mutates// the value, |pending_payload_| only has a mutable getter.base::span<const char>& MutablePendingPayload();void SetDidCallSendMessage(DidCallSendMessage did_call_send_message);private:MessageData message_data_;MessageType type_;scoped_refptr<BlobDataHandle> blob_data_handle_;base::span<const char> pending_payload_;DidCallSendMessage did_call_send_message_ = DidCallSendMessage(false);uint16_t code_ = 0;String reason_;base::OnceClosure completion_callback_;};// A handle to a global count of the number of WebSockets that have been// created. Can be used to limit the total number of WebSockets that have been// created in this render process.class ConnectionCountTrackerHandle {DISALLOW_NEW();public:enum class CountStatus {kOkayToConnect,kShouldNotConnect,};ConnectionCountTrackerHandle() = default;~ConnectionCountTrackerHandle() = default;ConnectionCountTrackerHandle(const ConnectionCountTrackerHandle&) = delete;ConnectionCountTrackerHandle& operator=(const ConnectionCountTrackerHandle&) = delete;// Increments the count and returns SHOULD_NOT_CONNECT if it exceeds// kMaxWebSocketsPerRenderProcess. Should only be called once.CountStatus IncrementAndCheckStatus();// Decrements the count. Should be called at least once. If there is no// matching call to IncrementAndCheckStatus() it does nothing, so it is safe// to call multiple times.void Decrement();private:bool incremented_ = false;};// The state is defined to see the conceptual state more clearly than checking// various members (for DCHECKs for example). This is only used internally.enum class State {// The channel is running an opening handshake. This is the initial state.// It becomes |kOpen| when the connection is established. It becomes// |kDisconnected| when detecting an error.kConnecting,// The channel is ready to send / receive messages. It becomes// |kDisconnected| when the connection is closed or when an error happens.kOpen,// The channel is not ready for communication. The channel stays in this// state forever.kDisconnected,};State GetState() const;bool MaybeSendSynchronously(network::mojom::blink::WebSocketMessageType,base::span<const char>* data);void ProcessSendQueue();bool SendMessageData(base::span<const char>* data);void FailAsError(const String& reason) {Fail(reason, mojom::ConsoleMessageLevel::kError,location_at_construction_->Clone());}void AbortAsyncOperations();void HandleDidClose(bool was_clean, uint16_t code, const String& reason);// Completion callback. It is called with the results of throttling.void OnCompletion(const absl::optional<WebString>& error);// Methods for BlobLoader.void DidFinishLoadingBlob(MessageData, size_t);void BlobTooLarge();void DidFailLoadingBlob(FileErrorCode);void TearDownFailedConnection();bool ShouldDisallowConnection(const KURL&);BaseFetchContext* GetBaseFetchContext() const;// Called when |readable_| becomes readable.void OnReadable(MojoResult result, const mojo::HandleSignalsState& state);void ConsumePendingDataFrames();void ConsumeDataFrame(bool fin,network::mojom::blink::WebSocketMessageType type,const char* data,size_t data_size);// Called when |writable_| becomes writable.void OnWritable(MojoResult result, const mojo::HandleSignalsState& state);MojoResult ProduceData(base::span<const char>* data,uint64_t* consumed_buffered_amount);String GetTextMessage(const Vector<base::span<const char>>& chunks,wtf_size_t size);void OnConnectionError(const base::Location& set_from,uint32_t custom_reason,const std::string& description);void Dispose();const Member<WebSocketChannelClient> client_;KURL url_;uint64_t identifier_;Member<BlobLoader> blob_loader_;WTF::Deque<Message> messages_;Member<WebSocketMessageChunkAccumulator> message_chunks_;const Member<ExecutionContext> execution_context_;bool backpressure_ = false;bool receiving_message_type_is_text_ = false;bool received_text_is_all_ascii_ = true;bool throttle_passed_ = false;bool has_initiated_opening_handshake_ = false;size_t sent_size_of_top_message_ = 0;FrameScheduler::SchedulingAffectingFeatureHandlefeature_handle_for_scheduler_;WTF::String failure_message_;const std::unique_ptr<const SourceLocation> location_at_construction_;network::mojom::blink::WebSocketHandshakeRequestPtr handshake_request_;std::unique_ptr<WebSocketHandshakeThrottle> handshake_throttle_;// This field is only initialised if the object is still waiting for a// throttle response when DidConnect is called.std::unique_ptr<ConnectInfo> connect_info_;HeapMojoRemote<network::mojom::blink::WebSocket> websocket_;HeapMojoReceiver<network::mojom::blink::WebSocketHandshakeClient,WebSocketChannelImpl>handshake_client_receiver_;HeapMojoReceiver<network::mojom::blink::WebSocketClient, WebSocketChannelImpl>client_receiver_;mojo::ScopedDataPipeConsumerHandle readable_;mojo::SimpleWatcher readable_watcher_;WTF::Deque<DataFrame> pending_data_frames_;mojo::ScopedDataPipeProducerHandle writable_;mojo::SimpleWatcher writable_watcher_;bool wait_for_writable_ = false;ConnectionCountTrackerHandle connection_count_tracker_handle_;const scoped_refptr<base::SingleThreadTaskRunner> file_reading_task_runner_;
};MODULES_EXPORT std::ostream& operator<<(std::ostream&,const WebSocketChannelImpl*);}  // namespace blink

四、调用堆栈参考:

Chromium 中HTML5 WebSocket实现分析c++(二)-CSDN博客

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

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

相关文章

prompt learning

prompt learning 对于CLIP&#xff08;如上图所示&#xff09;而言&#xff0c;对其prompt构造的更改就是在zero shot应用到下游任务的时候对其输入的label text进行一定的更改&#xff0c;比如将“A photo of a{obj}”改为“[V1][V2]…[Vn][Class]”这样可学习的V1-Vn的token…

业务开发常见问题-并发工具类

hello&#xff0c;大家好&#xff0c;本讲我们一起聊一下常见的几个并发工具类的使用和坑&#xff01; 在日常工作中&#xff0c;我们经常会遇到多线程并发问题&#xff0c;比如ThreadLocal、锁、ConcurrentHashMap、CopyOnWriteArrayList等。那么如何正常的使用呢&#xff1f;…

【最新通知】2024年Cisco思科认证CCNA详解

CCNA现在涵盖安全性、自动化和可编程性。该计划拥有一项涵盖IT职业基础知识的认证&#xff0c;包括一门考试和一门培训课程&#xff0c;助您做好准备。 CCNA培训课程和考试最近面向最新技术和工作岗位进行了重新调整&#xff0c;为您提供了向任何方向发展事业所需的基础。CCNA认…

blender分离含有多个动作的模型,并导出含有材质的fbx模型

问题背景 笔者是模型小白&#xff0c;需要将网络上下载的fbx模型中的动作&#xff0c;分离成单独的动作模型&#xff0c;经过3天摸爬滚打&#xff0c;先后使用了blender&#xff0c;3d max&#xff0c;unity&#xff0c;最终用blender完成&#xff0c;期间参考了众多网络上大佬…

【Ansiable】ansible的模块和主机清单

目录 一、介绍一些运维自动化工具 二、Ansible 概述/简介 三、Ansible 工作机制 3.1 内部工作机制 3.2 外部工作机制 四、Ansible 执行流程 五、Ansblie 安装以及日常操作模块***** 5.1 ansible 环境安装部署 5.2 ansible 命令行模块 5.2.1 command 模块 5.2.2 shel…

明源云ERP报表服务GetErpConfig.aspx接口存在敏感信息泄露

一、漏洞简介 在访问 /service/Mysoft.Report.Web.Service.Base/GetErpConfig.aspx?erpKeyerp60 路径时&#xff0c;返回了包含敏感信息的响应。这些信息包括但不限于数据库连接字符串、用户名、密码、加密密钥等。这些敏感信息的暴露可能导致以下风险&#xff1a;数据库访问…

【IPv6】IPv6 NAT66介绍

参考链接 IPv6-to-IPv6 Network Address Translation (NAT66) (ietf.org)https://datatracker.ietf.org/doc/id/draft-mrw-nat66-00.html IPv6 NAT66 NAT66&#xff0c;全称为Network Address Translation for IPv6 to IPv6&#xff0c;是一种用于IPv6网络的地址转换技术。在…

Tkinter -- python GUI学习与使用

前言 python GUI 目前pythonGUI有很多&#xff0c;哪一个最好&#xff1f; 先说说我选择的思路&#xff0c;我的目的是开发一个易用的软件&#xff0c;最重要的是稳定&#xff0c;并且碰到问题能够解决&#xff0c;因此&#xff0c;我的目标很明确&#xff0c;有比较大的用户群…

基于Python的自然语言处理系列(39):Huggingface中的解码策略

在自然语言生成任务中&#xff0c;如何选择下一步的单词或者词语对生成的文本质量影响巨大。Huggingface 提供了多种解码策略&#xff0c;可以在不同的场景下平衡流畅度、创造力以及生成效率。在这篇文章中&#xff0c;我们将逐步介绍 Huggingface 中的几种常见解码策略&#x…

web API基础

作用和分类 作用: 就是使用 JS 去操作 html 和浏览器 分类&#xff1a; DOM (文档对象模型)、 BOM &#xff08;浏览器对象模型&#xff09; 什么是DOM DOM (Document Object Model) 译为文档对象模型&#xff0c;是 HTML 和 XML 文档的编程接口。 HTML DOM 定义了访问和操作 …

mingw64的Windows安装及配置教程gcc、g++等

mingw64.rar 链接&#xff1a;https://pan.baidu.com/s/18YrDRyi5NHtqnTwhJG6PuA 提取码&#xff1a;pbli &#xff08;免费永久有效&#xff0c;免安装&#xff0c;解压后配置环境变量即可使用&#xff09; 1 下载 解压后随便放到一个地方&#xff1a; 复制“bin”路径&am…

重磅:中国首个SVG技术标准发布,计育韬老师主笔起草

编辑搜图 中华人民共和国《融媒体 SVG 交互设计技术规范》是由复旦大学奇点新媒体研究中心等单位牵头&#xff0c;学科带头人计育韬等人主要起草&#xff0c;并于 2024 年 8 月起面向全社会行业从业者发布的最高技术标准。该标准前身为 2016 年计育韬与微信团队合作拟定的《SV…

置分辨率设置多显示器的时候提示, 某些设置由系统管理员进行管理

遇到的问题 设置分辨率设置多显示器的时候提示&#xff08;如下图所示&#xff09;&#xff1a; 某些设置由系统管理员进行管理 解决方法 先试试这个方法&#xff1a; https://answers.microsoft.com/zh-hans/windows/forum/all/%E6%9B%B4%E6%94%B9%E5%88%86%E8%BE%A8%E7%8…

强大的Python必备库,你知道几个?建议收藏!

在Python的世界里&#xff0c;库的丰富性让开发者的工作变得轻松而高效。那么&#xff0c;你知道哪些强大的Python必备库吗&#xff1f; 面对众多的Python库&#xff0c;如何选择适合自己的工具来提升开发效率和代码质量&#xff1f;&#xff0c;丰富多样的库如同一个个强大的…

AnaTraf | 提升网络性能:深入解析网络关键指标监控、TCP重传与TCP握手时间

AnaTraf 网络性能监控系统NPM | 全流量回溯分析 | 网络故障排除工具 在当今的数字化时代&#xff0c;网络的稳定性和性能对企业的运营效率至关重要。无论是内部通信、应用程序的运行&#xff0c;还是对外提供服务&#xff0c;网络都发挥着关键作用。对于网络工程师或IT运维人员…

EasyX图形库的安装

前言 EasyX是一个图形库&#xff0c;可以用来做一些c/c小游戏&#xff0c;帮助学习。 一、进入EasyX官网 https://easyx.cn/ 二、点击下载EasyX 三、下载好后以管理员身份运行它 四、点击下一步 五、然后它会自动检测你的编辑器&#xff0c;用哪个就在哪个点安装 六、安装成功…

【linux问题】Linux命令行终端窗口的输出,显示不全问题

Linux命令行终端窗口的输出&#xff0c;显示不全问题 问题&#xff1a; 图中显示的是一个Linux命令行终端窗口&#xff0c; nmcli dev wifi 是一个命令——列出所有能用的Wi-Fi。 执行命令后&#xff0c;窗口输出了显示了所有能用的Wi-Fi网络列表。 但是在每一行末尾有一个“…

KPaaS:基于微服务架构的低代码平台

基于微服务架构的低代码平台是当前企业数字化转型的重要工具。通过将微服务架构与低代码平台结合&#xff0c;可以实现高效、灵活且可扩展的应用开发。在当今数字化转型的浪潮中&#xff0c;企业面临着诸多挑战&#xff0c;其中IT资源有限以及对高效开发的需求尤为突出。KPaaS业…

AI一键生成钢铁是怎样炼成的ppt!用这2个工具轻松拿捏ppt制作!

钢铁是怎样炼成的&#xff0c;是中小学语文新课标必读书目&#xff0c;它是由前苏联作家尼古拉奥斯特洛夫斯基于1930年至1934年写成的半自传体长篇小说&#xff0c;全书详细记叙了保尔柯察金于20世纪初期的成长历程。 对于每个接受过九年义务教育的大小朋友来说&#xff0c;这…

漏洞挖掘JS构造新手向

前置思路文章 JS逆向混淆前端对抗 油猴JS逆向插件 JS加解密之mitmproxy工具联动Burp JS挖掘基础 伪协议 JavaScript伪协议是一种在浏览器中模拟网络请求的方法。它使用window.XMLHttpRequest对象或fetch()方法来模拟发送HTTP请求&#xff0c;而不是通过实际的网络请求来获…