「java」 TCP編程

                         TCP編程

首先需要了解TCP的連接過程,然後使用socket編程實現。TCP分爲客戶端和服務器端編程。下面都會介紹。

但是首先會先介紹下TCP連接的整體概念,其實新手可以先連接整個過程,然後在細緻的分析其中各個類的函數怎麼用啊,有什麼參數啊。當然可以先寫一個程序,跑起來,然後在細緻的扣含義哈。

在看下面代碼之前,建議明白什麼java的Thread類如何使用,和I/O通訊哈!

1. TCP建立鏈接的步驟

1.服務端開放一個port,被動地等待客戶端的連接。 
2.客戶端向服務器端發送連接請求,需要通過該port,然後被動地等待服務器的響應, 通過I/O流與服務端通信 ,當然只有在服務器開啓的時候纔可能連接上。
3.當客戶端向服務端發出連接請求後,並被服務器正確接受並相應後,服務端創建一個sockt實例,併爲該實例創建一個新的線程,使用I/O 流與客戶端進行通訊

    public void client() throws Exception {
        Socket socket = new Socket(InetAddress.getLocalHost(), 8090);
        // InetAddress.getLocalHost()爲客戶端請求連接的主機號,此處設置爲本地主機,服務進程的端口號是8090
        // 主機號和端口號唯一確定了唯一主機上面的唯一進程。
        OutputStream os = socket.getOutputStream();
        // socket.getOutputStream()獲得輸出流,通過輸出流像主機發送數據。
        os.write("黑貓呼叫白貓收到請回復!".getBytes());
        socket.shutdownOutput();
        // 關閉數據輸出,如果不關閉的話服務端並不知道數據傳輸已經結束還會一直等待。
        InputStream is = socket.getInputStream();
        int len = 0;
        byte[] b = new byte[1024];
        while ((len = is.read(b)) != -1) {
            String str = new String(b, 0, len);
            System.out.println(str);
        }
        is.close();
        os.close();
        socket.close();
    }
@Test
    public void server() throws Exception {
        ServerSocket ss = new ServerSocket(8090);
        // 給服務端一個端口號8090使得客戶端可以連接。
        Socket socket = ss.accept();
        // 接受客戶端的連接
        InputStream is = socket.getInputStream();
        // 獲得客戶端的輸入流
        int len = 0;
        byte[] b = new byte[1024];
        while ((len = is.read(b)) != -1) {
            String str = new String(b, 0, len);
            System.out.println(str);
        }
        OutputStream os = socket.getOutputStream();
        // 通過輸出流向客戶端發送數據。
        os.write("黑貓這裏是白貓,我已收到你的呼叫!".getBytes());
        os.close();
        // socket.shutdownOutput();
        is.close();
        socket.close();
        ss.close();
    }

 

2. 服務器處理多客戶端請求時

在TCP Socket編程中,客戶端有多個,而服務器端只有一個。

  • 由客戶端TCP向服務器端TCP發送連接請求,服務器端的ServerSocket實例則監聽來自客戶端的TCP連接請求,併爲每個請求創建新的Socket實例。因此要爲每個Socket連接開啓一個線程。(P14)
  • 由於服務端在調用accept()等待客戶端的連接請求時會阻塞直到收到客戶端發送的連接請求才會繼續往下執行代碼
  • 服務器端要同時處理ServerSocket實例和Socket實例,而客戶端只需要使用Socket實例。 
  • 每個Socket實例會關聯一個InputStream和OutputStream對象,通過OutputStream來發送數據,並通過從InputStream來接收數據。. 

簡單來說,使用TCP方式進行網絡通訊時,需要建立專門的虛擬連接,然後進行可靠的數據傳輸,如果數據發送失敗,則客戶端會自動重發該數據 。由於TCP需要建立專用的虛擬連接以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產生的數據量要比UDP稍微大一些。 
 

2.1.TCP客戶端創建步驟

  1. 創建一個Socket實例 指定遠程主機的ip和端口,建立一個TCP連接
  2. 通過I/O流與服務端通信
  3. 當不需要連接的時候,使用Socket類的close來關閉連接(當通信結束,可以使用Socket的close()方法關閉該客戶端連接)

具體代碼實現如下:

/**
     *
     * @param serverIp  服務端 IP
     * @param serverPort 服務端端口
     * @param msg 發生的消息
     * @throws IOException
     */
    public static void client(String serverIp, int serverPort, String msg) throws IOException {
        String response = null;
        //與端口進行連接
        Socket socket = new Socket(serverIp, serverPort);
        socket.setSoTimeout(5000);
        PrintStream out = new PrintStream(socket.getOutputStream());
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out.println(msg);
        try {
            response = bufferedReader.readLine();
            System.out.println("接收到服務端消息:"+response);
        } catch (SocketTimeoutException e) {
            System.out.println("連接超時,無響應");
        }
        if (socket != null) {
            //如果構造函數建立起了連接,則關閉套接字,如果沒有建立起連接,自然不用關閉
            socket.close(); //只關閉socket,其關聯的輸入輸出流也會被關閉
        }
    }

 

2.2 TCP服務端創建步驟:

創建一個ServerSocket實例並指定本地端口,用來監聽客戶端在該端口發送的TCP連接請求。當接收客戶端TCP 請求後

  1. 調用ServerSocket的accept()方法以獲取客戶端連接,並通過其返回值創建一個Socket實例
  2. 爲返回的Socket實例開啓新的線程,並使用Socket實例的I/O流與客戶端通信
  3. 當連接終止後,調用ServerSocket close 方法,關閉服務端 

具體實現代碼:

/**
 * 這個類主要是爲了用來創建Sockt 實例
 */
public class TcpServerThread implements Runnable {
    private Socket socketClient = null;
    String responseToClient = "你好客戶端";

    public TcpServerThread(Socket socketClient) {
        this.socketClient = socketClient;
    }
    public void run() {
        try {
            //獲取Socket的輸出流,用來向客戶端發送數據
            PrintStream out = new PrintStream(socketClient.getOutputStream());
            //獲取Socket的輸入流,用來接收從客戶端發送過來的數據
            BufferedReader buf = new BufferedReader(new InputStreamReader(socketClient.getInputStream()));
            boolean flag = true;
            while (flag) {
                //接收從客戶端發送過來的數據
                String str = buf.readLine();
                if (str == null || "".equals(str)) {
                    flag = false;
                } else {
                    //將接收到的字符串前面加上echo,發送到對應的客戶端
                    System.out.println("接受到客戶端消息客戶端消息:" + str);
                    out.println(":" + responseToClient);
                }
            }
            out.close();
            socketClient.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 /**
     *
     * @param port 服務器端口
     * @throws Exception
     */
    public static void tcpServer(int port)throws  Exception{
        ServerSocket serverSocket =new ServerSocket(port);
        Socket socketClient=null;
        boolean flag=true;
        while (flag){
            socketClient=serverSocket.accept();
            System.out.println("與客戶端連接成功");
            new Thread(new TcpServerThread(socketClient)).start();
        }
        serverSocket.close();
    }

 

當然跑代碼的時候,肯定是先跑server,否則client的連接肯定會出錯哈,然後一定注意其中異常的拋出,還有使用完成後,需要對socket進行關閉什麼的問題。

 

參考:https://blog.csdn.net/u013309870/article/details/52121198

https://blog.csdn.net/github_38151745/article/details/79045713

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章