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 để liên lạc, chơi trò chơi hoặc chuyển tệp có thể là một quy trình khá phức tạp. Việc này đòi hỏi bạn phải thiết lập và trả tiền cho một máy chủ để chuyển tiếp dữ liệu, đồng thời có thể mở rộng quy mô này sang nhiều trung tâm dữ liệu. Trong trường hợp này, có thể xảy ra độ trễ cao và khó bảo mật dữ liệu.

Bạn có thể giảm thiểu những vấn đề này bằng cách sử dụng API RTCDataChannel của WebRTC để chuyển dữ liệu trực tiếp từ một máy ngang hàng sang một máy ngang hàng khác. Bài viết này trình bày những kiến thức cơ bản về cách thiết lập và sử dụng các 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 cần một kênh dữ liệu khác?

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

RTCDataChannel có một cách tiếp cận 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ể dẫn đến độ trễ thấp hơn – không có máy chủ trung gian và ít "chặng" hơn.
  • RTCDataChannel sử dụng Giao thức truyền tải kiểm soát luồng (SCTP), cho phép định cấu hình ngữ nghĩa phân phối – phân phối không theo thứ tự và cấu hình truyền lại.

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

Lưu ý: Truyền tín hiệu, STUN và TURN

WebRTC cho phép giao tiếp ngang hàng, nhưng vẫn cần các máy chủ để truyền tín hiệu nhằm trao đổi siêu dữ liệu về nội dung nghe nhìn và 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 cách:

  • Khung ICE để thiết lập đường dẫn mạng tốt nhất có thể giữa các thiết bị ngang hàng.
  • Máy chủ STUN để xác định IP và cổng có thể truy cập công khai cho mỗi người dùng ngang hàng.
  • Máy chủ TURN nếu kết nối trực tiếp không thành công 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 linh hoạt các loại dữ liệu. API này được thiết kế để mô phỏng chính xác WebSocket và RTCDataChannel hỗ trợ các chuỗi cũng như một số loại 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 làm việc với tính năng 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à theo 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 tải thông báo cũng như thứ tự mà thông báo được gửi. Đ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 đến được phía bên kia cũng như thứ tự mà chúng đến. Điều này giúp loại bỏ các chi phí phát sinh, 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 2 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ể đã cũ 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.

Sau đây là một bảng hữu ích trong cuốn sách High Performance Browser Networking (Mạng 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 độngHướng theo byteHướng đến tin nhắnHướng đến tin nhắn
Kiểm soát luồngKhông
Kiểm soát 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 chế độ 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 sẽ tự kết nối ngang hàng, 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 đó, nó sẽ tạo một kênh dữ liệu và gửi thông báo qua kết nối ngang hàng. Cuối cùng, thông điệp 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 việc 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 đối tượng này trước hoặc sau khi quá trình báo hiệu diễn ra. Sau đó, bạn sẽ truyền một nhãn để phân biệt kênh này với các kênh khác và một nhóm 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 lựa chọn maxRetransmits (số lần thử trước khi thất bại), 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 sau đây của IETF: 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 meta cho ứng dụng
  • negotiated: nếu được đặt thành true, sẽ xoá chế độ thiết lập tự động kênh dữ liệu trên thiết bị ngang hàng khác, cung cấp cách riêng của bạn để 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 riêng cho kênh và chỉ có thể dùng mã này khi kết hợp với negotiated được đặt thành true)

Ba lựa chọn đầu tiên là những lựa chọn duy nhất mà hầu hết mọi người cần sử dụng: ordered, maxPacketLifeTimemaxRetransmits. Với SCTP (hiện được tất cả trình duyệt hỗ trợ WebRTC sử dụng), độ tin cậy và thứ tự theo mặc định là true. Bạn nên sử dụng tuỳ chọn không đáng tin cậy và không theo thứ tự nếu muốn kiểm soát hoàn toàn 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, cũng như khi nhận được thông báo từ thiết bị ngang hàng khác.

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

Mã hoá là bắt buộc đối với tất cả các thành phần WebRTC. Với RTCDataChannel, tất cả dữ liệu đều được bảo mật bằng Bảo mật tầng truyền tải gói thông tin (DTLS). DTLS là một phiên bản phái sinh 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 tiêu chuẩn nào dựa trên SSL. 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 trang wiki của Wireshark.

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

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 một cách mới. Nếu đang chuyển một tệp có kích thước lớn hơn dung lượng bộ nhớ hiện có, 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 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, khiến tính năng chia sẻ tệp WebRTC trở thành một lựa chọn sáng giá cho web.

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

  1. Đọc một tệp trong JavaScript bằng File API.
  2. Thiết lập 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 số điểm cần cân nhắc khi bạn tìm cách gửi tệp qua RTCDataChannel:

  • Kích thước tệp: nếu kích thước tệp tương đối nhỏ và có thể được lưu trữ và tải dưới dạng một Blob, bạn có thể tải vào bộ nhớ bằng File API rồi 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 truyền tối đa). Khi kích thước tệp tăng lê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 đoạn tệp sẽ được tải và gửi đến một thiết bị ngang hàng khác, kèm theo siêu dữ liệu chunkID để thiết bị ngang hàng đó có thể nhận dạng các đoạn tệp. 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ụ: bằ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 khối: đây là "đơn vị" dữ liệu nhỏ nhất cho ứng dụng của bạn. Bạn phải phân chia thành khối vì hiện tại có giới hạn về kích thước gửi (mặc dù giới hạn này sẽ được khắc phục trong phiên bản kênh dữ liệu sau này). Đề xuất hiện tại về kích thước tối đa của đoạn là 64 KiB.

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

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 đều 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 một ứ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 thức mới để tạo ứng dụng chia sẻ tệp, trò chơi nhiều người chơi và phân phối nội dung.

  • 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
  • PeerCDN đã tái tạo quy trình phân phối nội dung. Đây là một khung phân phối tài sản trên web thông qua giao tiếp dữ liệu ngang hàng

Thay đổi cách bạn tạo ứ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 và độ trễ thấp thông qua RTCDataChannel. Các khung hình, chẳng hạn như PeerJSPubNub WebRTC SDK, giúp bạn dễ dàng triển khai RTCDataChannel và API hiện được hỗ trợ rộng rãi trên nhiều nền tảng.

Sự ra đời 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