基於TCP協定網路socket程式設計(java實現C/S通訊)

2020-10-25 14:01:21

目錄

零、1024特別禮物

一、前言:TCP原理簡介

二、Socket程式設計通訊

三、TCP伺服器端(具體程式碼)

四、TCP使用者端(具體程式碼)

五、通訊效果演示

六、「創意」機器人:價值一個億的AI核心程式碼(具體程式碼)

七、最後


零、1024特別禮物

1024,1GB,一級棒,加油!

一、前言:TCP原理簡介

首先,保證文章完整性,TCP的理論原理還是需要簡介一下,略顯枯燥๑乛◡乛๑。

TCP(傳輸控制協定,Transmission Control Protocol)是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協定。TCP旨在適應支援多網路應用的分層協定層次結構。也就是說,TCP是為了在不可靠的網際網路絡上提供可靠的端到端位元組流而專門設計的一個傳輸協定。 連線到不同但互連的計算機通訊網路的主計算機中的成對程序之間依靠TCP提供可靠的通訊服務。

以上TCP的特點,也正是與UDP的明顯不同之處。UDP(使用者資料包協定)是一種無連線的、不可靠的、不以位元組流傳輸通訊協定。具體區別可對比之前這篇文章:

基於UDP協定網路Socket程式設計(java實現C/S通訊案例) 】 [https://blog.csdn.net/Charzous/article/details/109016215]

接著,「三次握手」則是眾所周知的一個詞,是建立TCP連線的重要過程。許多文章有詳細解讀,本篇則是詳細記錄在此原理之上,使用Java實現TCP的Socket網路通訊,包含C/S軟體架構的程式設計,偏向實踐,更加有趣!

二、Socket程式設計通訊

本篇使用Java進行Socket程式設計,Java的TCP/IP通訊端程式設計將底層的細節進行了封裝,其程式設計模型如圖:

我們自頂向下觀察,基於TCP的通訊,必然有伺服器端Server和使用者端Client。

首先,建立連線。兩端分別有一個通訊端Socket,用於兩者之間的通訊。使用者端向伺服器傳送請求,建立socket進行連線。伺服器端則隨時監聽使用者端發起的請求,接收並建立裂解Socket。

其次,開始通訊。服務和客戶兩端的輸入輸出流互相通訊。邏輯上可理解為通訊程序的雙方具有兩個流(輸出流和輸入流)。邏輯上可將兩個流理解為兩個通訊管道的全雙工通訊模式,一個用於向對方傳送資料,另一個用於接收對方的資料。

最後,結束通訊。使用者端存取伺服器結束,斷開連線,關閉Socket和相關資源(輸入輸出流等)。伺服器端監聽使用者端狀態,同時關閉Socket等連線。

建立通訊規則:

Server和Client之間需要約定相同的規則,保證正常通訊。之後的程式設計,我們約定:

  1. 使用者端連線伺服器,連線成功後,伺服器首先給使用者端傳送一條歡迎資訊;

  2. 使用者端程式每傳送一條資訊給伺服器,伺服器接收並回送該資訊到使用者端,使用者端接收並顯示該資訊;

  3. 當用戶端傳送"bye",則結束對話。

三、TCP伺服器端(具體程式碼)

第一步,建立伺服器端通訊端。

類成員變數:ServerSocket serverSocket,監聽埠號port;

    private int port =8008;//伺服器監聽視窗
    private ServerSocket serverSocket;//定義伺服器通訊端

    public TCPServer() throws IOException{
        serverSocket =new ServerSocket(port);
        System.out.println("伺服器啟動監聽在"+port+"埠...");

    }

第二步,定義輸入輸出流方法:

    private PrintWriter getWriter(Socket socket) throws IOException{
        //獲得輸出流緩衝區的地址
        OutputStream socketOut=socket.getOutputStream();
        //網路流寫出需要使用flush,這裡在printWriter構造方法直接設定為自動flush
        return new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);
    }

    private BufferedReader getReader(Socket socket) throws IOException{
        //獲得輸入流緩衝區的地址
        InputStream socketIn=socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketIn,"utf-8"));
    }

 第三步,伺服器端核心:

//單客戶版本,每次只能與一個使用者建立通訊連線
public void Service(){
    while (true){
        Socket socket=null;
        try {
            //此處程式阻塞,監聽並等待使用者發起連線,有連線請求就生成一個通訊端
            socket=serverSocket.accept();

            //本地伺服器控制檯顯示客戶連線的使用者資訊
            System.out.println("New connection accepted:"+socket.getInetAddress());
            BufferedReader br=getReader(socket);//字串輸入流
            PrintWriter pw=getWriter(socket);//字串輸出流
            pw.println("來自伺服器訊息:歡迎使用本服務!");

            String msg=null;
            //此處程式阻塞,每次從輸入流中讀入一行字串
            while ((msg=br.readLine())!=null){
                //如果使用者傳送資訊為」bye「,就結束通訊
                if(msg.equals("bye")){
                    pw.println("來自伺服器訊息:伺服器斷開連線,結束服務!");
                    System.out.println("使用者端離開。");
                    break;
                }
                pw.println("來自伺服器訊息:"+msg);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                if (socket!=null)
                    socket.close();//關閉socket連線以及相關的輸入輸出流
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

 程式碼關鍵解析很清楚易懂。可以看到,伺服器端提供服務放到了一個While(true)裡面,這是因為伺服器程式需要一直執行,所以處理程式碼一般放在while(true)這種無限迴圈中,TCPServer執行一次,且自身不能終止執行,要終止它執行,只能通過強制方式(如在IDE環境強制關閉)。

四、TCP使用者端(具體程式碼)

第一步,建立使用者端通訊端,定義類構造方法,實現輸入輸出流。

    private Socket socket;

    private PrintWriter pw;
    private BufferedReader br;

    public TCPClient(String ip, String port) throws IOException{
        //主動向伺服器發起連線,實現TCP三次握手
        //不成功則丟擲錯誤,由呼叫者處理錯誤
        socket =new Socket(ip,Integer.parseInt(port));

        //得到網路流輸出位元組流地址,並封裝成網路輸出字元流
        OutputStream socketOut=socket.getOutputStream();
        //引數true表示自動flush資料
        pw=new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);

        //得到網路輸入位元組流地址,並封裝成網路輸入字元流
        InputStream socketIn=socket.getInputStream();
        br=new BufferedReader(new InputStreamReader(socketIn,"utf-8"));

    }

第二步,實現網路通訊傳送和接收方法。

    public void send(String msg){
        //輸出字元流,由socket呼叫系統底層函數,經網路卡傳送位元組流
        pw.println(msg);
    }

    public String receive(){
        String msg=null;
        try {
            //從網路輸入字元流中讀取資訊,每次只能接受一行資訊
            //不夠一行時(無行結束符),該語句阻塞
            //直到條件滿足,程式往下執行
            msg=br.readLine();
        }catch (IOException e){
            e.printStackTrace();
        }
        return msg;
    }

 第三步,定義網路連線關閉方法供外部呼叫。

    public void close(){
        try {
            if (socket!=null)
                socket.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

 TCP連線的釋放也有「四次握手」一說,必須經過2MSL後才真正釋放。具體過程如下圖:

五、通訊效果演示

GIF動圖演示:

六、「創意」機器人:價值一個億的AI核心程式碼(具體程式碼)

這部分我們要實現「聊天機器人」,效果這樣:

是不是迫不及待想知道如何實現呢!堪稱「價值一個億的AI核心程式碼」!!??

就這樣實現了!

 不賣關子了,就一行程式碼!

msg=msg.replace("?","!").replace("?","!").replace("嗎","").replace("嗎?","");

 具體想實現機器人如何回覆可以自行調整程式碼。

七、最後

本篇則是詳細記錄在此原理之上,使用Java實現TCP的Socket網路通訊,包含C/S軟體架構的程式設計,偏向實踐,更加有趣!仔細閱讀的朋友可以發現,在伺服器端核心部分,有一行註釋說明了該程式只支援單使用者,也就是單執行緒通訊,可以嘗試一下,如果再開一個使用者端連線該服務,是否因為單執行緒阻塞程式卡住了。

這個問題關鍵就在於:伺服器和使用者端互相約定通訊規則,否則就可能有問題,例如,如果伺服器在一個使用者端連線成功後,並沒有一條資訊傳送給使用者端,使用者端的讀取歡迎資訊的語句無法讀取到內容,就被阻塞住,由於是單執行緒,甚至整個程式都會被卡住。要解決這個問題,等待更新下一篇!

另外,UI介面的設計可參考上一篇部落格:【基於UDP協定網路Socket程式設計(java實現C/S通訊案例) 】 [https://blog.csdn.net/Charzous/article/details/109016215]


我的CSDN部落格:https://blog.csdn.net/Charzous/article/details/109260488