Sử dụng WebTransport

WebTransport là một API cung cấp tính năng nhắn tin hai chiều, độ trễ thấp giữa máy khách và máy chủ. Tìm hiểu thêm về các trường hợp sử dụng và cách đưa ra ý kiến phản hồi về việc triển khai trong tương lai.

Thông tin khái quát

WebTransport là gì?

WebTransport là một API web sử dụng giao thức HTTP/3 làm phương thức truyền tải hai chiều. Giao thức này dành cho hoạt động giao tiếp hai chiều giữa ứng dụng web và máy chủ HTTP/3. API này hỗ trợ gửi dữ liệu không đáng tin cậy thông qua API datagram và đáng tin cậy thông qua API luồng.

Datagram là phương thức lý tưởng để gửi và nhận dữ liệu không cần đảm bảo phân phối mạnh mẽ. Kích thước của từng gói dữ liệu bị giới hạn bởi đơn vị truyền tối đa (MTU) của kết nối cơ bản và có thể truyền thành công hoặc không. Nếu được chuyển, các gói dữ liệu có thể đến theo thứ tự tuỳ ý. Những đặc điểm này khiến API datagram trở nên lý tưởng cho việc truyền dữ liệu có độ trễ thấp, trong khả năng tốt nhất. Bạn có thể coi các gói dữ liệu là thông điệp giao thức dữ liệu người dùng (UDP), nhưng được mã hoá và kiểm soát tình trạng tắc nghẽn.

Ngược lại, các API luồng cung cấp tính năng truyền dữ liệu theo thứ tự đáng tin cậy. Luồng dữ liệu có thứ tự phù hợp với các trường hợp bạn cần gửi hoặc nhận một hoặc nhiều luồng dữ liệu có thứ tự. Việc sử dụng nhiều luồng WebTransport tương tự như việc thiết lập nhiều kết nối TCP, nhưng vì HTTP/3 sử dụng giao thức QUIC nhẹ hơn, nên các luồng này có thể được mở và đóng mà không tốn nhiều chi phí.

Trường hợp sử dụng

Đây là một danh sách nhỏ về những cách mà nhà phát triển có thể sử dụng WebTransport.

  • Gửi trạng thái trò chơi theo chu kỳ đều đặn với độ trễ tối thiểu đến máy chủ thông qua các thông báo nhỏ, không đáng tin cậy và không theo thứ tự.
  • Nhận luồng nội dung nghe nhìn được đẩy từ máy chủ với độ trễ tối thiểu, độc lập với các luồng dữ liệu khác.
  • Nhận thông báo được đẩy từ máy chủ trong khi trang web đang mở.

Chúng tôi muốn nghe thêm về cách bạn dự định sử dụng WebTransport.

Hỗ trợ trình duyệt

Browser Support

  • Chrome: 97.
  • Edge: 97.
  • Firefox: 114.
  • Safari: not supported.

Source

Giống như tất cả các tính năng không được trình duyệt phổ biến hỗ trợ, bạn nên lập trình phòng thủ thông qua tính năng phát hiện tính năng.

Trạng thái hiện tại

Bước Trạng thái
1. Tạo video giải thích Hoàn tất
2. Tạo bản nháp ban đầu của quy cách Hoàn tất
3. Thu thập ý kiến phản hồi và lặp lại thiết kế Hoàn chỉnh
4. Bản dùng thử theo nguyên gốc Hoàn chỉnh
5. Chạy Chromium 97

Mối quan hệ của WebTransport với các công nghệ khác

WebTransport có thay thế cho WebSocket không?

Có thể. Có những trường hợp sử dụng mà WebSockets hoặc WebTransport có thể là giao thức giao tiếp hợp lệ để sử dụng.

Giao tiếp WebSockets được mô hình hoá xung quanh một luồng tin nhắn đơn, đáng tin cậy và có thứ tự. Điều này phù hợp với một số loại nhu cầu giao tiếp. Nếu bạn cần những đặc điểm đó, thì các API luồng của WebTransport cũng có thể cung cấp những đặc điểm đó. So sánh với API datagram của WebTransport, API này cung cấp khả năng phân phối có độ trễ thấp mà không đảm bảo về độ tin cậy hoặc thứ tự, vì vậy, API này không thay thế trực tiếp cho WebSocket.

Việc sử dụng WebTransport, thông qua các API datagram hoặc thông qua nhiều phiên bản Streams API đồng thời, có nghĩa là bạn không phải lo lắng về việc chặn đầu hàng, đây có thể là vấn đề với WebSocket. Ngoài ra, bạn sẽ nhận được lợi ích về hiệu suất khi thiết lập các kết nối mới, vì giao thức bắt tay QUIC cơ bản nhanh hơn so với việc khởi động TCP qua TLS.

WebTransport là một phần của bản đặc tả nháp mới, do đó, hệ sinh thái WebSocket xung quanh các thư viện máy khách và máy chủ hiện mạnh mẽ hơn nhiều. Nếu bạn cần một giải pháp hoạt động ngay lập tức với các chế độ thiết lập máy chủ phổ biến và có khả năng hỗ trợ ứng dụng web rộng rãi, thì WebSockets là lựa chọn tốt hơn hiện nay.

WebTransport có giống với API ổ cắm UDP không?

Không. WebTransport không phải là API ổ cắm UDP. Mặc dù WebTransport sử dụng HTTP/3, nhưng lại sử dụng UDP "ở chế độ nền", WebTransport có các yêu cầu về mã hoá và kiểm soát tình trạng tắc nghẽn khiến nó trở thành một API ổ cắm UDP cơ bản.

WebTransport có phải là giải pháp thay thế cho các kênh dữ liệu WebRTC không?

Có, đối với các kết nối máy khách-máy chủ. WebTransport có nhiều thuộc tính giống với kênh dữ liệu WebRTC, mặc dù các giao thức cơ bản khác nhau.

Nhìn chung, việc chạy máy chủ tương thích với HTTP/3 đòi hỏi ít thiết lập và cấu hình hơn so với việc duy trì máy chủ WebRTC. Để có được một phương thức truyền tải hoạt động, bạn cần hiểu nhiều giao thức (ICE, DTLSSCTP). WebRTC đòi hỏi nhiều thành phần di chuyển hơn có thể dẫn đến việc đàm phán không thành công giữa máy khách/máy chủ.

API WebTransport được thiết kế cho các trường hợp sử dụng của nhà phát triển web và sẽ giống như việc viết mã nền tảng web hiện đại hơn là sử dụng giao diện kênh dữ liệu của WebRTC. Không giống như WebRTC, WebTransport được hỗ trợ bên trong Web Worker, cho phép bạn thực hiện giao tiếp máy khách-máy chủ độc lập với một trang HTML nhất định. Vì WebTransport hiển thị một giao diện tuân thủ Luồng, nên nó hỗ trợ các hoạt động tối ưu hoá xung quanh áp lực ngược.

Tuy nhiên, nếu bạn đã có một chế độ thiết lập máy chủ/ứng dụng WebRTC đang hoạt động mà bạn hài lòng, thì việc chuyển sang WebTransport có thể không mang lại nhiều lợi ích.

Dùng thử

Cách tốt nhất để thử nghiệm WebTransport là khởi động một máy chủ HTTP/3 tương thích. Sau đó, bạn có thể sử dụng trang này với một ứng dụng JavaScript cơ bản để thử nghiệm giao tiếp ứng dụng/máy chủ.

Ngoài ra, bạn có thể sử dụng máy chủ phản hồi do cộng đồng duy trì tại webtransport.day.

Sử dụng API

WebTransport được thiết kế dựa trên các nguyên hàm nền tảng web hiện đại, chẳng hạn như API Luồng. Phương thức này phụ thuộc nhiều vào lời hứa và hoạt động tốt với asyncawait.

Cách triển khai WebTransport hiện tại trong Chromium hỗ trợ 3 loại lưu lượng truy cập riêng biệt: gói dữ liệu, cũng như cả luồng một chiều và hai chiều.

Kết nối với máy chủ

Bạn có thể kết nối với máy chủ HTTP/3 bằng cách tạo một thực thể WebTransport. Giao thức của URL phải là https. Bạn cần chỉ định rõ ràng số cổng.

Bạn nên sử dụng lời hứa ready để chờ kết nối được thiết lập. Lời hứa này sẽ không được thực hiện cho đến khi quá trình thiết lập hoàn tất và sẽ từ chối nếu kết nối không thành công ở giai đoạn QUIC/TLS.

Lời hứa closed sẽ thực hiện khi kết nối đóng lại bình thường và từ chối nếu việc đóng lại là không mong muốn.

Nếu máy chủ từ chối kết nối do lỗi chỉ báo ứng dụng (ví dụ: đường dẫn của URL không hợp lệ), thì điều đó sẽ khiến closed từ chối, trong khi ready vẫn chưa được giải quyết.

const url = 'https://example.com:4999/foo/bar';
const transport = new WebTransport(url);

// Optionally, set up functions to respond to
// the connection closing:
transport.closed.then(() => {
  console.log(`The HTTP/3 connection to ${url} closed gracefully.`);
}).catch((error) => {
  console.error(`The HTTP/3 connection to ${url} closed due to ${error}.`);
});

// Once .ready fulfills, the connection can be used.
await transport.ready;

API Datagram

Sau khi có một thực thể WebTransport được kết nối với máy chủ, bạn có thể sử dụng thực thể đó để gửi và nhận các bit dữ liệu riêng biệt, được gọi là datagram.

Phương thức getter writeable trả về một WritableStream mà ứng dụng web có thể sử dụng để gửi dữ liệu đến máy chủ. Phương thức getter readable trả về một ReadableStream, cho phép bạn nghe dữ liệu từ máy chủ. Cả hai luồng đều vốn không đáng tin cậy, vì vậy, có thể máy chủ sẽ không nhận được dữ liệu mà bạn ghi và ngược lại.

Cả hai loại luồng đều sử dụng các thực thể Uint8Array để chuyển dữ liệu.

// Send two datagrams to the server.
const writer = transport.datagrams.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);

// Read datagrams from the server.
const reader = transport.datagrams.readable.getReader();
while (true) {
  const {value, done} = await reader.read();
  if (done) {
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

API luồng

Sau khi kết nối với máy chủ, bạn cũng có thể sử dụng WebTransport để gửi và nhận dữ liệu thông qua các API Luồng.

Mỗi phần của tất cả các luồng là một Uint8Array. Không giống như các API Datagram, các luồng này đáng tin cậy. Tuy nhiên, mỗi luồng đều độc lập, vì vậy, thứ tự dữ liệu trên các luồng không được đảm bảo.

WebTransportSendStream

WebTransportSendStream được ứng dụng web tạo bằng phương thức createUnidirectionalStream() của thực thể WebTransport. Phương thức này sẽ trả về một lời hứa cho WebTransportSendStream.

Sử dụng phương thức close() của WritableStreamDefaultWriter để đóng luồng HTTP/3 được liên kết. Trình duyệt sẽ cố gắng gửi tất cả dữ liệu đang chờ xử lý trước khi thực sự đóng luồng liên kết.

// Send two Uint8Arrays to the server.
const stream = await transport.createUnidirectionalStream();
const writer = stream.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);
try {
  await writer.close();
  console.log('All data has been sent.');
} catch (error) {
  console.error(`An error occurred: ${error}`);
}

Tương tự, hãy sử dụng phương thức abort() của WritableStreamDefaultWriter để gửi RESET_STREAM đến máy chủ. Khi sử dụng abort(), trình duyệt có thể loại bỏ mọi dữ liệu đang chờ xử lý chưa được gửi.

const ws = await transport.createUnidirectionalStream();
const writer = ws.getWriter();
writer.write(...);
writer.write(...);
await writer.abort();
// Not all the data may have been written.

WebTransportReceiveStream

WebTransportReceiveStream do máy chủ khởi tạo. Việc lấy WebTransportReceiveStream là một quy trình gồm hai bước cho ứng dụng web. Trước tiên, hàm này gọi thuộc tính incomingUnidirectionalStreams của một thực thể WebTransport, hàm này sẽ trả về một ReadableStream. Mỗi phần của ReadableStream đó, lần lượt là một WebTransportReceiveStream có thể dùng để đọc các thực thể Uint8Array do máy chủ gửi.

async function readFrom(receiveStream) {
  const reader = receiveStream.readable.getReader();
  while (true) {
    const {done, value} = await reader.read();
    if (done) {
      break;
    }
    // value is a Uint8Array
    console.log(value);
  }
}

const rs = transport.incomingUnidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is an instance of WebTransportReceiveStream
  await readFrom(value);
}

Bạn có thể phát hiện việc đóng luồng bằng cách sử dụng lời hứa closed của ReadableStreamDefaultReader. Khi luồng HTTP/3 cơ bản được đóng bằng bit FIN, lời hứa closed sẽ được thực hiện sau khi đọc tất cả dữ liệu. Khi luồng HTTP/3 bị đóng đột ngột (ví dụ: do RESET_STREAM), thì lời hứa closed sẽ từ chối.

// Assume an active receiveStream
const reader = receiveStream.readable.getReader();
reader.closed.then(() => {
  console.log('The receiveStream closed gracefully.');
}).catch(() => {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

WebTransportBidirectionalStream có thể do máy chủ hoặc ứng dụng khách tạo.

Ứng dụng web có thể tạo một ứng dụng bằng cách sử dụng phương thức createBidirectionalStream() của một thực thể WebTransport. Phương thức này sẽ trả về một lời hứa cho WebTransportBidirectionalStream.

const stream = await transport.createBidirectionalStream();
// stream is a WebTransportBidirectionalStream
// stream.readable is a ReadableStream
// stream.writable is a WritableStream

Bạn có thể theo dõi WebTransportBidirectionalStream do máy chủ tạo bằng thuộc tính incomingBidirectionalStreams của thực thể WebTransport. Thuộc tính này sẽ trả về ReadableStream. Mỗi phần của ReadableStream đó, lần lượt là một WebTransportBidirectionalStream.

const rs = transport.incomingBidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is a WebTransportBidirectionalStream
  // value.readable is a ReadableStream
  // value.writable is a WritableStream
}

WebTransportBidirectionalStream chỉ là sự kết hợp của WebTransportSendStreamWebTransportReceiveStream. Các ví dụ trong hai phần trước giải thích cách sử dụng từng phương thức.

Ví dụ khác

Bản đặc tả dự thảo WebTransport bao gồm một số ví dụ nội tuyến bổ sung, cùng với tài liệu đầy đủ cho tất cả các phương thức và thuộc tính.

WebTransport trong Công cụ của Chrome cho nhà phát triển

Rất tiếc, Công cụ của Chrome cho nhà phát triển hiện không hỗ trợ WebTransport. Bạn có thể "đánh dấu" vấn đề này trên Chrome để được thông báo về các bản cập nhật trên giao diện DevTools.

Polyfill

Có một polyfill (hay còn gọi là ponyfill cung cấp chức năng dưới dạng mô-đun độc lập mà bạn có thể sử dụng) có tên là webtransport-ponyfill-websocket triển khai một số tính năng của WebTransport. Hãy đọc kỹ các quy tắc ràng buộc trong README của dự án để xác định xem giải pháp này có phù hợp với trường hợp sử dụng của bạn hay không.

Những điều cần cân nhắc về quyền riêng tư và bảo mật

Hãy xem phần tương ứng của bản dự thảo quy cách để biết hướng dẫn chính thức.

Phản hồi

Nhóm Chrome muốn biết ý kiến và trải nghiệm của bạn khi sử dụng API này.

Ý kiến phản hồi về thiết kế API

API có gì khó hiểu hoặc không hoạt động như mong đợi không? Hay bạn còn thiếu những phần cần thiết để triển khai ý tưởng của mình?

Gửi vấn đề trên kho lưu trữ GitHub của Web Transport hoặc thêm ý kiến của bạn vào một vấn đề hiện có.

Bạn gặp vấn đề khi triển khai?

Bạn có phát hiện lỗi khi triển khai Chrome không?

Gửi lỗi tại https://new.crbug.com. Hãy cung cấp càng nhiều thông tin chi tiết càng tốt, cùng với hướng dẫn đơn giản để tái tạo lỗi.

Bạn có dự định sử dụng API này không?

Sự ủng hộ công khai của bạn giúp Chrome ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác thấy tầm quan trọng của việc hỗ trợ các tính năng đó.

  • Gửi tweet đến @ChromiumDev bằng hashtag #WebTransport và thông tin chi tiết về nơi và cách bạn sử dụng ứng dụng này.

Thảo luận chung

Bạn có thể sử dụng Nhóm Google web-transport-dev để đặt câu hỏi chung hoặc giải quyết các vấn đề không thuộc một trong các danh mục khác.

Lời cảm ơn

Bài viết này kết hợp thông tin từ WebTransport Explainer (Tài liệu giải thích về WebTransport), bản dự thảo quy cáchcác tài liệu thiết kế liên quan. Cảm ơn các tác giả tương ứng đã cung cấp nền tảng đó.

Hình ảnh chính trên bài đăng này là của Robin Pierre trên Unsplash.