Cách sử dụng WebTransport

Ngày xuất bản: 8 tháng 6 năm 2020

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. Đây là giao thức dành cho hoạt động giao tiếp hai chiều giữa một ứng dụng web và máy chủ HTTP/3. Nền tảng này hỗ trợ việc gửi dữ liệu không đáng tin cậy bằng API gói dữ liệu và đáng tin cậy bằng API luồng.

Datagram là lựa chọn lý tưởng để gửi và nhận dữ liệu mà không cần đảm bảo việc gửi. 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, có thể truyền thành công hoặc không, và nếu được truyền, chúng có thể đến theo thứ tự bất kỳ. 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 và nỗ lực tối đa. Bạn có thể coi các gói dữ liệu là thông báo 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 hoạt động truyền dữ liệu đáng tin cậy và có thứ tự. Chúng rất phù hợp với những 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 có trọng lượng nhẹ hơn, nên các kết nối này có thể được mở và đóng mà không tốn nhiều tài nguyên.

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 một khoảng thời gian đều đặn với độ trễ tối thiểu đến một máy chủ trong 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 truyền từ mộ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 gửi từ một máy chủ trong khi trang web đang mở.

Chúng tôi rất mong được biết 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: 26.4.

Source

Tương tự như tất cả các tính năng thiếu sự hỗ trợ của trình duyệt phổ biến, bạn nên thêm tính năng phát hiện.

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

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

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

Giao tiếp WebSockets được mô hình hoá dựa trên một luồng thông báo duy nhất, đáng tin cậy và có thứ tự, 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 chúng. Ngược lại, các API datagram của WebTransport 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, các API này không phải là giải pháp thay thế trực tiếp cho WebSocket.

Khi sử dụng WebTransport, với các API datagram hoặc nhiều phiên bản API Streams đồng thời, bạn không phải lo lắng về tình trạng chặn đầu dòng. Đây có thể là một vấn đề với WebSocket. Ngoài ra, việc thiết lập các kết nối mới sẽ mang lại lợi ích về hiệu suất, vì cái 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ủ sẽ mạnh mẽ hơn nhiều. Nếu bạn cần một thứ hoạt động "ngay lập tức" với các chế độ thiết lập máy chủ thông thường và có khả năng hỗ trợ rộng rãi cho máy khách web, thì WebSockets là lựa chọn phù hợp hơn hiện nay.

WebTransport có giống với UDP Socket API không?

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

WebTransport có phải là một 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 như các kênh dữ liệu WebRTC, mặc dù các giao thức cơ bản là khác nhau.

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

WebTransport API được thiết kế dựa trên các trường hợp sử dụng của nhà phát triển web và có vẻ giống với việc viết mã nền tảng web hiện đại hơn là sử dụng các giao diện kênh dữ liệu của WebRTC. Không giống như WebRTC, WebTransport được hỗ trợ trong Web Workers, cho phép bạn thực hiện giao tiếp giữa máy khách và 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ủ Streams, nên giao diện này hỗ trợ các hoạt động tối ưu hoá xung quanh áp suất ngược.

Tuy nhiên, nếu bạn đã có một chế độ thiết lập máy khách/máy chủ 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.

Thử nghiệm

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. Sử dụng trang này với một ứng dụng JavaScript cơ bản để dùng thử hoạt động giao tiếp giữa máy khách và máy chủ.

Ngoài ra, bạn có thể truy cập vào một máy chủ lặp lạ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 tắc cơ bản của nền tảng web hiện đại, chẳng hạn như Streams API. Nó phụ thuộc nhiều vào promise và hoạt động tốt với asyncawait.

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: datagram, 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ột máy chủ HTTP/3 bằng cách tạo một phiên bản WebTransport. Lượ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ờ thiết lập kết nối. Lời hứa này vẫn chưa đượ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ệnh hứa closed sẽ thực hiện khi kết nối đóng bình thường và từ chối nếu việc đóng kết nối diễn ra bất ngờ.

Nếu máy chủ từ chối kết nối do lỗi chỉ báo của máy khách (chẳng hạn như đườ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;

Datagram API

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

Phương thức getter writeable trả về WritableStream mà ứng dụng web có thể 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ể dữ liệu bạn ghi sẽ không được máy chủ nhận và ngược lại.

Cả hai loại luồng này đề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);
}

Streams API

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

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

WebTransportSendStream

WebTransportSendStream được ứng dụng web tạo bằng phương thức createUnidirectionalStream() của một 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 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

Máy chủ khởi tạo WebTransportReceiveStream. Việc lấy WebTransportReceiveStream là quy trình gồm hai bước đối với một ứng dụng web. Trước tiên, ứng dụng gọi thuộc tính incomingUnidirectionalStreams của một thực thể WebTransport, thuộc tính này sẽ trả về một ReadableStream. Mỗi đoạn 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 trạng thái đó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 đó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ụ: bằng RESET_STREAM), thì lời hứa closed sẽ bị 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 tạo.

Ứng dụng web có thể tạo một ứng dụng bằng phương thức createBidirectionalStream() của một phiên bản WebTransport. Phương thức này 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 một WebTransportBidirectionalStream do máy chủ tạo bằng thuộc tính incomingBidirectionalStreams của một thực thể WebTransport. Thuộc tính này sẽ trả về một ReadableStream. Mỗi khối 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 giữ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.

Polyfill

Có một polyfill (hoặc đúng hơn là ponyfill cung cấp chức năng dưới dạng một mô-đun độc lập mà bạn có thể sử dụng) có tên là webtransport-ponyfill-websocket. Polyfill này triển khai một số tính năng của WebTransport. Hãy đọc kỹ cá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 quy cách nháp để biết hướng dẫn chính thức.

Phản hồi

Có điều gì bất tiện hoặc không hoạt động như mong đợi về API này không? Hoặc bạn có thiếu những yếu tố cần thiết để triển khai ý tưởng của mình 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 này.

  • Đăng tweet cho @ChromiumDev bằng thẻ bắt đầu bằng #WebTransport và thông tin chi tiết về nơi cũng như cách bạn sử dụng.

Thảo luận chung

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

Lời cảm ơn

Chúng tôi đã kết hợp thông tin từ Phần giải thích về WebTransportbản nháp đặc tả. Cảm ơn các tác giả tương ứng đã cung cấp nền tảng đó.