Java 通過實現簡單的TCP通信程序來理解TCP通信


概述

TCP通信能實現兩台計算機之間的數據交互,通信的兩端,要嚴格區分為客戶端(Client)與服務端(Server)。

兩端通信的步驟

  1. 服務端程序,需要事先啟動,等待客戶端的連接。
  2. 客戶端主動連接服務器端,連接成功才能通信。服務端不可以主動連接客戶端。

在Java中,提供了兩個類用於實現TCP通信程序:

  1. 客戶端:java.net.Socket類表示。創建Socket對象,向服務端發出連接請求,服務端響應請求,兩者建立連接開始通信。
  2. 服務端:java.net.ServerSocket類表示。創建ServerSocket對象,相當於開啟一個服務,並等待客戶端的連接。

如下圖:

img

客戶端1和服務端進行一個通訊的交互①②③④,需要四個IO流對象,客戶端2也是如此。那么兩個客戶端都和同一個服務端進行交互,服務端怎么區分呢?在服務器端有一個accept方法,可以獲取到請求的客戶端對象。

多個客戶端同時和服務器進行交互,就需要使用多個IO流對象。服務器是沒有IO流的,服務器可以獲取到請求的客戶端Socket對象t,使用每個客戶端Socket中提供的IO流和客戶端進行交互。服務器使用客戶端的字節輸入流讀取客戶端發送的數據,使用客戶端的字節輸出流給客戶端回寫數據。簡單的說,就是服務器使用客戶端的流和客戶端交互。

Socket類

Socket類:該類實現客戶端套接字,套接字指的是兩台設備之間通訊的端點。

構造方法

public Socket(String host, int port)
// 創建套接字對象(兩台設備之間通訊的端點對象)並將其連接到指定主機上的指定端
// 口號。如果指定的host是null,則相當於指定地址為回送地址。
  1. 套接字:包含了IP地址(host)和端口號(port)的網絡單位。

  2. 回送地址:回送地址(127.x.x.x)是本機回送地址(Loopback Address),主要用於網絡軟件測試以及本地機進程間通信,無論什么程序,一旦使用回送地址發送數據,立即返回,不進行任何網絡傳輸。

成員方法

OutputStream getOutputStream()
// 返回此套接字的輸出流。

InputStream getInputStream()
// 返回此套接字的輸入流

void close()
// 關閉此套接字。

舉例

使用Java程序,進行客戶端和服務端之間的通信。

TCP通訊的客戶端

向服務器發送連接請求,給服務器發送數據,讀取服務器回寫的數據。

實現步驟:

1.創建一個客戶端對象Socket,構造方法中綁定服務器的IP地址和端口號。

2.使用Socket對象中的方法getOutputStream(),獲取網絡字節輸出流OutputStream對象。

3.使用網絡字節輸出流OutputStream對象中的方法write,給服務器發送數據。

4.使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。

5.使用網絡字節輸入流InputStream對象中的方法read,讀取服務器回寫的數據。

6.釋放資源(Socket)。

注意:

1.客戶端和服務器端進行交互,必須使用Socket中提供的網絡流,不能使用自己創建的流對象。

2.當我們創建客戶端對象Socket的時候,就會去請求服務器和服務器經過3次握手建立連接通路。
這時如果服務器沒有啟動,那么就會拋出異常(ConnectException: Connection refused: connect)。
如果服務器已經啟動,那么就可以進行交互了。

客戶端代碼實現:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {
        method();
    }

    private static void method() throws IOException {
        // 創建一個客戶端對象Socket,構造方法中綁定服務器的IP地址和端口號。
        Socket socket = new Socket("127.0.0.1", 8888);

        // 使用Socket對象中的方法getOutputStream(),獲取網絡字節輸出流OutputStream對象。
        OutputStream outputStream = socket.getOutputStream();

        // 使用網絡字節輸出流OutputStream對象中的方法write,給服務器發送數據。
        outputStream.write("你好吖!服務器。".getBytes());

        // 使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。
        InputStream  inputStream = socket.getInputStream();

        // 使用網絡字節輸入流InputStream對象中的方法read,讀取服務器回寫的數據。
        byte[] bytes = new byte[1024];
        int len = inputStream.read(bytes);
        System.out.println(new String(bytes, 0, len));

        // 釋放資源(Socket)。
        socket.close();
    }
}

完成了客戶端代碼的編寫后,先了解一下ServerSocket類,然后編寫服務端Java代碼。

ServerSocket類

ServerSocket類:此類現服務器套接字。服務器等待請求通過網絡傳入。它基於該請求執行某些操作,然后可能向請求者返回結果。

構造方法

ServerSocket(int port)
// 創建綁定到特定端口的服務器套接字。

成員方法

Socket accept()
// 偵聽並接受到此套接字的連接。

服務器端必須明確一件事情,必須的知道是哪個客戶端請求的服務器,所以可以使用accept方法獲取到請求的客戶端對象Socket。

舉例

接收客戶端的請求,讀取客戶端發送的數據,給客戶端回寫數據。

TCP通信的服務端

實現步驟:

1.創建服務器ServerSocket對象,對象中傳遞系統要指定的端口號。

2.使用ServerSocket對象中的方法accept,獲取到請求的客戶端對象Socket。

3.使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。

4.使用網絡字節輸入流InputStream對象中的方法read,讀取客戶端發送的數據。

5.使用Socket對象中的方法getOutputStream()獲取網絡字節輸出流OutputStream對象。

6.使用網絡字節輸出流OutputStream對象中的方法write,給客戶端回寫數據。

7.釋放資源(Socket,ServerSocket)。

服務端代碼實現:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        method();
    }

    private static void method() throws IOException {
        // 創建服務器ServerSocket對象,對象中傳遞系統要指定的端口號。
        ServerSocket serverSocket = new ServerSocket(8888);

        // 使用ServerSocket對象中的方法accept,獲取到請求的客戶端對象Socket。
        Socket socket = serverSocket.accept();

        // 使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。
        InputStream inputStream = socket.getInputStream();

        // 使用網絡字節輸入流InputStream對象中的方法read,讀取客戶端發送的數據。
        byte[] bytes = new byte[1024];
        int len = inputStream.read(bytes);
        System.out.println(new String(bytes, 0, len));

        // 使用Socket對象中的方法getOutputStream()獲取網絡字節輸出流OutputStream對象。
        OutputStream outputStream = socket.getOutputStream();

        // 使用網絡字節輸出流OutputStream對象中的方法write,給客戶端回寫數據。
        outputStream.write("收到,謝謝!".getBytes());

        // 釋放資源(Socket,ServerSocket)。
        socket.close();
        serverSocket.close();
    }
}

客戶端程序和服務端程序都已經編寫完成,先運行服務端程序,然后再運行客戶端程序。

客戶端程序:控制台輸出(收到來自服務端回寫的數據)

收到,謝謝您!客戶端。

服務端程序:控制台輸出(接收到客戶端發來的請求數據)

你好吖!服務器。

輸出截圖如下:

img

img

客戶端程序和服務端程序對比(4K圖,可放大):
img

代碼實現步驟:

  1. 第一步

    服務器端:
    
    // 創建服務器ServerSocket對象,對象中傳遞系統要指定的端口號。
    ServerSocket serverSocket = new ServerSocket(8888);
    
    // 使用ServerSocket對象中的方法accept,獲取到請求的客戶端對象Socket。
    Socket socket = serverSocket.accept();
    
  2. 第二步

    客戶端:
    
    // 創建一個客戶端對象Socket,構造方法中綁定服務器的IP地址和端口號。
    Socket socket = new Socket("127.0.0.1", 8888);
    
    // 使用Socket對象中的方法getOutputStream(),獲取網絡字節輸出流OutputStream對象。
    OutputStream outputStream = socket.getOutputStream();
    
    // 使用網絡字節輸出流OutputStream對象中的方法write,給服務器發送數據。
    outputStream.write("你好吖!服務器。".getBytes());
    
  3. 第三步

    服務器端:
    
    // 使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。
    InputStream inputStream = socket.getInputStream();
    
    // 使用網絡字節輸入流InputStream對象中的方法read,讀取客戶端發送的數據。
    byte[] bytes = new byte[1024];
    int len = inputStream.read(bytes);
    System.out.println(new String(bytes, 0, len));
    
    // 使用Socket對象中的方法getOutputStream()獲取網絡字節輸出流OutputStream對象。
    OutputStream outputStream = socket.getOutputStream();
    
    // 使用網絡字節輸出流OutputStream對象中的方法write,給客戶端回寫數據。
    outputStream.write("收到,謝謝您!客戶端。".getBytes());
    
  4. 第四步

    客戶端:
    
    // 使用Socket對象中的方法getInputStream()獲取網絡字節輸入流InputStream對象。
    InputStream  inputStream = socket.getInputStream();
    
    // 使用網絡字節輸入流InputStream對象中的方法read,讀取服務器回寫的數據。
    byte[] bytes = new byte[1024];
    int len = inputStream.read(bytes);
    System.out.println(new String(bytes, 0, len));
    
  5. 第五步

    客戶端:
    
    // 釋放資源(Socket)。
    socket.close();
    
  6. 第六步

    服務器端:
    
    // 釋放資源(Socket,ServerSocket)。
    socket.close();
    serverSocket.close();
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM