Hiểu biết của tôi về HTTP Polling, Long Polling, HTTP Streaming và WebSockets


123

Tôi đã đọc nhiều bài đăng trên SO và web liên quan đến các từ khóa trong tiêu đề câu hỏi của tôi và học được rất nhiều điều từ chúng. Một số câu hỏi tôi đọc được liên quan đến những thách thức thực hiện cụ thể trong khi những câu hỏi khác tập trung vào các khái niệm chung. Tôi chỉ muốn đảm bảo rằng tôi đã hiểu tất cả các khái niệm và lý do tại sao công nghệ X được phát minh hơn công nghệ Y, v.v. Vì vậy, đây là:

Http Polling: Về cơ bản là AJAX, sử dụng XmlHttpRequest.

Http Long Polling: AJAX nhưng máy chủ giữ phản hồi trừ khi máy chủ có bản cập nhật, ngay sau khi máy chủ có bản cập nhật, nó sẽ gửi nó và sau đó máy khách có thể gửi yêu cầu khác. Bất lợi là dữ liệu tiêu đề bổ sung cần phải được gửi qua lại gây thêm chi phí.

Http Streaming: Tương tự như bỏ phiếu dài nhưng máy chủ phản hồi bằng tiêu đề có "Mã hóa truyền: phân đoạn" và do đó chúng tôi không cần phải bắt đầu một yêu cầu mới mỗi khi máy chủ gửi một số dữ liệu (và do đó lưu thêm chi phí tiêu đề). Hạn chế ở đây là chúng ta phải "hiểu" và tìm ra cấu trúc của dữ liệu để phân biệt giữa nhiều khối được gửi bởi máy chủ.

Java Applet, Flash, Silverlight: Chúng cung cấp khả năng kết nối với máy chủ socket qua tcp / ip nhưng vì chúng là plugin nên các nhà phát triển không muốn phụ thuộc vào chúng.

WebSockets: chúng là API mới cố gắng giải quyết các kết quả ngắn của các phương thức ở trên theo cách sau:

  • Ưu điểm duy nhất của WebSockets so với các plugin như Java Applets, Flash hoặc Silverlight là WebSockets được tích hợp sẵn trong trình duyệt và không dựa vào plugin.
  • Ưu điểm duy nhất của WebSockets so với truyền trực tuyến http là bạn không phải nỗ lực để "hiểu" và phân tích cú pháp dữ liệu nhận được.
  • Ưu điểm duy nhất của WebSockets so với Long Polling là loại bỏ kích thước tiêu đề phụ và đóng mở kết nối socket theo yêu cầu.

Có sự khác biệt đáng kể nào khác mà tôi đang thiếu không? Tôi xin lỗi nếu tôi đang hỏi lại hoặc kết hợp nhiều câu hỏi đã có trên SO thành một câu hỏi duy nhất, nhưng tôi chỉ muốn hiểu rõ hơn về tất cả thông tin có trên SO và web liên quan đến những khái niệm này.

Cảm ơn!


4
Sự kiện do máy chủ gửi cũng có thể đáng xem khi bạn không cần giao tiếp hai chiều.
leggetter

1
Đây là một câu hỏi thực sự hữu ích. Tôi nghĩ rằng nó sẽ hữu ích hơn nếu có một câu trả lời mà nhiều tác giả có thể đóng góp.
leggetter 24/09/12

@leggetter Cảm ơn Phil, cảm ơn mẹo liên quan đến các sự kiện do máy chủ gửi. Tôi muốn tìm hiểu về các kịch bản giao tiếp hai chiều. cảm ơn.
Software Guy

1
Với HTTP Streaming và Long-Polling, bạn cần kết nối thứ hai để giao tiếp hai chiều. Một kết nối tồn tại lâu hơn cho máy chủ -> giao tiếp 'đẩy' máy khách và một kết nối ngắn thứ hai cho máy khách -> máy chủ comms. Kết nối thứ hai này được sử dụng để thực hiện những việc như thiết lập và thay đổi đăng ký thành dữ liệu. Vì vậy, EventSource có thể được sử dụng trong một giải pháp hai chiều và trên thực tế, nó thực sự là một giải pháp tiêu chuẩn hóa được sinh ra từ HTTP Streaming và Long-Polling.
leggetter 24/09/12

1
Bạn cũng có thể muốn xem phân loại kỹ thuật này mà tôi đã viết: stackoverflow.com/questions/12078550/…
Alessandro Alinone

Câu trả lời:


92

Có nhiều điểm khác biệt hơn những điểm bạn đã xác định.

Song công / định hướng:

  • Đơn hướng: thăm dò HTTP, thăm dò dài, phát trực tuyến.
  • Bi-direcitonal: WebSockets, mạng plugin

Để tăng độ trễ (gần đúng):

  • WebSockets
  • Mạng plugin
  • Truyền trực tuyến HTTP
  • Thăm dò ý kiến ​​dài HTTP
  • Thăm dò ý kiến ​​HTTP

CORS (hỗ trợ nguồn gốc chéo):

  • WebSockets: có
  • Kết nối mạng plugin: Flash qua yêu cầu chính sách (không chắc chắn về những thứ khác)
  • HTTP * (một số hỗ trợ gần đây)

Dữ liệu nhị phân gốc (mảng đã nhập, đốm màu):

  • WebSockets: có
  • Kết nối mạng plugin: không với Flash (yêu cầu mã hóa URL trên Giao diện bên ngoài)
  • HTTP *: đề xuất gần đây để bật hỗ trợ kiểu nhị phân

Băng thông giảm hiệu quả:

  • Kết nối mạng plugin: Ổ cắm flash còn nguyên ngoại trừ yêu cầu chính sách ban đầu
  • WebSockets: bắt tay thiết lập kết nối và một vài byte cho mỗi khung hình
  • Truyền trực tuyến HTTP (sử dụng lại kết nối máy chủ)
  • Thăm dò dài HTTP: kết nối cho mọi thư
  • Thăm dò ý kiến ​​HTTP: kết nối cho mọi tin nhắn + không có tin nhắn dữ liệu

Hỗ trợ thiết bị di động:

  • WebSocket: iOS 4.2 trở lên. Một số Android thông qua mô phỏng Flash hoặc sử dụng Firefox cho Android hoặc Google Chrome cho Android , cả hai đều cung cấp hỗ trợ WebSocket gốc.
  • Kết nối mạng plugin: một số Android. Không có trên iOS
  • HTTP *: chủ yếu là có

Mức độ phức tạp khi sử dụng Javascript (từ đơn giản đến phức tạp nhất). Phải thừa nhận rằng các biện pháp phức tạp là hơi chủ quan.

  • WebSockets
  • Thăm dò ý kiến ​​HTTP
  • Mạng plugin
  • Cuộc thăm dò dài HTTP, phát trực tuyến

Cũng lưu ý rằng có một đề xuất W3C để chuẩn hóa luồng HTTP được gọi là Sự kiện do máy chủ gửi . Nó hiện còn khá sớm trong quá trình phát triển và được thiết kế để cung cấp một API Javascript tiêu chuẩn với sự đơn giản tương đương với WebSockets.


1
Cảm ơn rất nhiều vì Kanaka đã trả lời tốt đẹp. Bạn có thể vui lòng cho tôi biết tại sao / cách phát trực tuyến http có độ trễ cao hơn so với websockets không? có thể với một ví dụ đơn giản? cảm ơn rất nhiều.
Software Guy

2
@SoftwareGuy. Nhiều lý do. Trên các trình duyệt gần đây, bạn có thể sử dụng trình xử lý sự kiện đầu vào XMLHTTPRequest để được thông báo về dữ liệu. Nhưng thông số kỹ thuật cho biết 50ms là khoảng thời gian thông báo nhỏ nhất. Nếu không, bạn phải thăm dò dữ liệu phản hồi. Ngoài ra, ứng dụng khách sẽ thiết lập một kết nối HTTP mới và do đó làm tăng đáng kể độ trễ cho cả chuyến đi. Ngoài ra, nhiều máy chủ web cắt kết nối HTTP sau 30 giây hoặc lâu hơn có nghĩa là bạn thường phải thiết lập lại kết nối đẩy của máy chủ. Tôi đã thấy độ trễ khứ hồi của WebSocket 5-10ms trên mạng cục bộ. Độ trễ phát trực tuyến HTTP có thể sẽ là 50ms +.
kanaka 24/09/12

Cảm ơn rất nhiều vì đã trả lời chi tiết :)
Software Guy

1
@leggetter Cảm ơn Phil, ý bạn là gửi dữ liệu từ máy khách đến máy chủ thông qua truyền trực tuyến http sẽ gây ra chi phí? có thể gửi dữ liệu tới máy chủ qua truyền trực tuyến http mà không cần mở kết nối mới không? Cảm ơn.
Software Guy

1
@Nathan nghe có vẻ là một dự án luận văn thạc sĩ hay! Chắc chắn, việc thăm dò ý kiến ​​sẽ giữ cho hệ thống bận rộn hơn so với mô hình điều khiển sự kiện, nhưng mức tiết kiệm điện năng chính xác có thể là bao nhiêu sẽ cần thử nghiệm thực nghiệm khá rộng rãi ở các quy mô khác nhau.
kanaka

13

Một số câu trả lời tuyệt vời từ những người khác bao gồm rất nhiều cơ sở. Đây là một chút bổ sung.

Ưu điểm duy nhất của WebSockets so với các plugin như Java Applets, Flash hoặc Silverlight là WebSockets được tích hợp sẵn trong trình duyệt và không dựa vào plugin.

Nếu điều này có nghĩa là bạn có thể sử dụng Java Applet, Flash hoặc Silverlight để thiết lập kết nối socket thì có, điều đó là có thể. Tuy nhiên, bạn không thấy điều đó được triển khai trong thế giới thực quá thường xuyên vì những hạn chế.

Ví dụ, người trung gian có thể và thực hiện việc tắt lưu lượng truy cập đó. Tiêu chuẩn WebSocket được thiết kế để tương thích với cơ sở hạ tầng HTTP hiện có và do đó ít bị can thiệp bởi các trung gian như tường lửa và proxy.

Hơn nữa, WebSocket có thể sử dụng cổng 80 và 443 mà không yêu cầu cổng chuyên dụng, một lần nữa nhờ thiết kế giao thức tương thích nhất có thể với cơ sở hạ tầng HTTP hiện có.

Các lựa chọn thay thế socket đó (Java, Flash và Silverlight) khó sử dụng một cách an toàn trong kiến ​​trúc nguồn gốc chéo. Vì vậy, mọi người thường cố gắng sử dụng chúng có nguồn gốc chéo sẽ chịu đựng được những bất an hơn là cố gắng thực hiện nó một cách an toàn.

Chúng cũng có thể yêu cầu mở thêm các cổng "không chuẩn" (điều mà quản trị viên không thích làm) hoặc các tệp chính sách cần được quản lý.

Nói tóm lại, việc sử dụng Java, Flash hoặc Silverlight cho kết nối socket có vấn đề đến mức bạn không thấy nó được triển khai trong các kiến ​​trúc nghiêm túc quá thường xuyên. Flash và Java đã có khả năng này trong ít nhất 10 năm, nhưng nó vẫn chưa phổ biến.

Tiêu chuẩn WebSocket đã có thể bắt đầu với một cách tiếp cận mới, lưu ý đến những hạn chế đó và hy vọng đã học được một số bài học từ chúng.

Một số triển khai WebSocket sử dụng Flash (hoặc có thể là Silverlight và / hoặc Java) làm dự phòng khi không thể thiết lập kết nối WebSocket (chẳng hạn như khi chạy trong trình duyệt cũ hoặc khi một bên trung gian can thiệp).

Mặc dù một số loại chiến lược dự phòng cho những tình huống đó là thông minh, thậm chí cần thiết, nhưng hầu hết những chiến lược sử dụng Flash et al sẽ mắc phải những nhược điểm được mô tả ở trên. Không nhất thiết phải như vậy - có những cách giải quyết để đạt được các kết nối có khả năng đa nguồn an toàn bằng cách sử dụng Flash, Silverlight, v.v. - nhưng hầu hết các triển khai sẽ không làm được điều đó vì nó không dễ dàng.

Ví dụ: nếu bạn dựa vào WebSocket cho kết nối nhiều nguồn gốc, điều đó sẽ hoạt động tốt. Nhưng nếu sau đó bạn chạy trong một trình duyệt cũ hoặc tường lửa / proxy bị can thiệp và dựa vào Flash, chẳng hạn như dự phòng của bạn, bạn sẽ thấy khó thực hiện cùng một kết nối nguồn gốc chéo đó. Tất nhiên, trừ khi bạn không quan tâm đến bảo mật.

Điều đó có nghĩa là rất khó để có một kiến ​​trúc thống nhất duy nhất hoạt động cho các kết nối gốc và không phải bản địa, trừ khi bạn đã chuẩn bị sẵn sàng để thực hiện khá nhiều công việc hoặc đi với một khuôn khổ đã hoạt động tốt. Trong một kiến ​​trúc lý tưởng, bạn sẽ không nhận thấy các kết nối có phải là bản địa hay không; cài đặt bảo mật của bạn sẽ hoạt động trong cả hai trường hợp; cài đặt phân cụm của bạn sẽ vẫn hoạt động; kế hoạch năng lực của bạn sẽ vẫn được giữ; và như thế.

Ưu điểm duy nhất của WebSockets so với truyền trực tuyến http là bạn không phải nỗ lực để "hiểu" và phân tích cú pháp dữ liệu nhận được.

Nó không đơn giản như mở một luồng HTTP và ngồi lại khi dữ liệu của bạn chảy trong vài phút, hàng giờ hoặc lâu hơn. Các khách hàng khác nhau hành xử khác nhau và bạn phải quản lý điều đó. Ví dụ: một số máy khách sẽ đệm dữ liệu và không phát hành nó vào ứng dụng cho đến khi đạt đến một số ngưỡng. Thậm chí tệ hơn, một số sẽ không chuyển dữ liệu vào ứng dụng cho đến khi kết nối bị đóng.

Vì vậy, nếu bạn đang gửi nhiều thư cho ứng dụng khách, có thể ứng dụng khách sẽ không nhận được dữ liệu cho đến khi nhận được 50 thư có giá trị dữ liệu. Đó không phải là thời gian thực.

Mặc dù phát trực tuyến HTTP có thể là một giải pháp thay thế khả thi khi không có WebSocket, nhưng nó không phải là thuốc chữa bách bệnh. Nó cần có sự hiểu biết tốt để hoạt động một cách hiệu quả trong vùng đất xấu của Web trong điều kiện thực tế.

Có sự khác biệt đáng kể nào khác mà tôi đang thiếu không?

Còn một điều nữa mà chưa ai nói đến nên mình sẽ đưa ra.

Giao thức WebSocket được thiết kế để trở thành một lớp truyền tải cho các giao thức cấp cao hơn. Mặc dù bạn có thể gửi tin nhắn JSON hoặc những gì không trực tiếp qua kết nối WebSocket, nó cũng có thể mang các giao thức tiêu chuẩn hoặc tùy chỉnh.

Ví dụ: bạn có thể thực hiện AMQP hoặc XMPP qua WebSocket, như mọi người đã làm. Vì vậy, một khách hàng có thể nhận được tin nhắn từ một nhà môi giới AMQP như thể nó được kết nối trực tiếp với chính nhà môi giới đó (và trong một số trường hợp là như vậy).

Hoặc nếu bạn có một máy chủ hiện có với một số giao thức tùy chỉnh, bạn có thể vận chuyển nó qua WebSocket, do đó mở rộng máy chủ back-end đó lên Web. Thông thường, một ứng dụng hiện có đã bị khóa trong doanh nghiệp có thể mở rộng phạm vi tiếp cận của nó bằng cách sử dụng WebSocket mà không cần phải thay đổi bất kỳ cơ sở hạ tầng back-end nào.

(Đương nhiên, bạn muốn có thể thực hiện tất cả những điều đó một cách an toàn, vì vậy hãy kiểm tra với nhà cung cấp hoặc nhà cung cấp WebSocket.)

Một số người đã gọi WebSocket là TCP cho Web. Bởi vì giống như TCP vận chuyển các giao thức cấp cao hơn, WebSocket cũng vậy, nhưng theo cách tương thích với cơ sở hạ tầng Web.

Vì vậy, mặc dù luôn có thể gửi các thông báo JSON (hoặc bất kỳ thứ gì) trực tiếp qua WebSocket, nhưng người ta cũng nên xem xét các giao thức hiện có. Bởi vì đối với nhiều thứ bạn muốn làm, có thể có một giao thức đã được nghĩ ra để làm điều đó.

Tôi xin lỗi nếu tôi đang hỏi lại hoặc kết hợp nhiều câu hỏi đã có trên SO thành một câu hỏi duy nhất, nhưng tôi chỉ muốn hiểu rõ hơn về tất cả thông tin có trên SO và web liên quan đến những khái niệm này.

Đây là một câu hỏi tuyệt vời và tất cả các câu trả lời đều rất giàu thông tin!


Cảm ơn Robin rất nhiều vì sự giúp đỡ và thông tin tuyệt vời. Nếu tôi có thể hỏi thêm một điều: Tôi đã xem qua một bài báo ở đâu đó nói rằng truyền trực tuyến http cũng có thể được lưu trữ bởi proxy trong khi websockets thì không. điều đó nghĩa là gì?
chàng phần mềm

Bởi vì StackOverflow giới hạn kích thước trong ý kiến phản ứng, tôi đã cho câu trả lời của tôi dưới đây: stackoverflow.com/questions/12555043/...
Robin Zimmermann

@RobinZimmermann, câu trả lời của bạn là câu trả lời yêu thích của tôi. +1 cho câu trả lời chi tiết thực sự tốt.
securecurve

10

Nếu tôi có thể hỏi thêm một điều: Tôi đã xem qua một bài báo ở đâu đó nói rằng truyền trực tuyến http cũng có thể được lưu trữ bởi proxy trong khi websockets thì không. điều đó nghĩa là gì?

(StackOverflow giới hạn kích thước của phản hồi nhận xét, vì vậy tôi phải trả lời ở đây thay vì nội dòng.)

Đó là một điểm hay. Để hiểu điều này, hãy nghĩ về một kịch bản HTTP truyền thống ... Hãy tưởng tượng một trình duyệt mở một trang web, vì vậy nó yêu cầu http://example.com , chẳng hạn. Máy chủ phản hồi bằng HTTP có chứa HTML cho trang. Sau đó, trình duyệt thấy rằng có các tài nguyên trong trang, vì vậy nó bắt đầu yêu cầu các tệp CSS, tệp JavaScript và hình ảnh tất nhiên. Chúng là tất cả các tệp tĩnh sẽ giống nhau cho tất cả các khách hàng yêu cầu chúng.

Một số proxy sẽ lưu vào bộ nhớ cache các tài nguyên tĩnh để các yêu cầu tiếp theo từ các máy khách khác có thể lấy các tài nguyên tĩnh đó từ proxy, thay vì phải quay lại máy chủ web trung tâm để lấy chúng. Đây là bộ nhớ đệm và đó là một chiến lược tuyệt vời để giảm tải các yêu cầu và xử lý từ các dịch vụ trung tâm của bạn.

Vì vậy, khách hàng số 1 yêu cầu http://example.com/images/logo.gif , chẳng hạn. Yêu cầu đó đi qua proxy đến máy chủ web trung tâm, nơi phục vụ logo.gif. Khi logo.gif đi qua proxy, proxy sẽ lưu hình ảnh đó và liên kết với địa chỉ http://example.com/images/logo.gif .

Khi khách hàng số 2 xuất hiện và cũng yêu cầu http://example.com/images/logo.gif , proxy có thể trả lại hình ảnh và không cần giao tiếp trở lại máy chủ web ở trung tâm. Điều này mang lại phản hồi nhanh hơn cho người dùng cuối, điều này luôn tuyệt vời, nhưng nó cũng có nghĩa là có ít tải hơn ở trung tâm. Điều đó có thể dẫn đến giảm chi phí phần cứng, giảm chi phí mạng, v.v. Vì vậy, đó là một điều tốt.

Sự cố phát sinh khi logo.gif được cập nhật trên máy chủ web. Proxy sẽ tiếp tục cung cấp hình ảnh cũ mà không biết rằng có hình ảnh mới. Điều này dẫn đến toàn bộ vấn đề xung quanh việc hết hạn, do đó proxy sẽ chỉ lưu vào bộ nhớ cache của hình ảnh trong một thời gian ngắn trước khi nó "hết hạn" và yêu cầu tiếp theo sẽ chuyển qua proxy tới máy chủ web, sau đó làm mới bộ nhớ cache của proxy. Ngoài ra còn có các giải pháp nâng cao hơn trong đó máy chủ trung tâm có thể đẩy ra các bộ nhớ đệm đã biết, v.v. và mọi thứ có thể trở nên khá phức tạp.

Làm thế nào để điều này liên quan đến câu hỏi của bạn?

Bạn đã hỏi về truyền trực tuyến HTTP trong đó máy chủ đang truyền trực tuyến HTTP tới máy khách. Nhưng HTTP truyền trực tuyến cũng giống như HTTP thông thường ngoại trừ việc bạn không ngừng gửi dữ liệu. Nếu máy chủ web phục vụ một hình ảnh, nó sẽ gửi HTTP đến máy khách mà cuối cùng kết thúc: bạn đã gửi toàn bộ hình ảnh. Và nếu bạn muốn gửi dữ liệu, nó hoàn toàn giống nhau, nhưng máy chủ chỉ gửi trong một thời gian rất dài (chẳng hạn như đó là một hình ảnh khổng lồ chẳng hạn) hoặc thậm chí không bao giờ kết thúc.

Theo quan điểm của proxy, nó không thể phân biệt giữa HTTP cho tài nguyên tĩnh như hình ảnh hay dữ liệu từ luồng HTTP. Trong cả hai trường hợp đó, máy khách đã đưa ra yêu cầu của máy chủ. Người được ủy quyền ghi nhớ yêu cầu đó và cả phản hồi. Lần tiếp theo khi yêu cầu đó xuất hiện, proxy sẽ cung cấp phản hồi tương tự.

Vì vậy, nếu khách hàng của bạn đưa ra yêu cầu về giá cổ phiếu, giả sử và nhận được phản hồi, thì khách hàng tiếp theo có thể đưa ra yêu cầu tương tự và nhận dữ liệu được lưu trong bộ nhớ cache. Có lẽ không phải những gì bạn muốn! Nếu bạn yêu cầu giá cổ phiếu, bạn muốn có dữ liệu mới nhất, phải không?

Vì vậy, đó là một vấn đề.

Có những thủ thuật và cách giải quyết để xử lý các vấn đề như vậy, đó là sự thật. Rõ ràng là bạn có thể làm cho phát trực tuyến HTTP hoạt động vì nó đang được sử dụng ngày nay. Tất cả đều minh bạch đối với người dùng cuối, nhưng những người phát triển và duy trì những kiến ​​trúc đó phải vượt qua vòng lặp và phải trả giá. Nó dẫn đến các kiến ​​trúc quá phức tạp, có nghĩa là phải bảo trì nhiều hơn, nhiều phần cứng hơn, phức tạp hơn, tốn kém hơn. Điều đó cũng có nghĩa là các nhà phát triển thường phải quan tâm đến những thứ mà họ không cần phải làm khi họ chỉ nên tập trung vào ứng dụng, GUI và logic kinh doanh - họ không cần phải quan tâm đến giao tiếp cơ bản.


1
chi tiết tuyệt vời Robin, cảm ơn rất nhiều! tôi thực sự đánh giá cao phản hồi kỹ lưỡng của bạn. Tôi đã học được rất nhiều điều từ tất cả những người tuyệt vời ở đây! :)
Software Guy

4

HTTP giới hạn số lượng kết nối mà máy khách có thể có với máy chủ ở mức 2 (mặc dù điều này có thể được giảm thiểu bằng cách sử dụng tên miền phụ) và IE đã được biết là thực thi điều này một cách hăng hái. Firefox và Chrome cho phép nhiều hơn (mặc dù tôi không thể nhớ chính xác trên đầu mình có bao nhiêu). Điều này có vẻ không phải là một vấn đề lớn nhưng nếu bạn đang sử dụng 1 kết nối liên tục để cập nhật thời gian thực, thì tất cả các yêu cầu khác đều phải tắc nghẽn thông qua kết nối HTTP khác. Và có một vấn đề là có nhiều kết nối mở hơn từ các máy khách sẽ tạo ra nhiều tải hơn cho máy chủ.

WebSockets là một giao thức dựa trên TCP và do đó không bị giới hạn kết nối cấp HTTP này (nhưng tất nhiên, hỗ trợ trình duyệt không đồng nhất).


cảm ơn các lợi ích, vì vậy ngoài vấn đề của nhiều kết nối đồng thời như bạn đã nhấn mạnh, phần còn lại của các giả định về websockets của tôi có đúng không?
Software Guy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.