国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)

這篇具有很好參考價(jià)值的文章主要介紹了Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

?Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)

??????點(diǎn)進(jìn)來你就是我的人了
博主主頁:??????戳一戳,歡迎大佬指點(diǎn)!

人生格言:當(dāng)你的才華撐不起你的野心的時(shí)候,你就應(yīng)該靜下心來學(xué)習(xí)!

歡迎志同道合的朋友一起加油喔??????
目標(biāo)夢(mèng)想:進(jìn)大廠,立志成為一個(gè)牛掰的Java程序猿,雖然現(xiàn)在還是一個(gè)??嘿嘿
謝謝你這么帥氣美麗還給我點(diǎn)贊!比個(gè)心

Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)


目錄

一.Socket概述

??Socket通信是有兩種方式的:TCP和UDP

TCP與UDP區(qū)別

socket之send和recv原理剖析

二. TCP通信客戶端Socket

三. TCP通信服務(wù)器端ServerSocket

四.基于TCP的Socket通信

五.UDP相關(guān)類DatagramPacket類和DatagramSocket類

數(shù)據(jù)包類DatagramPacket

發(fā)送數(shù)據(jù)包類DatagramSocket

InetAddress類(無構(gòu)造方法)

六.基于UDP的Socket通信

七. TCP和UCP的緩沖區(qū)

1.TCP的緩沖區(qū)

2.UDP的緩沖區(qū)



一.Socket概述

? Socket(套接字),是網(wǎng)絡(luò)上兩個(gè)程序之間實(shí)現(xiàn)數(shù)據(jù)交換的一端,它既可以發(fā)送請(qǐng)求,也可以接受請(qǐng)求,一個(gè)Socket由一個(gè)IP地址和一個(gè)端口號(hào)唯一確定,利用Socket能比較方便的實(shí)現(xiàn)兩端(服務(wù)端和客戶端)的網(wǎng)絡(luò)通信。

? 在Java中,有專門的Socket類來處理用戶請(qǐng)求和響應(yīng),學(xué)習(xí)使用Socket類方法,就可以實(shí)現(xiàn)兩臺(tái)機(jī)器之間通信。

??Socket通信是有兩種方式的:TCP和UDP

? TCP通信:客戶端提供了java.net.Socket類,服務(wù)器端提供了java.net.ServerSocket類。

? UDP通信:UDP通信不建立邏輯連接,使用DatagramPacket類打包數(shù)據(jù)包,使用DatagramSocket類發(fā)送數(shù)據(jù)包。

TCP與UDP區(qū)別

  • TCP面向連接;UDP是無連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接。
  • TCP提供可靠的服務(wù)。也就是說,通過TCP連接傳送的數(shù)據(jù),無差錯(cuò),不丟失,不重復(fù),且按序到達(dá);UDP盡最大努力交付,即不保證可靠交付。
  • UDP具有較好的實(shí)時(shí)性,工作效率比TCP高,適用于對(duì)高速傳輸和實(shí)時(shí)性有較高的通信或廣播通信。
  • TCP對(duì)系統(tǒng)資源要求較多,UDP對(duì)系統(tǒng)資源要求較少。
  • TCP面向字節(jié)流;UDP面向數(shù)據(jù)報(bào),一次發(fā)送/接收都必須是完整的一個(gè)數(shù)據(jù)報(bào)或者多個(gè)數(shù)據(jù)報(bào),不能是半個(gè)數(shù)據(jù)報(bào),兩者都是全雙工,支持雙向通信

Socket通信模型如下圖:

Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)?

socket之send和recv原理剖析

當(dāng)創(chuàng)建一個(gè)TCP socket對(duì)象的時(shí)候會(huì)有一個(gè)發(fā)送緩沖區(qū)和一個(gè)接收緩沖區(qū),這個(gè)發(fā)送和接收緩沖區(qū)指的就是內(nèi)存中的一片空間。

send原理剖析

send發(fā)數(shù)據(jù),必須得通過網(wǎng)卡發(fā)送數(shù)據(jù),應(yīng)用程序是無法直接通過網(wǎng)卡發(fā)送數(shù)據(jù)的,它需要調(diào)用操作系統(tǒng)接口,也就是說,應(yīng)用程序把發(fā)送的數(shù)據(jù)先寫入到發(fā)送緩沖區(qū)(內(nèi)存中的一片空間),再由操作系統(tǒng)控制網(wǎng)卡把發(fā)送緩沖區(qū)的數(shù)據(jù)發(fā)送給服務(wù)端網(wǎng)卡。

recv原理剖析

應(yīng)用軟件是無法直接通過網(wǎng)卡接收數(shù)據(jù)的,它需要調(diào)用操作系統(tǒng)接口,由操作系統(tǒng)通過網(wǎng)卡接收數(shù)據(jù),把接收的數(shù)據(jù)寫入到接收緩沖區(qū)(內(nèi)存中的一片空間),應(yīng)用程序再從接收緩存區(qū)獲取客戶端發(fā)送的數(shù)據(jù)。

二. TCP通信客戶端Socket

? Java中專門用來實(shí)現(xiàn)Socket客戶端的類就叫Socket,這個(gè)類實(shí)現(xiàn)了客戶端套接字,用于向服務(wù)器發(fā)出連接請(qǐng)求等。

  • 構(gòu)造方法

    Socket(String host, int port):創(chuàng)建一個(gè)流套接字并將其連接到指定IP地址的指定端口號(hào)。

    如果host為null,則相當(dāng)于指定地址為回送地址。

    ?127.x.x.x是本機(jī)的回送地址,即主機(jī)IP堆棧內(nèi)部的IP地址,主要用于網(wǎng)絡(luò)軟件測(cè)試以及本地機(jī)進(jìn)程間通信,無論什么程序,一旦使用回送地址發(fā)送數(shù)據(jù),協(xié)議軟件立即返回之,不進(jìn)行任何網(wǎng)絡(luò)傳輸。

  • 主要方法:

    • InputStream getInputStream():返回此套接字的輸入流。

      關(guān)閉生成的InputStream也將關(guān)閉相關(guān)的Socket。

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

      關(guān)閉生成的OutputStream也將關(guān)閉相關(guān)的Socket。

    • void close():關(guān)閉此套接字

三. TCP通信服務(wù)器端ServerSocket

? Java中專門用來建立Socket服務(wù)器的類叫ServerSocket,這個(gè)類實(shí)現(xiàn)了服務(wù)器套接字,該對(duì)象等待通過網(wǎng)絡(luò)的請(qǐng)求。

  • 構(gòu)造方法:

    ServerSocket(int port):創(chuàng)建綁定到特定端口的服務(wù)器套接字。

  • 主要方法:

    • Socket accept():監(jiān)聽并接受連接,返回一個(gè)新的Socket對(duì)象,用于和客戶端通信,該方法會(huì)一直阻塞直到建立連接。

    • void close():關(guān)閉此套接字。

四.基于TCP的Socket通信

  1. 步驟分析:

    • 服務(wù)端先啟動(dòng),創(chuàng)建ServerSocket對(duì)象,等待連接。
    • 客戶端啟動(dòng),創(chuàng)建Socket對(duì)象,請(qǐng)求連接。
    • 服務(wù)器端接收請(qǐng)求,調(diào)用accept方法,并返回一個(gè)Socket對(duì)象,連接成功
    • 客戶端的Socket對(duì)象通過調(diào)用getOutputStream()方法獲取OutputStream對(duì)象,并使用write()方法將數(shù)據(jù)寫入到發(fā)送緩沖區(qū)。隨后,通過調(diào)用flush()方法確保數(shù)據(jù)已被發(fā)送出去
    • 服務(wù)器端Socket對(duì)象通過調(diào)用getInputStream()方法獲取與該socket關(guān)聯(lián)的InputStream實(shí)例,然后使用read()方法從接收緩沖區(qū)中讀取數(shù)據(jù)。
    • 客戶端釋放資源,斷開連接。

服務(wù)器端:

public class TcpEchoServer {
    // serverSocket 就是外場(chǎng)拉客的小哥
    // clientSocket 就是內(nèi)場(chǎng)服務(wù)的小姐姐.
    // serverSocket 只有一個(gè). clientSocket 會(huì)給每個(gè)客戶端都分配一個(gè)~
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("服務(wù)器啟動(dòng)!");
        while (true) {
            Socket clientSocket = serverSocket.accept();
            //服務(wù)器的主線程(main 線程)負(fù)責(zé)運(yùn)行 while 循環(huán),用于接收客戶端的連接請(qǐng)求。
            // 與此同時(shí),針對(duì)每個(gè)接收到的連接請(qǐng)求,都會(huì)創(chuàng)建一個(gè)新線程處理與該客戶端的數(shù)據(jù)通信。這些新線程與主線程是并發(fā)執(zhí)行的。
            //由于主線程和新創(chuàng)建的線程并發(fā)執(zhí)行,服務(wù)器可以在處理一個(gè)客戶端連接的同時(shí),繼續(xù)接收其他客戶端的連接請(qǐng)求。
            // 這使得服務(wù)器可以并發(fā)處理多個(gè)客戶端連接,提高了服務(wù)器的處理能力。
            // 創(chuàng)建新的線程, 用新線程來調(diào)用 processConnection
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();

            //使用線程池
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    // 通過這個(gè)方法來處理一個(gè)連接.
    // 讀取請(qǐng)求
    // 根據(jù)請(qǐng)求計(jì)算響應(yīng)
    // 把響應(yīng)返回給客戶端
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客戶端上線!\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 沒有這個(gè) scanner 和 printWriter, 完全可以!! 但是代價(jià)就是得一個(gè)字節(jié)一個(gè)字節(jié)扣, 找到哪個(gè)是請(qǐng)求的結(jié)束標(biāo)記 \n
            // 不是不能做, 而是代碼比較麻煩.
            // 為了簡單, 把字節(jié)流包包裝成了更方便的字符流~~
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                // 1. 讀取請(qǐng)求
                if (!scanner.hasNext()) {
                    // 讀取的流到了結(jié)尾了 (對(duì)端關(guān)閉了)
                    System.out.printf("[%s:%d] 客戶端下線!\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                // 直接使用 scanner 讀取一段字符串.
                String request = scanner.next();
                // 2. 根據(jù)請(qǐng)求計(jì)算響應(yīng)
                String response = process(request);
                // 3. 把響應(yīng)寫回給客戶端. 不要忘了, 響應(yīng)里也是要帶上換行的.
                printWriter.println(response);
                //數(shù)據(jù)此時(shí)還在緩存區(qū),使用 flush() 方法,可以確保數(shù)據(jù)立即發(fā)送
                printWriter.flush();
                //clientSocket.getInetAddress().toString() 返回客戶端的 IP 地址,clientSocket.getPort() 返回客戶端的端口號(hào),
                // request 是客戶端發(fā)送的請(qǐng)求,response 是服務(wù)器響應(yīng)的數(shù)據(jù)。
                // 通過格式化輸出的方式將這些信息打印出來,方便程序員進(jìn)行調(diào)試和查看。
                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}

在服務(wù)器端我們使用了try-with-resources語句,確保在代碼塊執(zhí)行完畢后自動(dòng)關(guān)閉資源,無論代碼執(zhí)行過程中是否發(fā)生異常。

當(dāng)程序執(zhí)行到try語句塊結(jié)束時(shí),如果resource實(shí)現(xiàn)了AutoCloseableCloseable接口,那么close()方法將被自動(dòng)調(diào)用。

同時(shí)在服務(wù)器端引入了多線程的寫法,保證服務(wù)器能連接多個(gè)客戶端,同時(shí)與多個(gè)客戶端保持通信,具體實(shí)現(xiàn)邏輯如下:

服務(wù)器的主線程(main 線程)負(fù)責(zé)運(yùn)行 while 循環(huán),用于接收客戶端的連接請(qǐng)求。
與此同時(shí),針對(duì)每個(gè)接收到的連接請(qǐng)求,都會(huì)創(chuàng)建一個(gè)新線程處理與該客戶端的數(shù)據(jù)通信。這些新線程與主線程是并發(fā)執(zhí)行的。由于主線程和新創(chuàng)建的線程并發(fā)執(zhí)行,服務(wù)器可以在處理一個(gè)客戶端連接的同時(shí),繼續(xù)接收其他客戶端的連接請(qǐng)求。?這使得服務(wù)器可以并發(fā)處理多個(gè)客戶端連接,提高了服務(wù)器的處理能力。

?當(dāng)然我們也可以引入線程池來優(yōu)化這段代碼:

  1. 線程池中的線程數(shù)量是動(dòng)態(tài)調(diào)整的。
  2. 當(dāng)有新任務(wù)提交時(shí),如果線程池中有空閑線程,那么會(huì)復(fù)用空閑線程來執(zhí)行新任務(wù);如果沒有空閑線程,則會(huì)創(chuàng)建一個(gè)新線程來執(zhí)行新任務(wù)。
  3. 當(dāng)線程池中的線程空閑時(shí)間超過一定時(shí)間(默認(rèn)為 60 秒)時(shí),線程池會(huì)回收這個(gè)空閑線程。

好處:線程池可以復(fù)用已經(jīng)創(chuàng)建的線程,避免了頻繁地創(chuàng)建和銷毀線程所帶來的性能開銷。當(dāng)有新任務(wù)到來時(shí),線程池會(huì)優(yōu)先使用空閑的線程,從而提高系統(tǒng)資源的利用率。

?客戶端:

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIp, int port) throws IOException {
        // 這個(gè)操作相當(dāng)于讓客戶端和服務(wù)器建立 tcp 連接.
        // 這里的連接連上了, 服務(wù)器的 accept 就會(huì)返回.
        socket = new Socket(serverIp, port);
    }

    public void start() {
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            //outputStream 是一個(gè)字節(jié)輸出流,通過 PrintWriter 對(duì)象的構(gòu)造方法將其包裝成字符輸出流,以便能夠方便地寫入字符數(shù)據(jù)。
            PrintWriter printWriter = new PrintWriter(outputStream);
            //scannerFromSocket 對(duì)象是將輸入流對(duì)象包裝成一個(gè) Scanner 對(duì)象,以便能夠方便地讀取輸入流中的數(shù)據(jù)。
            // Scanner 對(duì)象會(huì)自動(dòng)解析和分隔輸入流中的數(shù)據(jù),并將其轉(zhuǎn)換為相應(yīng)的數(shù)據(jù)類型或字符串。
            Scanner scannerFromSocket = new Scanner(inputStream);

            while (true) {
                // 1. 從鍵盤上讀取用戶輸入的內(nèi)容.
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 把讀取的內(nèi)容構(gòu)造成請(qǐng)求, 發(fā)送給服務(wù)器.
                //在 Java 中,可以通過 PrintWriter 對(duì)象的 println() 方法發(fā)送帶有換行符的數(shù)據(jù)包。
                // 該方法會(huì)將指定的字符串添加一個(gè)換行符,并將其發(fā)送到輸出流中。
                printWriter.println(request);
                //數(shù)據(jù)此時(shí)還在緩存區(qū),使用 flush() 方法,可以確保數(shù)據(jù)立即發(fā)送
                printWriter.flush();
                // 3. 從服務(wù)器讀取響應(yīng)內(nèi)容
                //next() 方法會(huì)讀取下一個(gè)標(biāo)記,而不是一行數(shù)據(jù)。標(biāo)記通常是以空格、制表符或換行符為分隔符的單詞或符號(hào)。
                // 因此,在讀取數(shù)據(jù)時(shí),如果數(shù)據(jù)包中只有一個(gè)標(biāo)記,則可以使用 next() 方法讀取該標(biāo)記。
                String response = scannerFromSocket.next();
                // 4. 把響應(yīng)結(jié)果顯示到控制臺(tái)上.
                // request 是客戶端發(fā)送的請(qǐng)求,response 是服務(wù)器響應(yīng)的數(shù)據(jù)。
                // 通過格式化輸出的方式將這些信息打印出來,方便程序員進(jìn)行調(diào)試和查看。
                System.out.printf("req: %s; resp: %s\n", request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

?PrintWriter printWriter = new PrintWriter(outputStream);? ? ? ? ? ?

outputStream 是一個(gè)字節(jié)輸出流,通過 PrintWriter 對(duì)象的構(gòu)造方法將其包裝成字符輸出流,以便能夠方便地寫入字符數(shù)據(jù)。

Scanner scannerFromSocket = new Scanner(inputStream);

scannerFromSocket 對(duì)象是將輸入流對(duì)象包裝成一個(gè) Scanner 對(duì)象,以便能夠方便地讀取輸入流中的數(shù)據(jù)。
Scanner 對(duì)象會(huì)自動(dòng)解析和分隔輸入流中的數(shù)據(jù),并將其轉(zhuǎn)換為相應(yīng)的數(shù)據(jù)類型或字符串。

五.UDP相關(guān)類DatagramPacket類和DatagramSocket類

  1. 數(shù)據(jù)包類DatagramPacket

    • 作用:用來封裝發(fā)送端或接收端要發(fā)送或接收的數(shù)據(jù)。
    • 構(gòu)造方法
      • DatagramPacket(byte[] buf, int length):構(gòu)造DatagramPacket,用來接收長度為length的數(shù)據(jù)包。
      • DatagramPacket(byte[] buf, int length, InetAddress address, int port):構(gòu)造數(shù)據(jù)報(bào)包,用來將長度為length的包發(fā)送到指定主機(jī)上的指定端口號(hào)。
    • 常用方法
      • public int getLength():獲得發(fā)送端實(shí)際發(fā)送的字節(jié)數(shù)或接收端世界接收的字節(jié)數(shù)
      • public int getPort():獲得發(fā)送端或接收端端口號(hào)
  2. 發(fā)送數(shù)據(jù)包類DatagramSocket

    • 作用:用來發(fā)送和接收數(shù)據(jù)包對(duì)象
    • 構(gòu)造方法
      • DatagramSocket():構(gòu)造數(shù)據(jù)報(bào)套接字并將其綁定到本地主機(jī)上任何可用的端口。
      • DatagramSocket(int port):創(chuàng)建數(shù)據(jù)包套接字并將其綁定到本地主機(jī)上指定端口。
    • 常用方法
      • public void send(DatagramPacket p):從此套接字發(fā)送數(shù)據(jù)報(bào)包
      • public void receive(DatagramPacket p):從此套接字接收數(shù)據(jù)報(bào)包
      • public void close():關(guān)閉此數(shù)據(jù)報(bào)套接字
  3. InetAddress類(無構(gòu)造方法)

    • 作用:代表一個(gè)IP地址
    • 靜態(tài)方法
      • public static InetAddress getLocalHost():返回本地主機(jī)
      • public static InetAddress getByName():在給定主機(jī)名的情況下確定主機(jī)的 IP 地址。
    • 普通方法
      • public String getHostName(): 獲取此 IP 地址的主機(jī)名。
      • public String getHostAddress():返回 IP 地址字符串(以文本表現(xiàn)形式)

六.基于UDP的Socket通信

  1. 步驟分析

    • 服務(wù)器端先啟動(dòng),創(chuàng)建DatagramSocket對(duì)象,監(jiān)聽端口,用于接收
    • 服務(wù)器端創(chuàng)建DatagramPacket對(duì)象,打包用于接收的數(shù)據(jù)包
    • 服務(wù)器阻塞等待接收
    • 客戶端啟動(dòng),創(chuàng)建DatagramSocket對(duì)象,監(jiān)聽端口,用于接收
    • 客戶端創(chuàng)建DatagramPacket對(duì)象,打包用于發(fā)送的數(shù)據(jù)包
    • 客戶端發(fā)送數(shù)據(jù),服務(wù)端接收
    • 服務(wù)端接收數(shù)據(jù)后,創(chuàng)建DatagramPacket對(duì)象,打包用于發(fā)送的數(shù)據(jù)包,發(fā)送數(shù)據(jù)
    • 客戶端創(chuàng)建DatagramPacket對(duì)象,打包用于接收的數(shù)據(jù)包,阻塞等待接收
    • 客戶端接收服務(wù)端數(shù)據(jù),斷開連接,釋放資源

Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)

服務(wù)器端:

public class UdpEchoServer {
    // 需要先定義一個(gè) socket 對(duì)象.
    // 通過網(wǎng)絡(luò)通信, 必須要使用 socket 對(duì)象.
    private DatagramSocket socket = null;

    // 綁定一個(gè)端口, 不一定能成功!!
    // 如果某個(gè)端口已經(jīng)被別的進(jìn)程占用了, 此時(shí)這里的綁定操作就會(huì)出錯(cuò).
    // 同一個(gè)主機(jī)上, 一個(gè)端口, 同一時(shí)刻, 只能被一個(gè)進(jìn)程綁定.
    public UdpEchoServer(int port) throws SocketException {
        // 構(gòu)造 socket 的同時(shí), 指定要關(guān)聯(lián)/綁定的端口.
        socket = new DatagramSocket(port);
    }

    // 啟動(dòng)服務(wù)器的主邏輯.
    public void start() throws IOException {
        System.out.println("服務(wù)器啟動(dòng)!");
        while (true) {
            // 每次循環(huán), 要做三件事情:
            // 1. 讀取請(qǐng)求并解析
            //    構(gòu)造空飯盒
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //    食堂大媽給飯盒里盛飯. (飯從網(wǎng)卡上來的)
            socket.receive(requestPacket);
            //    為了方便處理這個(gè)請(qǐng)求, 把數(shù)據(jù)包轉(zhuǎn)成 String
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根據(jù)請(qǐng)求計(jì)算響應(yīng)(此處省略這個(gè)步驟)
            String response = process(request);
            // 3. 把響應(yīng)結(jié)果寫回到客戶端
            //    根據(jù) response 字符串, 構(gòu)造一個(gè) DatagramPacket .
            //    和請(qǐng)求 packet 不同, 此處構(gòu)造響應(yīng)的時(shí)候, 需要指定這個(gè)包要發(fā)給誰.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    // requestPacket 是從客戶端這里收來的. getSocketAddress 就會(huì)得到客戶端的 ip 和 端口
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    // 這個(gè)方法希望是根據(jù)請(qǐng)求計(jì)算響應(yīng).
    // 由于咱們寫的是個(gè) 回顯 程序. 請(qǐng)求是啥, 響應(yīng)就是啥!!
    // 如果后續(xù)寫個(gè)別的服務(wù)器, 不再回顯了, 而是有具體的業(yè)務(wù)了, 就可以修改 process 方法,
    // 根據(jù)需要來重新構(gòu)造響應(yīng).
    // 之所以單獨(dú)列成一個(gè)方法, 就是想讓同學(xué)們知道, 這是一個(gè)服務(wù)器中的關(guān)鍵環(huán)節(jié)!!!
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}

UdpEchoServer(UDP 服務(wù)器):

a. 首先,創(chuàng)建一個(gè) DatagramSocket 對(duì)象,并綁定到指定的端口。這個(gè)端口用于服務(wù)器與客戶端之間的通信。

b. 在服務(wù)器的主循環(huán)中,首先創(chuàng)建一個(gè)空的 DatagramPacket 對(duì)象,用于接收客戶端發(fā)來的請(qǐng)求數(shù)據(jù)。

c. 調(diào)用 socket.receive(requestPacket) 方法接收客戶端發(fā)來的數(shù)據(jù)包。

d. 將收到的數(shù)據(jù)包中的數(shù)據(jù)轉(zhuǎn)換成字符串形式,并調(diào)用 process() 方法生成響應(yīng)。在這個(gè)例子中,響應(yīng)就是原請(qǐng)求。

e. 創(chuàng)建一個(gè)新的 DatagramPacket 對(duì)象,包含響應(yīng)數(shù)據(jù)和客戶端的地址信息。

f. 使用 socket.send(responsePacket) 方法將響應(yīng)數(shù)據(jù)包發(fā)送回客戶端。

g. 打印請(qǐng)求和響應(yīng)信息。

?客戶端:

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    // 客戶端啟動(dòng), 需要知道服務(wù)器在哪里!!
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        // 對(duì)于客戶端來說, 不需要顯示關(guān)聯(lián)端口.
        // 不代表沒有端口, 而是系統(tǒng)自動(dòng)分配了個(gè)空閑的端口.
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        // 通過這個(gè)客戶端可以多次和服務(wù)器進(jìn)行交互.
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 先從控制臺(tái), 讀取一個(gè)字符串過來
            //    先打印一個(gè)提示符, 提示用戶要輸入內(nèi)容
            System.out.print("-> ");
            String request = scanner.next();
            // 2. 把字符串構(gòu)造成 UDP packet, 并進(jìn)行發(fā)送.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            // 3. 客戶端嘗試讀取服務(wù)器返回的響應(yīng)
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            // 4. 把響應(yīng)數(shù)據(jù)轉(zhuǎn)換成 String 顯示出來.
            //這行代碼將接收到的字節(jié)流數(shù)據(jù)按照指定的編碼格式轉(zhuǎn)換成字符串,方便我們查看和處理數(shù)據(jù)。
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);
        udpEchoClient.start();
    }
}

UdpEchoClient(UDP 客戶端):

a. 創(chuàng)建一個(gè) DatagramSocket 對(duì)象,不需要顯式地關(guān)聯(lián)端口,系統(tǒng)會(huì)自動(dòng)分配一個(gè)空閑的端口。

b. 在客戶端的主循環(huán)中,從控制臺(tái)讀取用戶輸入的字符串作為請(qǐng)求。

c. 創(chuàng)建一個(gè) DatagramPacket 對(duì)象,包含請(qǐng)求數(shù)據(jù)、服務(wù)器的 IP 地址和端口信息。

d. 調(diào)用 socket.send(requestPacket) 方法將請(qǐng)求數(shù)據(jù)包發(fā)送給服務(wù)器。

e. 創(chuàng)建一個(gè)空的 DatagramPacket 對(duì)象,用于接收服務(wù)器返回的響應(yīng)數(shù)據(jù)。

f. 調(diào)用 socket.receive(responsePacket) 方法接收服務(wù)器發(fā)來的響應(yīng)數(shù)據(jù)包。

g. 將收到的響應(yīng)數(shù)據(jù)包中的數(shù)據(jù)轉(zhuǎn)換成字符串形式,并打印請(qǐng)求和響應(yīng)信息。

總結(jié):這兩段代碼實(shí)現(xiàn)了一個(gè)簡單的 UDP 回顯服務(wù)器和客戶端??蛻舳藢⒂脩糨斎氲恼?qǐng)求數(shù)據(jù)通過 UDP 協(xié)議發(fā)送給服務(wù)器,服務(wù)器接收到請(qǐng)求后原樣返回響應(yīng),客戶端接收響應(yīng)并打印信息。整個(gè)過程使用無連接的 UDP 協(xié)議進(jìn)行通信。

七. TCP和UCP的緩沖區(qū)

1.TCP的緩沖區(qū)

創(chuàng)建一個(gè)TCP的socket,同時(shí)在內(nèi)核中創(chuàng)建一個(gè) 發(fā)送緩沖區(qū) 和一個(gè) 接收緩沖區(qū);
調(diào)用write時(shí),數(shù)據(jù)會(huì)先寫入發(fā)送緩沖區(qū)中;
如果發(fā)送的字節(jié)數(shù)太長,會(huì)被拆分成多個(gè)TCP的數(shù)據(jù)包發(fā)出;
如果發(fā)送的字節(jié)數(shù)太短,就會(huì)先在緩沖區(qū)里等待,等到緩沖區(qū)長度差不多了,或者其他合適
的時(shí)機(jī)發(fā)送出去;
接收數(shù)據(jù)的時(shí)候,數(shù)據(jù)也是從網(wǎng)卡驅(qū)動(dòng)程序到達(dá)內(nèi)核的接收緩沖區(qū);
然后應(yīng)用程序可以調(diào)用read從接收緩沖區(qū)拿數(shù)據(jù);
另一方面,TCP的另一個(gè)連接,既有發(fā)送緩沖區(qū),也有接收緩沖區(qū),那么對(duì)于這一個(gè)連接,既
可以讀數(shù)據(jù),也可以寫數(shù)據(jù)。這個(gè)概念叫做 全雙工

2.UDP的緩沖區(qū)

UDP只有接收緩沖區(qū),沒有發(fā)送緩沖區(qū):
UDP沒有真正意義上的 發(fā)送緩沖區(qū)。發(fā)送的數(shù)據(jù)會(huì)直接交給內(nèi)核,由內(nèi)核將數(shù)據(jù)傳給網(wǎng)絡(luò)層協(xié)議
進(jìn)行后續(xù)的傳輸動(dòng)作;
UDP具有接收緩沖區(qū),但是這個(gè)接收緩沖區(qū)不能保證收到的UDP報(bào)的順序和發(fā)送UDP報(bào)的順序一
致;如果緩沖區(qū)滿了,再到達(dá)的UDP數(shù)據(jù)就會(huì)被丟棄;

Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)?文章來源地址http://www.zghlxwxcb.cn/news/detail-437173.html

到了這里,關(guān)于Socket套接字編程(實(shí)現(xiàn)TCP和UDP的通信)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【網(wǎng)絡(luò)通信】socket編程——TCP套接字

    【網(wǎng)絡(luò)通信】socket編程——TCP套接字

    TCP依舊使用代碼來熟悉對(duì)應(yīng)的套接字,很多接口都是在udp中使用過的 所以就不會(huì)單獨(dú)把他們拿出來作為標(biāo)題了,只會(huì)把第一次出現(xiàn)的接口作為標(biāo)題 通過TCP的套接字 ,來把數(shù)據(jù)交付給對(duì)方的應(yīng)用層,完成雙方進(jìn)程的通信 在 tcpServer.hpp 中,創(chuàng)建一個(gè)命名空間 yzq 用于封裝 在命名

    2024年02月13日
    瀏覽(102)
  • 「網(wǎng)絡(luò)編程」第二講:網(wǎng)絡(luò)編程socket套接字(三)_ 簡單TCP網(wǎng)絡(luò)通信程序的實(shí)現(xiàn)

    「網(wǎng)絡(luò)編程」第二講:網(wǎng)絡(luò)編程socket套接字(三)_ 簡單TCP網(wǎng)絡(luò)通信程序的實(shí)現(xiàn)

    「前言」文章是關(guān)于網(wǎng)絡(luò)編程的socket套接字方面的,上一篇是網(wǎng)絡(luò)編程socket套接字(二),下面開始講解!? 「歸屬專欄」網(wǎng)絡(luò)編程 「主頁鏈接」個(gè)人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點(diǎn)文青病」「每篇一句」 I?do?not?know?where?to?go,but?I?have?been?on?the?road. 我不知

    2024年02月11日
    瀏覽(29)
  • Java網(wǎng)絡(luò)編程基礎(chǔ):TCP Socket套接字編程 IntAddress UDP等...

    Java網(wǎng)絡(luò)編程基礎(chǔ):TCP Socket套接字編程 IntAddress UDP等...

    目錄 一,網(wǎng)絡(luò)基礎(chǔ) 1.IP地址 2.端口 3.TCP/UDP協(xié)議 4.網(wǎng)絡(luò)編程開發(fā)模式 ?二,基于套接字的Java網(wǎng)絡(luò)編程 1.Socket ?2.InetAddress 三.基于TCP的Socket網(wǎng)絡(luò)編程 1.單服務(wù)器端與單Socket客戶端一次通訊 2.單服務(wù)器端接收多次通訊 ?3.TCP網(wǎng)絡(luò)通訊補(bǔ)充 四,基于UDP的網(wǎng)絡(luò)編程 1. DatagramSocket:收發(fā)

    2024年04月29日
    瀏覽(28)
  • Java網(wǎng)絡(luò)編程(二)Socket 套接字(TCP和UDP),以及TCP的回顯

    Java網(wǎng)絡(luò)編程(二)Socket 套接字(TCP和UDP),以及TCP的回顯

    我們軟件工作者,著重編寫的是應(yīng)用層的代碼,但是發(fā)送這個(gè)數(shù)據(jù),我們就需要將應(yīng)用層傳輸?shù)絺鬏攲?,也就意味著我們需要調(diào)用應(yīng)用層的API,統(tǒng)稱為 Socket API。 套接字的分類: 流套接字:使用傳輸層TCP協(xié)議 特點(diǎn): 有連接:使用 TCP 通信的雙方,需要時(shí)刻保存對(duì)方的相關(guān)消

    2024年02月09日
    瀏覽(21)
  • 【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程概念,socket套接字,基于UDP和TCP的網(wǎng)絡(luò)編程

    【網(wǎng)絡(luò)編程】網(wǎng)絡(luò)編程概念,socket套接字,基于UDP和TCP的網(wǎng)絡(luò)編程

    前言: 大家好,我是 良辰丫 ,今天我們一起來學(xué)習(xí)網(wǎng)絡(luò)編程,網(wǎng)絡(luò)編程的基本概念,認(rèn)識(shí)套接字,UDP與TCP編程.?????? ??個(gè)人主頁:良辰針不戳 ??所屬專欄:javaEE初階 ??勵(lì)志語句:生活也許會(huì)讓我們遍體鱗傷,但最終這些傷口會(huì)成為我們一輩子的財(cái)富。 ??期待大家三連,關(guān)注

    2023年04月20日
    瀏覽(85)
  • 【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字 -- 基于socket實(shí)現(xiàn)一個(gè)簡單UDP網(wǎng)絡(luò)程序

    【Linux網(wǎng)絡(luò)】網(wǎng)絡(luò)編程套接字 -- 基于socket實(shí)現(xiàn)一個(gè)簡單UDP網(wǎng)絡(luò)程序

    我們把數(shù)據(jù)從A主機(jī)發(fā)送到B主機(jī),是目的嗎?不是,真正通信的不是這兩個(gè)機(jī)器!其實(shí)是這兩臺(tái)機(jī)器上面的軟件(人) 數(shù)據(jù)有 IP(公網(wǎng)) 標(biāo)識(shí)一臺(tái)唯一的主機(jī) ,用誰來標(biāo)識(shí)各自主機(jī)上客戶或者服務(wù)進(jìn)程的唯一性呢? 為了更好的表示一臺(tái)主機(jī)上服務(wù)進(jìn)程的唯一性,我們采用 端口號(hào)

    2024年02月12日
    瀏覽(848)
  • 【網(wǎng)絡(luò)】socket——預(yù)備知識(shí) | 套接字 | UDP網(wǎng)絡(luò)通信

    【網(wǎng)絡(luò)】socket——預(yù)備知識(shí) | 套接字 | UDP網(wǎng)絡(luò)通信

    ??作者:一只大喵咪1201 ??專欄:《網(wǎng)絡(luò)》 ??格言: 你只管努力,剩下的交給時(shí)間! 在前面本喵對(duì)網(wǎng)絡(luò)的整體輪廓做了一個(gè)大概的介紹,比如分層,協(xié)議等等內(nèi)容,現(xiàn)在我們直接進(jìn)入socket(套接字)編程,先來感受到網(wǎng)絡(luò)編程。 我們知道,在網(wǎng)絡(luò)通信中,存在兩套地址,一

    2024年02月13日
    瀏覽(21)
  • C語言 進(jìn)程通訊 socket套接字(TCP/UDP)示例

    C語言 進(jìn)程通訊 socket套接字(TCP/UDP)示例

    主機(jī)字節(jié)序(host-byte):指處理器存儲(chǔ)數(shù)據(jù)的字節(jié)順序,分兩種 ? ? ? ? 大端存儲(chǔ)(big-endian):低地址存儲(chǔ)數(shù)據(jù)高位(符合書寫規(guī)則),由ARM、Motorola等采用 ? ? ? ? 小端存儲(chǔ)(little-endian):低地址存儲(chǔ)數(shù)據(jù)低位(將數(shù)據(jù)不重要的部分保存在低地址,重要的部分保存在高地

    2024年02月05日
    瀏覽(18)
  • C++網(wǎng)絡(luò)編程 TCP套接字基礎(chǔ)知識(shí),利用TCP套接字實(shí)現(xiàn)客戶端-服務(wù)端通信

    C++網(wǎng)絡(luò)編程 TCP套接字基礎(chǔ)知識(shí),利用TCP套接字實(shí)現(xiàn)客戶端-服務(wù)端通信

    流式套接字編程針對(duì)TCP協(xié)議通信,即是面向?qū)ο蟮耐ㄐ牛譃榉?wù)端和客戶端兩部分。 1)加載套接字庫( 使用函數(shù)WSAStartup() ),創(chuàng)建套接字( 使用socket() ) 2)綁定套接字到一個(gè)IP地址和一個(gè)端口上( 使用函數(shù)bind() ) 3)將套接字設(shè)置為監(jiān)聽模式等待連接請(qǐng)求( 使用函數(shù)

    2024年02月03日
    瀏覽(102)
  • 網(wǎng)絡(luò)編程『socket套接字 ‖ 簡易UDP網(wǎng)絡(luò)程序』

    網(wǎng)絡(luò)編程『socket套接字 ‖ 簡易UDP網(wǎng)絡(luò)程序』

    ??個(gè)人主頁: 北 海 ??所屬專欄: Linux學(xué)習(xí)之旅、神奇的網(wǎng)絡(luò)世界 ??操作環(huán)境: CentOS 7.6 阿里云遠(yuǎn)程服務(wù)器 在當(dāng)今數(shù)字化時(shí)代,網(wǎng)絡(luò)通信作為連接世界的橋梁,成為計(jì)算機(jī)科學(xué)領(lǐng)域中至關(guān)重要的一部分。理解網(wǎng)絡(luò)編程是每一位程序員必備的技能之一,而掌握套接字編程則

    2024年02月04日
    瀏覽(103)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包