Giáo Trình Lập Trình Mạng Java - Đại Học Cần Thơ (Khoa CNTT)
Chuyên khảo phân tích Lap trinh mang java, đánh giá các khía cạnh quan trọng, đề xuất hướng nghiên cứu tiếp theo., phục vụ nghiên cứu và ứng dụng thực tiễn
Trường đại học
Đại học cần thơChuyên ngành
Công nghệ thông tinNgười đăng
Ẩn danhThể loại
Giáo trìnhPhí lưu trữ
35 PointMục lục chi tiết
Tóm tắt
I. Hướng Dẫn Lập Trình Mạng Java Từ Nền Tảng Đến Nâng Cao
Lập trình mạng Java là một lĩnh vực chuyên sâu, cho phép các ứng dụng giao tiếp và trao đổi dữ liệu qua mạng máy tính, điển hình là Internet. Nền tảng của lĩnh vực này là khả năng xây dựng các hệ thống phân tán, nơi các thành phần phần mềm có thể hoạt động trên nhiều máy tính khác nhau nhưng vẫn phối hợp nhịp nhàng. Tài liệu này cung cấp một lộ trình toàn diện, bắt đầu từ những khái niệm cốt lõi nhất như giao tiếp liên quá trình (Inter-Process Communication - IPC) đến các kỹ thuật triển khai ứng dụng mạng phức tạp. Trong các hệ thống đa nhiệm, mỗi quá trình vận hành trong một không gian địa chỉ riêng biệt. Để chúng có thể giao tiếp, hệ điều hành cung cấp các cơ chế IPC. Các cơ chế này được phân thành hai loại chính: giao tiếp trên cùng một máy tính và giao tiếp giữa các máy tính khác nhau. Đối với lập trình mạng, trọng tâm nằm ở loại thứ hai, nơi dữ liệu không chỉ đi qua nhân hệ điều hành mà còn phải tuân thủ các quy tắc chung gọi là giao thức (Protocol). Ngôn ngữ Java, với bộ thư viện mạng phong phú và đặc tính "Viết một lần, chạy mọi nơi" (Write Once, Run Anywhere) nhờ vào Máy ảo Java (JVM), đã trở thành một lựa chọn hàng đầu để phát triển các ứng dụng mạng mạnh mẽ, an toàn và đa nền tảng. Giáo trình này sẽ đi sâu vào việc sử dụng các API của Java để triển khai các mô hình giao tiếp phổ biến, đặc biệt là mô hình Client-Server, và cách quản lý các kết nối mạng một cách hiệu quả.
1.1. Định nghĩa Giao tiếp liên quá trình IPC trong mạng
Giao tiếp liên quá trình (IPC) là cơ chế cho phép các tiến trình độc lập trao đổi dữ liệu và đồng bộ hóa hoạt động. Trong bối cảnh lập trình mạng, IPC vượt ra khỏi phạm vi một máy tính đơn lẻ. Nó cho phép một ứng dụng trên máy A gửi thông tin đến một ứng dụng trên máy B. Theo tài liệu gốc từ Khoa CNTT - Đại học Cần Thơ, cơ chế này đòi hỏi dữ liệu không chỉ đi qua nhân (kernel) của hệ điều hành mà "các hạt nhân của các máy có liên quan phải hiểu nhau". Sự "hiểu nhau" này được thiết lập thông qua các giao thức truyền thông. Có hai loại IPC chính: loại 1 hỗ trợ giao tiếp trên cùng một máy tính (ví dụ: shared memory, files) và loại 2 hỗ trợ giao tiếp giữa các máy tính khác nhau. Lập trình mạng Java tập trung hoàn toàn vào loại 2, sử dụng các API mạng để đóng gói và gửi dữ liệu theo các quy tắc đã được chuẩn hóa, đảm bảo rằng thông tin được truyền đi một cách chính xác và đáng tin cậy.
1.2. Vai trò của Giao thức Protocol trong lập trình mạng
Giao thức (Protocol) là một tập hợp các quy tắc và quy ước mà các thực thể truyền thông phải tuân theo để có thể giao tiếp với nhau. Nếu không có giao thức, dữ liệu được gửi từ một máy tính sẽ chỉ là một chuỗi bit vô nghĩa đối với máy tính nhận. Giao thức định nghĩa mọi khía cạnh của quá trình giao tiếp, bao gồm định dạng dữ liệu (cấu trúc của các thông điệp), thứ tự trao đổi thông điệp, và các hành động cần thực hiện khi nhận được một thông điệp hoặc khi có sự kiện xảy ra. Ví dụ, giao thức HTTP quy định cách một trình duyệt web (client) yêu cầu một trang web từ một máy chủ (server) và cách server trả về nội dung trang web đó. Trong lập trình mạng Java, các lập trình viên không cần phải tự xây dựng các giao thức tầng thấp như TCP hay IP, mà sẽ sử dụng các lớp và thư viện có sẵn để làm việc với các giao thức tầng ứng dụng như HTTP, FTP, hoặc tự định nghĩa giao thức riêng cho ứng dụng của mình.
1.3. Ưu điểm của Java trong việc phát triển ứng dụng mạng
Java sở hữu nhiều đặc điểm vượt trội khiến nó trở thành lựa chọn lý tưởng cho lập trình ứng dụng mạng. Thứ nhất, tính đa nền tảng của Java, được đảm bảo bởi Máy ảo Java (JVM), cho phép một ứng dụng mạng được viết một lần và có thể chạy trên nhiều hệ điều hành khác nhau (Windows, Linux, macOS) mà không cần biên dịch lại. Điều này giúp giảm đáng kể chi phí phát triển và triển khai. Thứ hai, Java cung cấp một bộ API mạng (java.net) rất mạnh mẽ và dễ sử dụng, trừu tượng hóa các chi tiết phức tạp của việc lập trình socket. Thứ ba, khả năng lập trình đa luồng (multithreading) được tích hợp sẵn trong ngôn ngữ, giúp việc xây dựng các server có khả năng phục vụ nhiều client cùng lúc trở nên đơn giản hơn. Cuối cùng, mô hình bảo mật của Java giúp hạn chế các thao tác nguy hiểm, tạo ra các ứng dụng mạng an toàn hơn.
II. Thách Thức Lập Trình Mạng Tại Sao Mô Hình OSI Quan Trọng
Việc xây dựng các hệ thống mạng có khả năng kết nối toàn cầu là một công việc cực kỳ phức tạp. Các hệ thống này bao gồm vô số thiết bị phần cứng và phần mềm từ nhiều nhà sản xuất khác nhau. Thách thức lớn nhất là làm thế nào để tất cả các thành phần này có thể "nói chuyện" được với nhau một cách thống nhất. Để giải quyết vấn đề này, Tổ chức Tiêu chuẩn Quốc tế (ISO) đã phát triển Mô hình tham khảo OSI (Open Systems Interconnection). Mô hình này không phải là một giao thức, mà là một khuôn khổ khái niệm, chia quá trình truyền thông mạng thành bảy tầng (layer) riêng biệt. Mỗi tầng chịu trách nhiệm cho một nhóm chức năng cụ thể và chỉ giao tiếp với tầng ngay trên và ngay dưới nó. Cách tiếp cận phân tầng này giúp đơn giản hóa việc thiết kế và triển khai. Lập trình viên có thể tập trung vào một tầng cụ thể mà không cần quan tâm đến chi tiết phức tạp của các tầng khác. Ví dụ, khi thực hiện lập trình mạng Java ở tầng ứng dụng, lập trình viên không cần phải xử lý các vấn đề như tín hiệu điện trên dây cáp (Tầng Vật lý) hay tìm đường đi cho gói tin (Tầng Mạng). Việc hiểu rõ mô hình OSI giúp các nhà phát triển có cái nhìn tổng quan về toàn bộ quá trình truyền thông, từ đó gỡ lỗi và tối ưu hóa ứng dụng hiệu quả hơn.
2.1. Phân tích 7 tầng chức năng của Mô hình tham khảo OSI
Mô hình OSI cấu trúc hóa giao tiếp mạng thành 7 tầng logic. Bắt đầu từ dưới lên: 1. Tầng Vật lý (Physical Layer): Điều khiển việc truyền bit trên đường truyền vật lý. 2. Tầng Liên kết dữ liệu (Data-Link Layer): Đảm bảo truyền các khung dữ liệu (frame) tin cậy giữa hai nút mạng kết nối trực tiếp. 3. Tầng Mạng (Network Layer): Chịu trách nhiệm định tuyến các gói tin (packet) qua nhiều mạng khác nhau để đến được đích cuối cùng. 4. Tầng Vận chuyển (Transport Layer): Cung cấp dịch vụ truyền dữ liệu đầu cuối (end-to-end), đảm bảo dữ liệu được gửi đi không lỗi, đúng thứ tự. 5. Tầng Giao dịch (Session Layer): Quản lý các phiên giao tiếp giữa các ứng dụng. 6. Tầng Trình bày (Presentation Layer): Đảm bảo dữ liệu từ tầng ứng dụng của một hệ thống có thể được đọc bởi tầng ứng dụng của hệ thống khác (ví dụ: mã hóa, nén dữ liệu). 7. Tầng Ứng dụng (Application Layer): Cung cấp giao diện cho người dùng và các ứng dụng để truy cập vào dịch vụ mạng. Hầu hết hoạt động lập trình mạng Java diễn ra ở tầng này.
2.2. Quá trình đóng gói dữ liệu Encapsulation qua các tầng
Khi dữ liệu được gửi từ một ứng dụng trên máy gửi, nó sẽ di chuyển từ Tầng Ứng dụng xuống Tầng Vật lý. Tại mỗi tầng, dữ liệu sẽ được thêm vào một phần thông tin điều khiển gọi là header, quá trình này được gọi là đóng gói (encapsulation). Ví dụ, Tầng Vận chuyển sẽ thêm header chứa thông tin cổng (port) vào dữ liệu từ tầng trên để tạo thành một Segment. Tầng Mạng nhận Segment này, thêm vào header chứa địa chỉ IP để tạo thành một Packet. Tầng Liên kết dữ liệu lại thêm header chứa địa chỉ MAC để tạo thành một Frame. Cuối cùng, Frame được chuyển thành chuỗi các bit để truyền đi ở Tầng Vật lý. Tại máy nhận, quá trình diễn ra ngược lại (gọi là bóc tách - de-encapsulation), dữ liệu được truyền lên các tầng cao hơn và header của mỗi tầng sẽ được gỡ bỏ và xử lý. Quá trình này đảm bảo mỗi tầng thực hiện đúng chức năng của mình một cách độc lập.
III. Bí Quyết Xây Dựng Ứng Dụng Mạng Với Bộ Giao Thức TCP IP
Trong khi mô hình OSI là một mô hình tham khảo lý thuyết, bộ giao thức được sử dụng phổ biến nhất trên thực tế là TCP/IP. Đây là bộ giao thức nền tảng của mạng Internet. Mô hình TCP/IP đơn giản hơn OSI, thường được mô tả với bốn hoặc năm tầng. Đối với lập trình viên, hai giao thức quan trọng nhất trong bộ này là TCP (Transmission Control Protocol) và UDP (User Datagram Protocol), cả hai đều hoạt động ở Tầng Vận chuyển. TCP là giao thức hướng kết nối (connection-oriented), đảm bảo dữ liệu được truyền đi một cách đáng tin cậy, đúng thứ tự và không bị mất mát. Nó thực hiện điều này thông qua cơ chế bắt tay ba bước (three-way handshake) để thiết lập kết nối và cơ chế xác nhận (acknowledgment) để kiểm tra lỗi. Do đó, TCP phù hợp cho các ứng dụng đòi hỏi độ tin cậy cao như truyền file (FTP), duyệt web (HTTP), email (SMTP). Ngược lại, UDP là giao thức không kết nối (connectionless), gửi các gói dữ liệu (datagram) mà không cần thiết lập kết nối trước và không đảm bảo việc gửi nhận. Điều này làm cho UDP nhanh và nhẹ hơn TCP, phù hợp cho các ứng dụng ưu tiên tốc độ hơn độ tin cậy như game online, streaming video, hoặc các truy vấn DNS. Khi lập trình mạng Java, việc lựa chọn giữa TCP và UDP là một quyết định kiến trúc quan trọng, phụ thuộc vào yêu cầu cụ thể của ứng dụng.
3.1. Tìm hiểu mô hình Client Server trong kiến trúc mạng
Mô hình Client-Server là kiến trúc nền tảng cho hầu hết các dịch vụ mạng hiện nay. Trong mô hình này, chương trình ứng dụng được chia thành hai thành phần: Server và Client. Server là một quá trình chuyên cung cấp một dịch vụ cụ thể, luôn ở trạng thái lắng nghe các yêu cầu từ Client. Client là quá trình có nhu cầu sử dụng dịch vụ đó. Để được phục vụ, Client sẽ gửi một thông điệp yêu cầu (Request Message) đến Server. Server sau khi nhận và xử lý yêu cầu sẽ gửi lại một thông điệp trả lời (Reply Message) chứa kết quả. Ví dụ điển hình là dịch vụ web: Web Server cung cấp các trang web và Web Browser (Client) yêu cầu và hiển thị chúng. Lập trình mạng Java cung cấp các công cụ mạnh mẽ để xây dựng cả hai phía Client và Server một cách dễ dàng.
3.2. So sánh hai giao thức TCP và UDP trong bộ giao thức TCP IP
Sự khác biệt cốt lõi giữa TCP và UDP nằm ở cơ chế truyền tải. TCP (Transmission Control Protocol) thiết lập một kết nối ảo tin cậy giữa hai điểm cuối trước khi truyền dữ liệu. Nó phân mảnh dữ liệu lớn thành các segment, đánh số thứ tự, và yêu cầu phía nhận gửi xác nhận (ACK) cho mỗi segment nhận được. Nếu có lỗi hoặc mất gói, TCP sẽ tự động gửi lại. Điều này đảm bảo tính toàn vẹn và đúng thứ tự của dữ liệu, nhưng cũng làm tăng độ trễ. Ngược lại, UDP (User Datagram Protocol) chỉ đơn giản là "bắn" các datagram đến địa chỉ đích mà không cần bất kỳ sự xác nhận nào. Nó không đảm bảo dữ liệu sẽ đến, đến đúng thứ tự, hay không bị trùng lặp. Ưu điểm của UDP là tốc độ rất nhanh và chi phí tài nguyên thấp. Việc lựa chọn giữa chúng là sự đánh đổi giữa độ tin cậy và hiệu suất.
IV. Phương Pháp Lập Trình Socket Java Stream I O Hiệu Quả
Để triển khai các ứng dụng mạng trong Java, API cốt lõi được sử dụng là Socket. Một socket có thể được hình dung như một điểm cuối (endpoint) của một kênh giao tiếp hai chiều giữa hai chương trình chạy trên mạng. Java cung cấp hai lớp socket chính trong gói java.net: Socket dùng cho client và ServerSocket dùng cho server khi sử dụng giao thức TCP. Đối với giao thức UDP, Java cung cấp các lớp DatagramSocket và DatagramPacket. Quá trình giao tiếp sử dụng TCP socket thường diễn ra như sau: Server tạo một đối tượng ServerSocket và lắng nghe trên một cổng (port) cụ thể. Client tạo một đối tượng Socket, chỉ định địa chỉ IP và cổng của server để kết nối. Khi ServerSocket chấp nhận kết nối, một kênh giao tiếp được thiết lập. Sau khi kết nối, cả client và server đều sử dụng các luồng I/O (Stream I/O) để gửi và nhận dữ liệu. Dữ liệu được truyền qua socket dưới dạng một dòng byte liên tục. Việc hiểu và sử dụng thành thạo các lớp InputStream và OutputStream là chìa khóa để lập trình socket Java hiệu quả. Các lớp này cung cấp các phương thức để đọc và ghi dữ liệu byte, từ đó xây dựng nên các cơ chế giao tiếp phức tạp hơn.
4.1. Hướng dẫn tạo kết nối TCP với Socket và ServerSocket
Để tạo một Server TCP trong Java, bước đầu tiên là khởi tạo một đối tượng ServerSocket, liên kết nó với một số cổng cụ thể (ví dụ: ServerSocket server = new ServerSocket(8080);). Sau đó, server gọi phương thức accept(). Lệnh này sẽ chặn (block) chương trình cho đến khi có một client kết nối tới. Khi một client kết nối, phương thức accept() trả về một đối tượng Socket, đại diện cho kết nối với client đó. Về phía Client TCP, chương trình tạo một đối tượng Socket và truyền vào địa chỉ IP và số cổng của server (ví dụ: Socket client = new Socket("127.0.0.1", 8080);). Nếu kết nối thành công, một kênh giao tiếp hai chiều được thiết lập. Cả server và client lúc này đều có thể lấy các luồng I/O từ đối tượng Socket để bắt đầu trao đổi dữ liệu.
4.2. Kỹ thuật đọc và ghi dữ liệu qua InputStream và OutputStream
Sau khi kết nối socket được thiết lập, việc trao đổi dữ liệu được thực hiện thông qua các đối tượng InputStream và OutputStream. Mỗi đối tượng Socket cung cấp hai phương thức: getInputStream() để nhận dữ liệu và getOutputStream() để gửi dữ liệu. Các luồng này xử lý dữ liệu ở dạng byte thô. Để làm việc với các kiểu dữ liệu phức tạp hơn như chuỗi ký tự hay các đối tượng, người ta thường "bọc" các luồng cơ bản này bằng các lớp khác như BufferedReader để đọc từng dòng văn bản, hoặc PrintWriter để ghi văn bản. Ví dụ, để đọc một chuỗi từ client, server có thể thực hiện: BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String message = in.readLine();. Tương tự, để gửi một chuỗi đến client, server dùng: PrintWriter out = new PrintWriter(socket.getOutputStream(), true); out.println("Hello Client!");. Việc quản lý các luồng này đúng cách là rất quan trọng để tránh tắc nghẽn và rò rỉ tài nguyên.
V. Top Kỹ Thuật Tối Ưu Server Lập Trình Đa Luồng Trong Java
Một server đơn luồng chỉ có thể xử lý một client tại một thời điểm. Nếu server đang bận xử lý yêu cầu của client A, thì client B, C, và D phải chờ đợi. Điều này tạo ra một nút thắt cổ chai nghiêm trọng và làm giảm hiệu năng của hệ thống. Giải pháp cho vấn đề này là sử dụng lập trình đa luồng (multithreading). Java có sự hỗ trợ mạnh mẽ cho đa luồng ngay từ cấp độ ngôn ngữ. Ý tưởng cơ bản là server chính sẽ chạy trong một luồng (thread) chính, có nhiệm vụ lắng nghe các kết nối mới từ client. Mỗi khi một client kết nối thành công (phương thức accept() trả về một Socket), server sẽ không xử lý trực tiếp kết nối đó. Thay vào đó, nó sẽ tạo ra một luồng công việc (worker thread) mới và giao đối tượng Socket của client đó cho luồng này xử lý. Luồng chính sau đó ngay lập tức quay trở lại vòng lặp để tiếp tục lắng nghe các kết nối mới. Bằng cách này, server có thể phục vụ đồng thời nhiều client, mỗi client được xử lý trong một luồng riêng biệt. Kỹ thuật này giúp tận dụng tối đa tài nguyên CPU và cải thiện đáng kể khả năng đáp ứng của ứng dụng mạng. Luồng trong Java được quản lý thông qua lớp Thread hoặc giao diện Runnable.
5.1. Khái niệm Luồng Thread và tầm quan trọng trong ứng dụng server
Một luồng (Thread) là một đơn vị thực thi nhỏ nhất trong một chương trình. Trong một ứng dụng Java, có thể có nhiều luồng chạy song song, chia sẻ cùng một không gian bộ nhớ. Đối với ứng dụng server, tầm quan trọng của luồng là không thể phủ nhận. Thay vì xử lý tuần tự từng yêu cầu, một server đa luồng có thể xử lý đồng thời nhiều yêu cầu. Tài liệu gốc mô tả mô hình này với một "Luồng phân phát (Dispatcher thread)" và nhiều "Luồng thực hiện (Worker thread)". Luồng phân phát nhận kết nối và chuyển cho các luồng thực hiện xử lý. "Những luồng thực hiện hoạt động song song nhau [...] nhờ đó, Server có thể phục vụ nhiều Client một cách đồng thời". Việc này không chỉ tăng thông lượng (throughput) mà còn cải thiện trải nghiệm người dùng, vì họ không phải chờ đợi lâu để được phục vụ.
5.2. Cách triển khai một Server đa luồng để phục vụ nhiều Client
Để triển khai một server đa luồng trong Java, cấu trúc chung thường như sau: Server chính tạo một ServerSocket và đặt nó trong một vòng lặp vô hạn (while(true)). Bên trong vòng lặp, server gọi serverSocket.accept() để chờ kết nối. Khi một Socket của client được trả về, server sẽ tạo một đối tượng của một lớp xử lý (ví dụ ClientHandler) vốn được cài đặt giao diện Runnable. Đối tượng Socket này được truyền vào constructor của ClientHandler. Sau đó, server tạo một Thread mới với đối tượng ClientHandler này và gọi phương thức start(). Luồng mới này sẽ thực thi logic giao tiếp với client trong phương thức run() của nó, trong khi luồng chính tiếp tục vòng lặp để chấp nhận các client khác. Cấu trúc này cho phép server mở rộng quy mô để xử lý hàng trăm hoặc hàng ngàn kết nối đồng thời.
VI. Tổng Kết Tương Lai Của Các Kiến Trúc Lập Trình Mạng Java
Giáo trình này đã trình bày một cách hệ thống các khái niệm từ cơ bản đến nâng cao trong lập trình mạng Java. Bắt đầu từ nền tảng lý thuyết với mô hình OSI và bộ giao thức TCP/IP, nội dung đã đi sâu vào các kiến trúc ứng dụng phổ biến như mô hình Client-Server. Trọng tâm của việc triển khai thực tế nằm ở việc sử dụng API Socket của Java để thiết lập các kênh giao tiếp và dùng Stream I/O để truyền nhận dữ liệu. Một kỹ thuật then chốt để xây dựng các server hiệu năng cao là áp dụng lập trình đa luồng, cho phép xử lý đồng thời nhiều kết nối. Các kiến trúc ứng dụng cũng đã phát triển từ đơn tầng, hai tầng (client-server) lên đến kiến trúc đa tầng (N-Tier Architecture), nơi logic nghiệp vụ (Business Rule) được tách ra thành một tầng riêng biệt. Kiến trúc này, như được mô tả trong tài liệu, "giải quyết được hầu hết các nhược điểm mắc phải của kiến trúc đơn tầng và kiến trúc hai tầng". Trong tương lai, lập trình mạng Java tiếp tục phát triển với các framework và công nghệ mới như Non-blocking I/O (Java NIO), các framework như Netty, Spring WebFlux để xây dựng các hệ thống phản ứng (reactive systems) có khả năng mở rộng tốt hơn nữa.
6.1. So sánh các kiến trúc ứng dụng Đơn tầng Hai tầng và Đa tầng
Các ứng dụng mạng có thể được cấu trúc theo nhiều kiến trúc khác nhau. Kiến trúc đơn tầng (Single-tier) tích hợp tất cả các thành phần (giao diện, logic nghiệp vụ, lưu trữ dữ liệu) vào một chương trình duy nhất. Kiến trúc hai tầng (Two-tier), hay Client-Server, tách biệt phần giao diện (Client) và phần lưu trữ dữ liệu (Server), nhưng logic nghiệp vụ có thể nằm ở một trong hai phía. Nhược điểm là khó bảo trì và mở rộng khi logic thay đổi. Kiến trúc đa tầng (N-tier), thường là ba tầng, tách biệt rõ ràng ba thành phần: Tầng trình bày (Client), Tầng logic nghiệp vụ (Application Server), và Tầng dữ liệu (Database Server). Kiến trúc này rất linh hoạt, dễ bảo trì và mở rộng, là nền tảng cho hầu hết các ứng dụng doanh nghiệp hiện đại.
6.2. Hướng phát triển Từ Blocking I O đến Non blocking I O NIO
Mô hình lập trình socket truyền thống trong Java (java.net) dựa trên Blocking I/O. Điều này có nghĩa là các lệnh đọc (read()) hoặc ghi (write()) sẽ làm luồng bị chặn cho đến khi thao tác hoàn tất. Mô hình một luồng-một client, dù hiệu quả, vẫn có giới hạn về số lượng luồng mà hệ điều hành có thể quản lý. Để giải quyết vấn đề này, Java đã giới thiệu gói Java NIO (New I/O), cung cấp cơ chế Non-blocking I/O. Với NIO, một luồng duy nhất có thể quản lý nhiều kết nối mạng cùng lúc thông qua các khái niệm như Channels và Selectors. Thay vì phải chờ đợi, luồng có thể đăng ký quan tâm đến các sự kiện (ví dụ: có dữ liệu để đọc) trên nhiều kênh và chỉ xử lý khi sự kiện xảy ra. Đây là nền tảng cho các máy chủ hiệu năng cực cao, có khả năng xử lý hàng chục nghìn kết nối đồng thời trên một máy chủ duy nhất.