Hướng dẫn lập trình Java Socket
1. Socket là gì?
Tại phía server (server-side):
- Thông thường, một chương trình server chạy trên một máy tính cụ thể, chương trình này có một ổ cắm (Server Socket), ổ cắm được ràng buộc bởi cổng (Port number) cụ thể. Các chương trình phục vụ (Server program) chỉ chờ đợi, lắng nghe tại ổ cắm (Server Socket) các Client để thực hiện một yêu cầu kết nối.
Tại phía client (client-side):
- Các Client biết tên máy của máy tính mà trên đó chương trình chủ (server) đang chạy và số cổng mà chương trình chủ lắng nghe. Để thực hiện một yêu cầu kết nối, Client cố gắng tạo ra cuộc gặp với máy chủ trên máy tính của chương trình chủ và cổng. Các Client cũng cần phải tự định danh chính nó với server để gắn với một cổng địa phương cái sẽ được sử dụng trong suốt quá trình kết nối này, thông thường nó được gán bởi hệ điều hành.
Hình minh họa tổng quát:
Nếu mọi việc suôn sẻ, chương trình chủ (server program) chấp nhận kết nối của client. Khi chấp nhận, máy chủ có được một socket mới bị ràng buộc vào cùng "cổng địa phương" và thông tin đầu cuối (remote endpoint) của nó chính là địa chỉ và cổng của client. Nó đã tạo ra một socket mới để chăm sóc Client vừa được chấp nhận kết nối, và tiếp tục lắng nghe tại ổ cắm gốc ban đầu (ServerSocket) cho các yêu cầu kết nối khác.
Về phía Client, nếu kết nối được chấp nhận, một ổ cắm được tạo thành công và Client có thể sử dụng ổ cắm để giao tiếp với chương trình chủ.
Các Client và Server có thể giao tiếp bằng cách ghi hay đọc từ ổ cắm (Socket) của chúng.
Dữ liệu ghi vào luồng đầu ra trên Socket của client sẽ nhận được trên luồng đầu vào của Socket tại Server. Và ngược lại dữ liệu ghi vào luồng đầu ra trên Socket của Server sẽ nhận được trên luồng đầu vào của Socket tại Client.
DefinitionMột Socket là một điểm cuối của một giao tiếp 2 chiều giữa hai chương trình chạy trên mạng. Socket được giàng buộc với một cổng (con số cụ thể) để các tầng TCP (TCP Layer) có thể định danh ứng dụng mà dữ liệu sẽ được gửi tới.
2. Ví dụ đơn giản với Socket
SimpleServerProgram.java
package org.o7planning.tutorial.socket;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleServerProgram {
public static void main(String args[]) {
ServerSocket listener = null;
String line;
BufferedReader is;
BufferedWriter os;
Socket socketOfServer = null;
// Mở một ServerSocket tại cổng 9999.
// Chú ý bạn không thể chọn cổng nhỏ hơn 1023 nếu không là người dùng
// đặc quyền (privileged users (root)).
try {
listener = new ServerSocket(9999);
} catch (IOException e) {
System.out.println(e);
System.exit(1);
}
try {
System.out.println("Server is waiting to accept user...");
// Chấp nhận một yêu cầu kết nối từ phía Client.
// Đồng thời nhận được một đối tượng Socket tại server.
socketOfServer = listener.accept();
System.out.println("Accept a client!");
// Mở luồng vào ra trên Socket tại Server.
is = new BufferedReader(new InputStreamReader(socketOfServer.getInputStream()));
os = new BufferedWriter(new OutputStreamWriter(socketOfServer.getOutputStream()));
// Nhận được dữ liệu từ người dùng và gửi lại trả lời.
while (true) {
// Đọc dữ liệu tới server (Do client gửi tới).
line = is.readLine();
// Ghi vào luồng đầu ra của Socket tại Server.
// (Nghĩa là gửi tới Client).
os.write(">> " + line);
// Kết thúc dòng
os.newLine();
// Đẩy dữ liệu đi
os.flush();
// Nếu người dùng gửi tới QUIT (Muốn kết thúc trò chuyện).
if (line.equals("QUIT")) {
os.write(">> OK");
os.newLine();
os.flush();
break;
}
}
} catch (IOException e) {
System.out.println(e);
e.printStackTrace();
}
System.out.println("Sever stopped!");
}
}
SimpleClientDemo.java
package org.o7planning.tutorial.socket;
import java.io.*;
import java.net.*;
public class SimpleClientDemo {
public static void main(String[] args) {
// Địa chỉ máy chủ.
final String serverHost = "localhost";
Socket socketOfClient = null;
BufferedWriter os = null;
BufferedReader is = null;
try {
// Gửi yêu cầu kết nối tới Server đang lắng nghe
// trên máy 'localhost' cổng 9999.
socketOfClient = new Socket(serverHost, 9999);
// Tạo luồng đầu ra tại client (Gửi dữ liệu tới server)
os = new BufferedWriter(new OutputStreamWriter(socketOfClient.getOutputStream()));
// Luồng đầu vào tại Client (Nhận dữ liệu từ server).
is = new BufferedReader(new InputStreamReader(socketOfClient.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host " + serverHost);
return;
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " + serverHost);
return;
}
try {
// Ghi dữ liệu vào luồng đầu ra của Socket tại Client.
os.write("HELO");
os.newLine(); // kết thúc dòng
os.flush(); // đẩy dữ liệu đi.
os.write("I am Tom Cat");
os.newLine();
os.flush();
os.write("QUIT");
os.newLine();
os.flush();
// Đọc dữ liệu trả lời từ phía server
// Bằng cách đọc luồng đầu vào của Socket tại Client.
String responseLine;
while ((responseLine = is.readLine()) != null) {
System.out.println("Server: " + responseLine);
if (responseLine.indexOf("OK") != -1) {
break;
}
}
os.close();
is.close();
socketOfClient.close();
} catch (UnknownHostException e) {
System.err.println("Trying to connect to unknown host: " + e);
} catch (IOException e) {
System.err.println("IOException: " + e);
}
}
}
Chạy ví dụ:
Trước hết bạn cần chạy class SimpleServerProgram:
Server is waiting to accept user...
Tiếp theo chạy class SimpleClientDemo.
Server: >> HELO
Server: >> I am Tom Cat
Server: >> QUIT
Server: >> OK
3. Ví dụ Socket + Thread
Thông thường một kết nối giữa chương trình chủ (Server) và 1 Client được tạo ra, bạn nên để chúng nói chuyện với nhau trên một luồng (Thread), như vậy mỗi khi có một kết nối mới một luồng mới lại được tạo ra.
ServerProgram.java
package org.o7planning.tutorial.socketthread;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerProgram {
public static void main(String args[]) throws IOException {
ServerSocket listener = null;
System.out.println("Server is waiting to accept user...");
int clientNumber = 0;
// Mở một ServerSocket tại cổng 7777.
// Chú ý bạn không thể chọn cổng nhỏ hơn 1023 nếu không là người dùng
// đặc quyền (privileged users (root)).
try {
listener = new ServerSocket(7777);
} catch (IOException e) {
System.out.println(e);
System.exit(1);
}
try {
while (true) {
// Chấp nhận một yêu cầu kết nối từ phía Client.
// Đồng thời nhận được một đối tượng Socket tại server.
Socket socketOfServer = listener.accept();
new ServiceThread(socketOfServer, clientNumber++).start();
}
} finally {
listener.close();
}
}
private static void log(String message) {
System.out.println(message);
}
private static class ServiceThread extends Thread {
private int clientNumber;
private Socket socketOfServer;
public ServiceThread(Socket socketOfServer, int clientNumber) {
this.clientNumber = clientNumber;
this.socketOfServer = socketOfServer;
// Log
log("New connection with client# " + this.clientNumber + " at " + socketOfServer);
}
@Override
public void run() {
try {
// Mở luồng vào ra trên Socket tại Server.
BufferedReader is = new BufferedReader(new InputStreamReader(socketOfServer.getInputStream()));
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(socketOfServer.getOutputStream()));
while (true) {
// Đọc dữ liệu tới server (Do client gửi tới).
String line = is.readLine();
// Ghi vào luồng đầu ra của Socket tại Server.
// (Nghĩa là gửi tới Client).
os.write(">> " + line);
// Kết thúc dòng
os.newLine();
// Đẩy dữ liệu đi
os.flush();
// Nếu người dùng gửi tới QUIT (Muốn kết thúc trò chuyện).
if (line.equals("QUIT")) {
os.write(">> OK");
os.newLine();
os.flush();
break;
}
}
} catch (IOException e) {
System.out.println(e);
e.printStackTrace();
}
}
}
}
ClientDemo.java
package org.o7planning.tutorial.socketthread;
import java.io.*;
import java.net.*;
import java.util.Date;
public class ClientDemo {
public static void main(String[] args) {
// Địa chỉ máy chủ.
final String serverHost = "localhost";
Socket socketOfClient = null;
BufferedWriter os = null;
BufferedReader is = null;
try {
// Gửi yêu cầu kết nối tới Server đang lắng nghe
// trên máy 'localhost' cổng 7777.
socketOfClient = new Socket(serverHost, 7777);
// Tạo luồng đầu ra tại client (Gửi dữ liệu tới server)
os = new BufferedWriter(new OutputStreamWriter(socketOfClient.getOutputStream()));
// Luồng đầu vào tại Client (Nhận dữ liệu từ server).
is = new BufferedReader(new InputStreamReader(socketOfClient.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host " + serverHost);
return;
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " + serverHost);
return;
}
try {
// Ghi dữ liệu vào luồng đầu ra của Socket tại Client.
os.write("HELO! now is " + new Date());
os.newLine(); // kết thúc dòng
os.flush(); // đẩy dữ liệu đi.
os.write("I am Tom Cat");
os.newLine();
os.flush();
os.write("QUIT");
os.newLine();
os.flush();
// Đọc dữ liệu trả lời từ phía server
// Bằng cách đọc luồng đầu vào của Socket tại Client.
String responseLine;
while ((responseLine = is.readLine()) != null) {
System.out.println("Server: " + responseLine);
if (responseLine.indexOf("OK") != -1) {
break;
}
}
os.close();
is.close();
socketOfClient.close();
} catch (UnknownHostException e) {
System.err.println("Trying to connect to unknown host: " + e);
} catch (IOException e) {
System.err.println("IOException: " + e);
}
}
}
Java cơ bản
- Tùy biến trình biên dịch java xử lý Annotation của bạn (Annotation Processing Tool)
- Lập trình Java theo nhóm sử dụng Eclipse và SVN
- Hướng dẫn và ví dụ Java WeakReference
- Hướng dẫn và ví dụ Java PhantomReference
- Hướng dẫn nén và giải nén trong Java
- Cấu hình Eclipse để sử dụng JDK thay vì JRE
- Phương thức String.format() và printf() trong Java
- Cú pháp và các tính năng mới trong Java 5
- Cú pháp và các tính năng mới trong Java 8
- Hướng dẫn sử dụng biểu thức chính quy trong Java
- Hướng dẫn lập trình đa luồng trong Java - Java Multithreading
- Thư viện điều khiển các loại cơ sở dữ liệu khác nhau trong Java
- Hướng dẫn sử dụng Java JDBC kết nối cơ sở dữ liệu
- Lấy các giá trị của các cột tự động tăng khi Insert một bản ghi sử dụng JDBC
- Hướng dẫn và ví dụ Java Stream
- Functional Interface trong Java
- Giới thiệu về Raspberry Pi
- Hướng dẫn và ví dụ Java Predicate
- Abstract class và Interface trong Java
- Access modifier trong Java
- Hướng dẫn và ví dụ Java Enum
- Hướng dẫn và ví dụ Java Annotation
- So sánh và sắp xếp trong Java
- Hướng dẫn và ví dụ Java String, StringBuffer và StringBuilder
- Hướng dẫn xử lý ngoại lệ trong Java - Java Exception Handling
- Hướng dẫn và ví dụ Java Generics
- Thao tác với tập tin và thư mục trong Java
- Hướng dẫn và ví dụ Java BiPredicate
- Hướng dẫn và ví dụ Java Consumer
- Hướng dẫn và ví dụ Java BiConsumer
- Bắt đầu với Java cần những gì?
- Lịch sử của Java và sự khác biệt giữa Oracle JDK và OpenJDK
- Cài đặt Java trên Windows
- Cài đặt Java trên Ubuntu
- Cài đặt OpenJDK trên Ubuntu
- Cài đặt Eclipse
- Cài đặt Eclipse trên Ubuntu
- Học nhanh Java cho người mới bắt đầu
- Lịch sử của bit và byte trong khoa học máy tính
- Các kiểu dữ liệu trong Java
- Các toán tử Bitwise
- Câu lệnh rẽ nhánh (if else) trong Java
- Câu lệnh rẽ nhánh switch trong Java
- Vòng lặp trong Java
- Mảng (Array) trong Java
- JDK Javadoc định dạng CHM
- Thừa kế và đa hình trong Java
- Hướng dẫn và ví dụ Java Function
- Hướng dẫn và ví dụ Java BiFunction
- Ví dụ về Java encoding và decoding sử dụng Apache Base64
- Hướng dẫn và ví dụ Java Reflection
- Hướng dẫn gọi phương thức từ xa với Java RMI
- Hướng dẫn lập trình Java Socket
- Các nền tảng nào bạn nên chọn để lập trình ứng dụng Java Desktop?
- Hướng dẫn và ví dụ Java Commons IO
- Hướng dẫn và ví dụ Java Commons Email
- Hướng dẫn và ví dụ Java Commons Logging
- Tìm hiểu về Java System.identityHashCode, Object.hashCode và Object.equals
- Hướng dẫn và ví dụ Java SoftReference
- Hướng dẫn và ví dụ Java Supplier
- Lập trình Java hướng khía cạnh với AspectJ (AOP)
Show More
- Hướng dẫn lập trình Java Servlet/JSP
- Các hướng dẫn Java Collections Framework
- Java API cho HTML & XML
- Các hướng dẫn Java IO
- Các hướng dẫn Java Date Time
- Các hướng dẫn Spring Boot
- Các hướng dẫn Maven
- Các hướng dẫn Gradle
- Các hướng dẫn Java Web Services
- Các hướng dẫn lập trình Java SWT
- Các hướng dẫn lập trình JavaFX
- Các hướng dẫn Java Oracle ADF
- Các hướng dẫn Struts2 Framework
- Các hướng dẫn Spring Cloud