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.


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ủ.

Về phía, nếu liên kết được gật đầu, một ổ cắm được tạo thành công xuất sắc và Client hoàn toàn có thể sử dụng ổ cắm để tiếp xúc với chương trình chủ .Các Client và Server hoàn toàn có thể tiếp xúc 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.

Definition

Mộ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);
       }
   }

}