Gửi dữ liệu giữa các trình duyệt bằng kênh dữ liệu WebRTC

Việc gửi dữ liệu giữa hai trình duyệt để giao tiếp, chơi trò chơi hoặc chuyển tệp có thể là một quá trình khá phức tạp. Bạn cần thiết lập và trả phí cho một máy chủ để chuyển tiếp dữ liệu, đồng thời có thể mở rộng quy mô cho nhiều trung tâm dữ liệu. Trong trường hợp này, có thể xảy ra độ trễ cao và khó giữ bí mật dữ liệu.

Bạn có thể giảm thiểu các vấn đề này bằng cách sử dụng API RTCDataChannel của WebRTC để truyền dữ liệu trực tiếp từ máy khách này sang máy khách khác. Bài viết này trình bày các kiến thức cơ bản về cách thiết lập và sử dụng kênh dữ liệu, cũng như các trường hợp sử dụng phổ biến trên web hiện nay.

Tại sao lại cần một kênh dữ liệu khác?

Chúng ta có WebSocket, AJAXSự kiện do máy chủ gửi. Tại sao chúng ta cần một kênh liên lạc khác? WebSocket là hai chiều, nhưng tất cả các công nghệ này đều được thiết kế để giao tiếp với hoặc từ máy chủ.

RTCDataChannel sử dụng một phương pháp khác:

  • API này hoạt động với API RTCPeerConnection, cho phép kết nối ngang hàng. Điều này có thể làm giảm độ trễ – không có máy chủ trung gian và ít "điểm chuyển tiếp" hơn.
  • RTCDataChannel sử dụng Giao thức truyền điều khiển luồng (SCTP), cho phép cấu hình phân phối ngữ nghĩa-phân phối không theo thứ tự và cấu hình truyền lại.

RTCDataChannel hiện có hỗ trợ SCTP trên máy tính và Android trong Google Chrome, Opera và Firefox.

Cảnh báo: Báo hiệu, STUN và TURN

WebRTC cho phép giao tiếp ngang hàng, nhưng vẫn cần máy chủ để gửi tín hiệu nhằm trao đổi nội dung nghe nhìn và siêu dữ liệu mạng để khởi động một kết nối ngang hàng.

WebRTC xử lý NAT và tường lửa bằng:

  • Khung ICE để thiết lập đường dẫn mạng tốt nhất có thể giữa các máy ngang hàng.
  • Máy chủ STUN để xác định một địa chỉ IP và cổng có thể truy cập công khai cho mỗi máy ngang hàng.
  • Máy chủ TURN nếu không kết nối được trực tiếp và cần chuyển tiếp dữ liệu.

Để biết thêm thông tin về cách WebRTC hoạt động với các máy chủ để báo hiệu và kết nối mạng, hãy xem bài viết WebRTC trong thực tế: STUN, TURN và báo hiệu.

Các chức năng

API RTCDataChannel hỗ trợ một tập hợp các loại dữ liệu linh hoạt. API này được thiết kế để mô phỏng chính xác WebSocket và RTCDataChannel hỗ trợ chuỗi cũng như một số loại tệp nhị phân trong JavaScript, chẳng hạn như Blob, ArrayBufferArrayBufferView. Các loại này có thể hữu ích khi bạn chuyển tệp và chơi trò chơi nhiều người chơi.

RTCDataChannel có thể hoạt động ở chế độ không đáng tin cậy và không theo thứ tự (tương tự như Giao thức dữ liệu người dùng hoặc UDP), chế độ đáng tin cậy và có thứ tự (tương tự như Giao thức điều khiển truyền tải hoặc TCP) và các chế độ đáng tin cậy một phần:

  • Chế độ đáng tin cậy và có thứ tự đảm bảo việc truyền tin nhắn cũng như thứ tự phân phối tin nhắn. Điều này làm tăng thêm chi phí, do đó có thể làm cho chế độ này chậm hơn.
  • Chế độ không đáng tin cậy và không theo thứ tự không đảm bảo mọi thông báo đều được gửi đến phía bên kia cũng như thứ tự gửi thông báo. Điều này giúp loại bỏ hao tổn, cho phép chế độ này hoạt động nhanh hơn nhiều.
  • Chế độ đáng tin cậy một phần đảm bảo việc truyền thông báo trong một điều kiện cụ thể, chẳng hạn như thời gian chờ truyền lại hoặc số lần truyền lại tối đa. Bạn cũng có thể định cấu hình thứ tự của các thông báo.

Hiệu suất của hai chế độ đầu tiên gần như giống nhau khi không có gói dữ liệu nào bị mất. Tuy nhiên, ở chế độ đáng tin cậy và có thứ tự, một gói bị mất sẽ khiến các gói khác bị chặn phía sau gói đó và gói bị mất có thể đã lỗi thời vào thời điểm được truyền lại và đến nơi. Tất nhiên, bạn có thể sử dụng nhiều kênh dữ liệu trong cùng một ứng dụng, mỗi kênh có ngữ nghĩa đáng tin cậy hoặc không đáng tin cậy riêng.

Dưới đây là bảng hữu ích trong bài viết High Performance Browser Networking (Mạng truyền tải trình duyệt hiệu suất cao) của Ilya Grigorik:

TCPUDPSCTP
Độ tin cậyĐáng tin cậyKhông đáng tin cậyCó thể định cấu hình
Giao hàngĐã đặt hàngKhông theo thứ tựCó thể định cấu hình
Hệ thống truyền độngĐịnh hướng theo byteĐịnh hướng theo thông điệpĐịnh hướng theo thông điệp
Kiểm soát luồngKhông
Kiểm soát tình trạng tắc nghẽnKhông

Tiếp theo, bạn sẽ tìm hiểu cách định cấu hình RTCDataChannel để sử dụng chế độ đáng tin cậy và có thứ tự hoặc không đáng tin cậy và không có thứ tự.

Định cấu hình kênh dữ liệu

Có một số bản minh hoạ đơn giản về RTCDataChannel trên mạng:

Trong các ví dụ này, trình duyệt tạo một kết nối ngang hàng với chính nó, sau đó tạo một kênh dữ liệu và gửi thông báo thông qua kết nối ngang hàng. Sau đó, ứng dụng này sẽ tạo một kênh dữ liệu và gửi thông báo dọc theo kết nối ngang hàng. Cuối cùng, thông báo của bạn sẽ xuất hiện trong hộp ở phía bên kia của trang!

Mã để bắt đầu với tính năng này rất ngắn:

const peerConnection = new RTCPeerConnection();

// Establish your peer connection using your signaling channel here
const dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = (error) => {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = (event) => {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = () => {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = () => {
  console.log("The Data Channel is Closed");
};

Đối tượng dataChannel được tạo từ một kết nối ngang hàng đã được thiết lập. Bạn có thể tạo trước hoặc sau khi tín hiệu xảy ra. Sau đó, bạn truyền vào một nhãn để phân biệt kênh này với các kênh khác và một tập hợp các chế độ cài đặt cấu hình không bắt buộc:

const dataChannelOptions = {
  ordered: false, // do not guarantee order
  maxPacketLifeTime: 3000, // in milliseconds
};

Bạn cũng có thể thêm tuỳ chọn maxRetransmits (số lần thử trước khi không thành công), nhưng bạn chỉ có thể chỉ định maxRetransmits hoặc maxPacketLifeTime, chứ không thể chỉ định cả hai. Đối với ngữ nghĩa UDP, hãy đặt maxRetransmits thành 0ordered thành false. Để biết thêm thông tin, hãy xem các RFC IETF sau: Giao thức truyền tải điều khiển luồngTiện ích độ tin cậy một phần của Giao thức truyền tải điều khiển luồng.

  • ordered: liệu kênh dữ liệu có đảm bảo thứ tự hay không
  • maxPacketLifeTime: thời gian tối đa để thử và truyền lại một thông báo không thành công
  • maxRetransmits: số lần tối đa để thử và truyền lại một thông báo không thành công
  • protocol: cho phép sử dụng một giao thức phụ cung cấp thông tin siêu dữ liệu cho ứng dụng
  • negotiated: nếu được đặt thành true, sẽ xoá chế độ tự động thiết lập kênh dữ liệu trên máy ngang hàng khác, cung cấp cách riêng để tạo kênh dữ liệu có cùng mã nhận dạng ở phía bên kia
  • id: cho phép bạn cung cấp mã nhận dạng của riêng mình cho kênh. Mã nhận dạng này chỉ có thể được sử dụng kết hợp với negotiated được đặt thành true)

Các tuỳ chọn duy nhất mà hầu hết mọi người cần sử dụng là 3 tuỳ chọn đầu tiên: ordered, maxPacketLifeTimemaxRetransmits. Với SCTP (hiện được tất cả trình duyệt hỗ trợ WebRTC sử dụng), đáng tin cậy và có thứ tự là đúng theo mặc định. Bạn nên sử dụng không đáng tin cậy và không theo thứ tự nếu muốn có toàn quyền kiểm soát từ lớp ứng dụng, nhưng trong hầu hết các trường hợp, độ tin cậy một phần sẽ hữu ích.

Xin lưu ý rằng, giống như WebSocket, RTCDataChannel sẽ kích hoạt các sự kiện khi một kết nối được thiết lập, đóng hoặc gặp lỗi và khi nhận được thông báo từ máy chủ đồng cấp khác.

Thử thách này có an toàn không?

Tất cả thành phần WebRTC đều bắt buộc phải mã hoá. Với RTCDataChannel, tất cả dữ liệu đều được bảo mật bằng Giao thức bảo mật tầng truyền tải gói thông tin (DTLS). DTLS là một dẫn xuất của SSL, nghĩa là dữ liệu của bạn sẽ an toàn như khi sử dụng bất kỳ kết nối dựa trên SSL tiêu chuẩn nào. DTLS được chuẩn hoá và tích hợp vào tất cả trình duyệt hỗ trợ WebRTC. Để biết thêm thông tin, hãy xem Wireshark wiki.

Thay đổi cách bạn suy nghĩ về dữ liệu

Việc xử lý lượng lớn dữ liệu có thể là một điểm khó khăn trong JavaScript. Như các nhà phát triển của Sharefest đã chỉ ra, điều này đòi hỏi phải suy nghĩ về dữ liệu theo cách mới. Nếu đang chuyển một tệp lớn hơn dung lượng bộ nhớ có sẵn, bạn phải nghĩ đến những cách mới để lưu thông tin này. Đây là nơi các công nghệ, chẳng hạn như FileSystem API, phát huy tác dụng, như bạn sẽ thấy trong phần tiếp theo.

Tạo ứng dụng chia sẻ tệp

Giờ đây, bạn có thể tạo một ứng dụng web có thể chia sẻ tệp trong trình duyệt bằng RTCDataChannel. Việc xây dựng dựa trên RTCDataChannel có nghĩa là dữ liệu tệp được chuyển sẽ được mã hoá và không chạm vào máy chủ của nhà cung cấp ứng dụng. Chức năng này, kết hợp với khả năng kết nối với nhiều ứng dụng để chia sẻ nhanh hơn, giúp tính năng chia sẻ tệp WebRTC trở thành một ứng dụng mạnh mẽ cho web.

Bạn cần thực hiện một số bước để chuyển thành công:

  1. Đọc tệp trong JavaScript bằng File API (API Tệp).
  2. Tạo kết nối ngang hàng giữa các ứng dụng bằng RTCPeerConnection.
  3. Tạo kênh dữ liệu giữa các ứng dụng bằng RTCDataChannel.

Có một vài điểm cần cân nhắc khi cố gắng gửi tệp qua RTCDataChannel:

  • Kích thước tệp: nếu kích thước tệp nhỏ hợp lý và có thể được lưu trữ và tải dưới dạng một Blob, thì bạn có thể tải vào bộ nhớ bằng File API, sau đó gửi tệp đó qua một kênh đáng tin cậy (mặc dù hãy lưu ý rằng trình duyệt áp đặt giới hạn về kích thước chuyển tối đa). Khi kích thước tệp lớn hơn, mọi thứ sẽ trở nên phức tạp hơn. Khi cần có cơ chế phân đoạn, các phân đoạn tệp sẽ được tải và gửi đến một máy ngang hàng khác, kèm theo siêu dữ liệu chunkID để máy ngang hàng có thể nhận ra các phân đoạn đó. Xin lưu ý rằng trong trường hợp này, trước tiên, bạn cũng cần lưu các đoạn vào bộ nhớ ngoại tuyến (ví dụ: sử dụng FileSystem API) và chỉ lưu vào ổ đĩa của người dùng khi bạn có toàn bộ tệp.
  • Kích thước phân đoạn: đây là những "nguyên tử" dữ liệu nhỏ nhất cho ứng dụng của bạn. Bạn phải phân đoạn vì hiện có giới hạn kích thước gửi (mặc dù vấn đề này sẽ được khắc phục trong phiên bản kênh dữ liệu trong tương lai). Kích thước khối tối đa được đề xuất hiện tại là 64KiB.

Sau khi tệp được chuyển hoàn toàn sang phía bên kia, bạn có thể tải tệp đó xuống bằng thẻ neo:

function saveFile(blob) {
  const link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

Các ứng dụng chia sẻ tệp này trên PubShareGitHub sử dụng kỹ thuật này. Cả hai đều là nguồn mở và cung cấp nền tảng tốt cho ứng dụng chia sẻ tệp dựa trên RTCDataChannel.

Vậy bạn có thể làm gì?

RTCDataChannel mở ra những cách mới để xây dựng ứng dụng chia sẻ tệp, chơi trò chơi nhiều người chơi và phân phối nội dung.

  • Hoạt động chia sẻ tệp ngang hàng như mô tả trước đó
  • Chơi trò chơi nhiều người chơi, kết hợp với các công nghệ khác, chẳng hạn như WebGL, như trong BananaBread của Mozilla
  • Phân phối nội dung do PeerCDN tái tạo, một khung phân phối tài sản web thông qua giao tiếp dữ liệu ngang hàng

Thay đổi cách bạn xây dựng ứng dụng

Giờ đây, bạn có thể cung cấp các ứng dụng hấp dẫn hơn bằng cách sử dụng các kết nối có hiệu suất cao, độ trễ thấp thông qua RTCDataChannel. Các khung, chẳng hạn như PeerJSSDK WebRTC PubNub, giúp triển khai RTCDataChannel dễ dàng hơn và API hiện được hỗ trợ rộng rãi trên các nền tảng.

Sự xuất hiện của RTCDataChannel có thể thay đổi cách bạn nghĩ về việc chuyển dữ liệu trong trình duyệt.

Tìm hiểu thêm