1. Trang chủ
  2. » Công Nghệ Thông Tin

Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO pdf

11 578 5
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 11
Dung lượng 145,34 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Trong bài này, hãy đi chi tiết về cách sử dụng Comet và WebSockets trong ứng dụng web của bạn với các API và các web container khác nhau Servlet 3.0 và Continuations của Jetty.. Hãy tìm

Trang 1

Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO

Giới thiệu

Người dùng hiện nay mong đợi các ứng dụng linh động, nhanh chóng có thể truy cập được từ trang web Loạt bài này cho bạn thấy cách phát triển các ứng dụng web theo hướng sự kiện bằng cách sử dụng các kỹ thuật Reverse Ajax Phần 1 đã giới thiệu Reverse Ajax, polling, streaming, Comet, long-polling Bạn đã tìm hiểu vì sao Comet sử dụng HTTP long-polling là cách tốt nhất

để thực hiện Reverse Ajax, khi hiện nay tất cả các trình duyệt đều hỗ trợ nó Phần 2 đã mô tả cách thực hiện Reverse Ajax bằng cách sử dụng WebSockets Các ví dụ mã giúp minh họa các ràng buộc WebSockets, FlashSocket bên phía máy chủ, các dịch vụ phạm vi-yêu cầu, và cách để tạm dừng các yêu cầu long-lived

Trong bài này, hãy đi chi tiết về cách sử dụng Comet và WebSockets trong ứng dụng web của bạn với các API và các web container khác nhau (Servlet 3.0 và Continuations của Jetty) Hãy tìm hiểu cách sử dụng Comet và WebSockets một cách trong suốt bằng cách sử dụng các thư viện trừu tượng hóa, chẳng hạn như Socket.IO Socket.IO sử dụng tính năng nhận dạng để quyết định xem kết nối đó sẽ được thiết lập với WebSocket, AJAX long-polling, Flash và v.v hay không

Bạn có thể tải về mã nguồn được sử dụng trong bài này

Điều kiện tiên quyết

Tốt nhất bạn nên biết trước về JavaScript và Java Các ví dụ trong bài này đã được xây dựng bằng cách sử dụng Google Guice, một framework tích hợp phụ thuộc (dependency injection framework) được viết bằng Java Để làm theo cùng với bài này, bạn nên hiểu rõ các khái niệm về dependency injection framework, chẳng hạn như Guice, Spring hoặc Pico

Để chạy được các ví dụ trong bài này, bạn cần có phiên bản mới nhất của Maven và JDK (xem phần Tài nguyên)

Về đầu trang

Các giải pháp máy chủ cho Comet và WebSocket

Ở Phần 1, bạn đã tìm hiểu về phương pháp Comet (long-polling hay streaming) có khả năng yêu cầu máy chủ có thể tạm dừng một request và tiếp tục hay hoàn thành nó sau một thời gian nắm giữ Phần 2 đã mô tả cách mà máy chủ cần sử dụng tính năng I/O non-blocking như thế nào để

xử lý nhiều kết nối và chúng chỉ sử dụng các luồng để phục vụ các yêu cầu (mô hình luồng cho mỗi yêu cầu) Bạn cũng đã biết rằng việc sử dụng WebSocket phụ thuộc vào máy chủ và không phải tất cả các máy chủ đều hỗ trợ WebSockets

Phần này sẽ cho bạn thấy cách sử dụng Comet và WebSockets, nếu có, trên các máy chủ web Jetty, Tomcat và Grizzly Mã nguồn được cung cấp cùng với bài này có một ứng dụng web chat

Trang 2

(trò chuyện trực tuyến) là ví dụ mẫu cho Jetty và Tomcat Phần này cũng thảo luận về các API

hỗ trợ cho các máy chủ ứng dụng sau: Jboss, Glassfish và WebSphere

Jetty

Jetty là một máy chủ web hỗ trợ đặc tả Java Servlet 3.0, WebSockets và nhiều đặc tả tích hợp khác Jetty là:

 Mạnh mẽ và linh hoạt

 Dễ dàng nhúng

 Hỗ trợ các máy chủ ảo, phân cụm session và rất nhiều tính năng có thể được cấu hình dễ dàng thông qua mã Java hoặc XML

 Được dùng cho dịch vụ lưu trữ của Google App Engine

Dự án Jetty được quản lý bởi Quỹ Eclipse

Kể từ phiên bản 6, Jetty bao gồm một API không đồng bộ được gọi là Jetty Continuations, cho phép một yêu cầu được tạm dừng và có thể tiếp tục lại sau đó Bảng 1 cho thấy bản đồ về đặc tả

và API hỗ trợ với các họ phiên bản Jetty chính

Bảng 1 Các phiên bản Jetty và khả năng hỗ trợ

Các sự hỗ trợ Jetty 6Jetty 7Jetty 8

Non-blocking I/O X X X

Jetty Continuations (Comet)X X X

Để thực hiện Reverse Ajax với Comet, bạn có thể sử dụng các API Continuation của Jetty, như thể hiện trong Liệt kê 1:

Liệt kê 1 API Continuation của Jetty cho Comet

// Pausing a request from a servlet's method (get, post, ):

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Continuation continuation = ContinuationSupport.getContinuation(req);

// optionally set a timeout to avoid suspending requests for too long

continuation.setTimeout(0);

// suspend the request

continuation.suspend();

// then hold a reference for future usage from another thread

continuations.offer(continuation);

Trang 3

}

// Then, from another thread which wants to send an event to the client: while (!continuations.isEmpty()) {

Continuation continuation = continuations.poll();

HttpServletResponse response =

(HttpServletResponse) continuation.getServletResponse();

// write to the response

continuation.complete();

}

Ứng dụng web hoàn chỉnh có trong mã nguồn đi kèm với bài này Jetty Continuations được đóng gói trong một tệp JAR Bạn phải đặt tệp JAR này trong thư mục WEB-INF/lib của ứng dụng web của mình để có thể sử dụng các tính năng Comet của Jetty Continuations của Jetty sẽ làm việc trên Jetty 6, 7 và 8

Bắt đầu với Jetty 7, bạn cũng có quyền truy cập vào tính năng WebSockets Đặt tệp JAR

WebSocket của Jetty vào thư mục WEB-INF/lib của ứng dụng web của bạn để có quyền truy cập vào API WebSocket của Jetty, như trong Liệt kê 2:

Liệt kê 2 API WebSocket của Jetty

// Implement the doWebSocketConnect and returns an implementation of

// WebSocket:

public final class ReverseAjaxServlet extends WebSocketServlet {

@Override

protected WebSocket doWebSocketConnect(HttpServletRequest request,

String protocol) {

return [ ]

}

}

// Sample implementation of WebSocket:

class Endpoint implements WebSocket {

Outbound outbound;

public void onConnect(Outbound outbound) {

this.outbound = outbound;

}

public void onMessage(byte opcode, String data) {

outbound.sendMessage("Echo: " + data);

if("close".equals(data))

outbound.disconnect();

}

public void onFragment(boolean more, byte opcode,

byte[] data, int offset, int length) {

}

public void onMessage(byte opcode, byte[] data,

int offset, int length) {

onMessage(opcode, new String(data, offset, length));

Trang 4

}

public void onDisconnect() {

outbound = null;

}

}

Trong thư mục jetty-websocket củamã nguồn có một ví dụ chat mẫu để trình bày cách sử dụng API WebSocket của Jetty

Tomcat

Có lẽ Tomcat là máy chủ web nổi tiếng nhất Nó đã được sử dụng trong nhiều năm và đã được tích hợp như là một web container vào các phiên bản đầu tiên của máy chủ ứng dụng Jboss Ngoài ra, Tomcat còn được dùng cho việc thực thi tham khảo đặc tả servlet Nó đã được loại bỏ khỏi phiên bản API servlet 2.5 khi mọi người bắt đầu xem xét các lựa chọn thay thế dựa trên I/O non-blocking (như Jetty) Bảng 2 cho thấy API và các đặc tả được hỗ trợ cho hai họ phiên bản Tomcat mới nhất

Bảng 2 Sự hỗ trợ của Tomcat

Các sự hỗ trợ Tomcat 6 Tomcat 7

Non-blocking I/O X X

Advanced I/O (Comet)X X

WebSockets

Như thể hiện trong Bảng 2, Tomcat không hỗ trợ WebSockets; nó có một bản tương đương với Continuations của Jetty được gọi là I/O nâng cao (Advanced I/O) để hỗ trợ Comet I/O nâng cao

là một trình bao bọc mức thấp xung quanh NIO mạnh hơn một API tốt để làm cho việc sử dụng Comet dễ dàng hơn Nó có ít tài liệu và chỉ có một vài ví dụ mô tả cách sử dụng API này Liệt kê

3 cho thấy một ví dụ về servlet được sử dụng để treo và tiếp tục lại các yêu cầu trong một ứng dụng web chat Bạn có thể tìm thấy ứng dụng web hoàn chỉnh trong mã nguồn đi kèm với bài này

Liệt kê 3 API của Tomcat cho Comet

public final class ChatServlet extends HttpServlet

implements CometProcessor {

private final BlockingQueue<CometEvent> events =

new LinkedBlockingQueue<CometEvent>();

public void event(CometEvent evt)

throws IOException, ServletException {

Trang 5

HttpServletRequest request = evt.getHttpServletRequest();

String user =

(String) request.getSession().getAttribute("user");

switch (evt.getEventType()) {

case BEGIN: {

if ("GET".equals(request.getMethod())) {

evt.setTimeout(Integer.MAX_VALUE);

events.offer(evt);

} else {

String message = request.getParameter("message");

if ("/disconnect".equals(message)) {

broadcast(user + " disconnected");

request.getSession().removeAttribute("user");

events.remove(evt);

} else if (message != null) {

broadcast("[" + user + "]" + message);

}

evt.close();

}

}

}

}

void broadcast(String message) throws IOException {

Queue<CometEvent> q = new LinkedList<CometEvent>();

events.drainTo(q);

while (!q.isEmpty()) {

CometEvent event = q.poll();

HttpServletResponse resp = event.getHttpServletResponse();

resp.setStatus(HttpServletResponse.SC_OK);

resp.setContentType("text/html");

resp.getWriter().write(message);

event.close();

}

}

}

Trong Tomcat, một servlet không đồng bộ phải thực hiện một CometProcessor Đối với các servlet không đồng bộ, Tomcat không gọi các phương thức HTTP tiêu chuẩn (doGet, doPost và v.v) Thay vào đó, nó gửi một sự kiện đến phương thức event(CometdEvent) Khi yêu cầu đầu tiên gửi đến, ví dụ sẽ kiểm tra để xem liệu nó có là một GET để treo nó không; evt.close()

không được gọi Nếu nó là một POST, có nghĩa là người dùng đã gửi một thông báo, do đó, nó được broadcast (phát hàng loạt) đến CometEvent khác và evt.close() được gọi để hoàn thành yêu cầu đăng thông báo Về phía máy khách, việc broadcast sẽ thực hiện tất cả các yêu cầu long-polling để hoàn thành thông báo đã gửi và một yêu cầu long-long-polling khác được gửi ngay để nhận được các sự kiện tiếp theo

Grizzly và Glassfish

Grizzly không phải là web container, nhưng hơn một framework NIO giúp các nhà phát triển xây dựng các ứng dụng có khả năng co giãn Nó được phát triển như là một phần trong dự án

Glassfish, nhưng nó cũng có thể được sử dụng độc lập hoặc nhúng vào hệ thống khác Grizzly

Trang 6

cung cấp các thành phần như một máy chủ HTTP/HTTPS và các thành phần cho Bayeux

Protocol, Servlet, HTTPService OSGi và Comet, trong số những thứ khác Grizzly hỗ trợ

WebSockets và được sử dụng trong Glassfish để cung cấp Comet và hỗ trợ WebSocket

Glassfish, Application Server (Máy chủ ứng dụng) của Oracle, là cách thực hiện tham khảo của các đặc tả J2EE 6 Glassfish là một gói hoàn chỉnh, như WebSphere và Jboss, khi sử dụng

Grizzly để hỗ trợ NIO, WebSocket và Comet Kiến trúc mô đun của nó, dựa trên OSGI, giúp nó linh hoạt trong việc thay đổi các thành phần Bảng 3 cho thấy Glassfish hỗ trợ Comet và

WebSockets

Bảng 3 Sự hỗ trợ của Glassfish

Các sự hỗ trợ Glassfish 2Glassfish 3

Non-blocking I/OX X

Servlet 2.5 X X

Cách sử dụng Grizzly cũng rất quan trọng, khi người ta dự định sử dụng nó theo cách nhúng hoặc trực tiếp từ mã Java Nó được sử dụng rộng rãi như một framework để hỗ trợ Comet và WebSockets, mà nó có thể được nhúng trong một ứng dụng lớn hơn, ví dụ như Glassfish, cung cấp các khả năng triển khai web và API đặc tả Servlet

Xem phần Tài nguyên để biết các liên kết đến các ví dụ của WebSockets và Comet trong Grizzly hoặc Glassfish Vì Glassfish sử dụng Grizzly, nên các ví dụ sẽ làm việc trên cả hai API

WebSocket cũng rất giống như một API trong Jetty, nhưng API của Comet phức tạp hơn

Jboss

Jboss là một máy chủ ứng dụng được xây dựng dựa trên Tomcat Nó đã hỗ trợ Comet và NIO kể

từ phiên bản 5 Jboss 7 vẫn còn đang phát triển, nhưng được đưa vào trong Bảng 4 dưới đây

Bảng 4 Sự hỗ trợ của Jboss

Các sự hỗ trợ Jboss 5Jboss 6 Jboss 7

Non-blocking I/OX X X

Servlet 2.5 X X X

WebSockets

WebSphere

Trang 7

WebSphere là một máy chủ ứng dụng của IBM Phiên bản 8 của WebSphere đã hỗ trợ của API Servlet 3 (có chứa API không đồng bộ tiêu chuẩn hóa cho Comet) (xem phần Tài nguyên để đọc thông báo)

Bảng 5 Sự hỗ trợ của WebSphere

Các sự hỗ trợ WebSphere 8

Non-blocking I/OX

Servlet 2.5 X

Servlet 3.0 X

WebSockets

Về đầu trang

Thế còn các API chung thì như thế nào?

Mỗi máy chủ đưa ra API nguyên gốc của riêng mình cho Comet và WebSocket Như bạn có thể đoán là việc viết một ứng dụng web di động có thể khó khăn Đặc tả Servlet 3.0 gồm có các phương thức bổ sung để treo và tiếp tục lại một yêu cầu sau đó, cho phép tất cả các web

container hỗ trợ Đặc tả Servlet 3.0 đều hỗ trợ các yêu cầu Comet long-polling

Nhóm phát triển Jetty cung cấp một thư viện được gọi là Continuation của Jetty, độc lập với Jetty container Thư viện Continuation của Jetty đủ thông minh để phát hiện ra container hoặc đặc tả

có sẵn Nếu bạn đang chạy trên một máy chủ Jetty, API của Jetty nguyên gốc sẽ được sử dụng Nếu bạn đang chạy trên một container hỗ trợ Đặc tả Servlet3.0, API chung (common API) này sẽ được sử dụng Nếu không, việc thực hiện không có khả năng co giãn sẽ được sử dụng

Về WebSockets, vẫn chưa có tiêu chuẩn nào trong Java và do đó bạn cần sử dụng API container của nhà cung cấp trong ứng dụng web của mình nếu bạn muốn sử dụng WebSockets

Bảng 6 tóm tắt các công nghệ được các máy chủ khác nhau hỗ trợ

Bảng 6 Các công nghệ được các máy chủ hỗ trợ

Jetty 6 Jetty Continuations Không có

Jetty 7 Servlet 3.0

Jetty Continuations

Native Jetty API (API Jetty nguyên gốc)

Jetty 8 Servlet 3.0

Jetty Continuations Native Jetty API

Tomcat 6 Advanced I/O (Vào/ra nâng cao) Không có

Tomcat 7

Servlet 3.0

Advanced I/O

Jetty Continuations

Không có

Trang 8

Glassfish 2 Native Grizzly API (API Grizzly nguyên

Glassfish 3

Servlet 3.0

Native Grizzly API

Jetty Continuations

Native Grizzly API Jboss 5 Native Jboss API (API Jboss nguyên gốc) Không có

Jboss 6

Servlet 3.0

Native Jboss API

Jetty Continuations

Không có

Jboss 7

Servlet 3.0

Native Jboss API

Jetty Continuations

Không có WebSphere

8

Servlet 3.0

Jetty Continuations Không có

Không có giải pháp rõ ràng nào cho WebSockets ngoại trừ việc sử dụng API container Như với Comet, mỗi container hỗ trợ Đặc tả Servlet 3.0 đều hỗ trợ Comet Ở đây lợi thế của Jetty

Continuations là cung cấp hỗ trợ Comet trên tất cả các container này Vì vậy, một số thư viện Reverse Ajax (được tiếp tục thảo luận dưới đây và trong phần tiếp theo của loạt bài này) đang sử dụng Jetty Continuations API phía máy chủ

API Jetty Continuation được hiển thị trong ví dụ mẫu trong bài này Đặc tả Servlet 3.0 đã được

mô tả và được sử dụng trong hai ví dụ Comet trong Phần 1 của loạt bài này

Về đầu trang

Các thư viện trừu tượng hóa

Khi xem xét tất cả các API chủ yếu (Servlet 3.0 và Jetty Continuations), cộng với tất cả sự hỗ trợ bên phía máy chủ và hai cách chủ yếu để thực hiện Reverse Ajax bên phía máy khách (Comet và WebSocket) sẽ thấy thật khó để viết JavaScript và mã Java riêng của bạn để nối chúng lại với nhau Bạn cũng phải tính đến các yếu tố như thời gian chờ timeout, các lỗi kết nối, tin báo nhận, chỉ dẫn, giữ dữ liệu trong bộ đệm và v.v

Phần còn lại của bài này sẽ cho bạn thấy Socket.IO hoạt động như thế nào Phần 4 của loạt bài

này sẽ tìm hiểu Atmosphere và CometD Tất cả ba thư viện này đều là nguồn mở và tất cả chúng đều hỗ trợ Comet và WebSocket trên nhiều máy chủ

Socket.IO

Socket.IO là một thư viện máy khách JavaScript cung cấp một API riêng, tương tự như

WebSocket, để kết nối đến một máy chủ từ xa để gửi và nhận thông báo không đồng bộ Bằng cách cung cấp một API chung, Socket.IO hỗ trợ một số kiểu truyền tải: WebSocket, Flash

Sockets, long-polling, streaming, forever Iframes (Các khung nội tuyến vô hạn) và JSONP polling Socket.IO phát hiện ra các khả năng của trình duyệt và cố gắng lựa chọn kiểu truyền tải

Trang 9

tốt nhất có thể Thư viện Socket.IO tương thích với hầu hết tất cả các trình duyệt (bao gồm cả các trình duyệt cũ, chẳng hạn như IE 5.5) cũng như các trình duyệt di động Nó cũng có các tính năng như các nhịp hoạt động (heartbeat), thời gian chờ, ngắt kết nối và xử lý lỗi

Trang web Socket.IO (xem phần Tài nguyên) mô tả chi tiết thư viện này hoạt động ra sao và trình duyệt và kỹ thuật Reverse Ajax nào được sử dụng Về cơ bản, Socket.IO sử dụng một giao thức truyền thông cho phép thư viện máy khách truyền thông với một điểm cuối ở phía máy chủ,

mà điểm này có thể hiểu được giao thức Socket.IO Đầu tiên người ta phát triển Socket.IO cho Node JS, một công cụ JavaScript được sử dụng để xây dựng các máy chủ nhanh hơn Nhưng giờ đây, nhiều dự án đã mang đến sự hỗ trợ cho các ngôn ngữ khác, bao gồm cả Java

Liệt kê 4 cho thấy một ví dụ về sử dụng thư viện JavaScript Socket.IO bên phía máy khách Trang web Socket.IO có tài liệu hướng dẫn và các ví dụ

Liệt kê 4 Sử dụng thư viện máy khách Socket.IO

var socket = new io.Socket(document.domain, {

resource: 'chat'

});

socket.on('connect', function() {

// Socket.IO is connected

});

socket.on('disconnect', function(disconnectReason, errorMessage) {

// Socket.IO disconnected

});

socket.on('message', function(mtype, data, error) {

// The server sent an event

});

// Now that the handlers are defined, establish the connection:

socket.connect();

Để sử dụng thư viện JavaScript Socket.IO, bạn sẽ cần một thành phần Java tương ứng được gọi

là Socket.IO Java (xem phần Tài nguyên) Ban đầu dự án này được nhóm công tác Apache Wave khởi động để đem đến sự hỗ trợ Reverse Ajax cho Wave trước khi WebSockets được hỗ trợ Socket.IO Java được chia làm hai nhánh và được duy trì bởi Ovea (một công ty chuyên về phát triển web theo hướng sự kiện) và sau đó bị bỏ rơi Việc phát triển tầng sau để hỗ trợ thư viện máy khách Socket.IO có phức tạp là do có nhiều kiểu truyền tải Phần 4 của loạt bài này sẽ chỉ ra

vì sao việc hỗ trợ nhiều kiểu truyền tải trong một thư viện máy khách là không cần thiết để đạt được khả năng co giãn và hỗ trợ trình duyệt tốt hơn, do chỉ cần long-polling và WebSockets là

đủ Khi chưa hỗ trợ WebSockets, Socket.IO đã là một lựa chọn tốt

Socket.IO Java sử dụng API Jetty Continuation để treo và tiếp tục lại các yêu cầu Nó sử dụng API WebSockets của Jetty nguyên gốc để hỗ trợ WebSockets Bạn có thể xác định máy chủ nào

sẽ làm việc đúng với một ứng dụng web khi sử dụng Socket.IO Java

Trang 10

Liệt kê 5, dưới đây, cho thấy một ví dụ về cách sử dụng Socket.IO trên máy chủ Bạn phải định nghĩa một SocketIOServlet mở rộng của servlet và thực hiện một phương thức để trả về một loại đại diện điểm cuối API này rất giống với API WebSockets Ưu điểm là ở chỗ API này được

sử dụng bên phía máy chủ, độc lập với việc truyền tải được chọn bên phía máy khách Socket.IO chuyển dịch tất cả các kiểu truyền tải tới API tương tự bên phía máy chủ

Liệt kê 5 Thư viện Socket.IO Java được sử dụng cho ví dụ mẫu chat - servlet

public final class ChatServlet extends SocketIOServlet {

private final BlockingQueue<Endpoint> endpoints =

new LinkedBlockingQueue<Endpoint>();

@Override

protected SocketIOInbound doSocketIOConnect

(HttpServletRequest request) {

String user =

(String) request.getSession().getAttribute("user");

return user == null ? null : new Endpoint(this, user, request); }

void broadcast(String data) {

for (Endpoint endpoint : endpoints) {

endpoint.send(data);

}

}

void add(Endpoint endpoint) {

endpoints.offer(endpoint);

}

void remove(Endpoint endpoint) {

endpoints.remove(endpoint);

}

}

Liệt kê 6 cho thấy cách trả về điểm cuối

Liệt kê 6 Sử dụng thư viện Socket.IO Java cho ví dụ mẫu chat - điểm cuối

class Endpoint implements SocketIOInbound {

[ ]

private SocketIOOutbound outbound;

[ ]

@Override

public void onConnect(SocketIOOutbound outbound) {

this.outbound = outbound;

Ngày đăng: 09/03/2014, 04:20

HÌNH ẢNH LIÊN QUAN

Bảng 1. Các phiên bản Jetty và khả năng hỗ trợ - Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO pdf
Bảng 1. Các phiên bản Jetty và khả năng hỗ trợ (Trang 2)
Bảng 2. Sự hỗ trợ của Tomcat - Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO pdf
Bảng 2. Sự hỗ trợ của Tomcat (Trang 4)
Bảng 3. Sự hỗ trợ của Glassfish - Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO pdf
Bảng 3. Sự hỗ trợ của Glassfish (Trang 6)
Bảng 4. Sự hỗ trợ của Jboss - Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO pdf
Bảng 4. Sự hỗ trợ của Jboss (Trang 6)

TỪ KHÓA LIÊN QUAN

w