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

一、WebSocket前端接口定义:

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

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

构造函数

WebSocket(url[, protocols])

返回一个 WebSocket 对象。

常量

Constant Value
WebSocket.CONNECTING 0
WebSocket.OPEN 1
WebSocket.CLOSING 2
WebSocket.CLOSED 3

属性

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

cpp 复制代码
// https://html.spec.whatwg.org/C/#the-websocket-interface

enum 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 state
    const 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;

    // networking
    attribute 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);

    // messaging
    attribute 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

cpp 复制代码
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

cpp 复制代码
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前端接口通信收发数据等。

cpp 复制代码
// 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

cpp 复制代码
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>");
   });
#endif

  const 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));

cpp 复制代码
// 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");

cpp 复制代码
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>");
   });
#endif

  const 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));

cpp 复制代码
// 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相互对应。

cpp 复制代码
class GURL;

namespace base {
class Location;
}  // namespace base

namespace net {
class IOBuffer;
class IsolationInfo;
class SSLInfo;
class SiteForCookies;
class WebSocketChannel;
}  // namespace net

namespace 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 afterwards
    kMojoPipe,
    // Interrupted by the interceptor or resuming afterwards
    kInterceptor,
  };

  // 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进程 发送和接收数据的具体实现。

cpp 复制代码
namespace v8 {
class Isolate;
}  // namespace v8

namespace 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 string
    Message(v8::Isolate*,
            const std::string&,
            base::OnceClosure completion_callback,
            DidCallSendMessage did_call_send_message);

    // Initializes message as a blob
    explicit Message(scoped_refptr<BlobDataHandle>);

    // Initializes message from the contents of a blob
    Message(MessageData, size_t);

    // Initializes message as a ArrayBuffer
    Message(v8::Isolate*,
            base::span<const char> message,
            base::OnceClosure completion_callback,
            DidCallSendMessage did_call_send_message);

    // Initializes a Blank message
    Message(MessageType type,
            base::span<const char> message,
            base::OnceClosure completion_callback);

    // Close message
    Message(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::SchedulingAffectingFeatureHandle
      feature_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博客

相关推荐
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年7 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder7 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727577 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架