欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?Socket編程從零到實(shí)戰(zhàn)詳解(完整實(shí)戰(zhàn)案例)

 更新時(shí)間:2025年04月12日 10:06:42   作者:北嶺敲鍵盤(pán)的荒漠貓  
這篇文章主要介紹了Java?Socket編程從零到實(shí)戰(zhàn)詳解,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧

一、Socket基礎(chǔ)概念與工作流程(圖解)

(先理解“打電話”模型,再寫(xiě)代碼)

1. Socket通信核心模型

關(guān)鍵角色

  • 客戶(hù)端:主動(dòng)發(fā)起連接(類(lèi)似撥打電話)
  • 服務(wù)端:監(jiān)聽(tīng)端口,等待連接(類(lèi)似待機(jī)電話)
  • Socket對(duì)象:連接建立后的數(shù)據(jù)傳輸通道(通話線路)

2. 核心流程分解

  • 服務(wù)端:創(chuàng)建ServerSocket → 綁定端口 → 阻塞等待連接(accept()
  • 客戶(hù)端:創(chuàng)建Socket → 指定服務(wù)端IP和端口 → 發(fā)起連接
  • 雙向通信:通過(guò)輸入流(InputStream)和輸出流(OutputStream)收發(fā)數(shù)據(jù)
  • 關(guān)閉連接:調(diào)用close()釋放資源

二、服務(wù)端與客戶(hù)端基礎(chǔ)代碼分步解析

(每行代碼加注釋?zhuān)率直乜矗?/p>

1. 服務(wù)端基礎(chǔ)代碼(單線程版)

// 步驟1:創(chuàng)建ServerSocket,綁定端口8080
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("服務(wù)端啟動(dòng),等待連接...");
// 步驟2:等待客戶(hù)端連接(阻塞方法,直到有客戶(hù)端連接)
Socket clientSocket = serverSocket.accept(); 
System.out.println("客戶(hù)端接入:" + clientSocket.getRemoteSocketAddress());
// 步驟3:獲取輸入流(接收客戶(hù)端數(shù)據(jù))
InputStream input = clientSocket.getInputStream();
byte[] buffer = new byte[1024];
int len = input.read(buffer);  // 讀取數(shù)據(jù)到buffer數(shù)組
String receivedData = new String(buffer, 0, len);
System.out.println("收到消息:" + receivedData);
// 步驟4:發(fā)送響應(yīng)數(shù)據(jù)
OutputStream output = clientSocket.getOutputStream();
output.write("已收到!".getBytes());
// 步驟5:關(guān)閉連接(實(shí)際開(kāi)發(fā)中需在finally塊處理)
clientSocket.close();
serverSocket.close();

2. 客戶(hù)端基礎(chǔ)代碼

// 步驟1:連接服務(wù)端(IP+端口)
Socket socket = new Socket("127.0.0.1", 8080);
System.out.println("連接服務(wù)端成功!");
// 步驟2:發(fā)送數(shù)據(jù)
OutputStream output = socket.getOutputStream();
output.write("你好,服務(wù)端!".getBytes());
// 步驟3:接收響應(yīng)
InputStream input = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = input.read(buffer);
String response = new String(buffer, 0, len);
System.out.println("服務(wù)端響應(yīng):" + response);
// 步驟4:關(guān)閉連接
socket.close();

三、超時(shí)設(shè)置詳解(解決卡死問(wèn)題)

(必學(xué)技能,避免程序無(wú)限等待)

1. 連接超時(shí)(防止無(wú)法連接時(shí)卡死)

Socket socket = new Socket();
// 設(shè)置連接超時(shí)為5秒(單位:毫秒)
socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000);  // 
  • 觸發(fā)場(chǎng)景:服務(wù)端未啟動(dòng)或網(wǎng)絡(luò)不通
  • 異常處理:捕獲SocketTimeoutException提示用戶(hù)檢查網(wǎng)絡(luò)

2. 讀取超時(shí)(防止數(shù)據(jù)未到達(dá)時(shí)阻塞)

socket.setSoTimeout(3000);  // 設(shè)置讀取超時(shí)3秒 
  • 作用范圍InputStream.read()操作
  • 異常處理:超時(shí)后拋出SocketTimeoutException,可重試或終止

3. 完整超時(shí)處理示例

try (Socket socket = new Socket()) {
    // 連接超時(shí)5秒
    socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000);
    // 讀取超時(shí)3秒
    socket.setSoTimeout(3000);
    InputStream input = socket.getInputStream();
    // 讀取數(shù)據(jù)...
} catch (SocketTimeoutException e) {
    System.err.println("操作超時(shí):" + e.getMessage());
} catch (IOException e) {
    System.err.println("連接失?。? + e.getMessage());
}

四、心跳機(jī)制實(shí)現(xiàn)(維持長(zhǎng)連接)

(防止長(zhǎng)時(shí)間無(wú)數(shù)據(jù)導(dǎo)致連接斷開(kāi))

1. 心跳包原理

  • 作用:定時(shí)發(fā)送空數(shù)據(jù)包,告知對(duì)方連接存活
  • 實(shí)現(xiàn)方式:客戶(hù)端定時(shí)任務(wù) + 服務(wù)端超時(shí)檢測(cè)

2. 客戶(hù)端心跳代碼

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        try {
            OutputStream out = socket.getOutputStream();
            out.write(0);  // 發(fā)送心跳包(內(nèi)容可為任意約定標(biāo)識(shí))
            out.flush();
            System.out.println("心跳發(fā)送成功");
        } catch (IOException e) {
            System.err.println("心跳發(fā)送失敗,連接已斷開(kāi)");
            timer.cancel();  // 停止定時(shí)任務(wù)
        }
    }
}, 0, 30000);  // 立即啟動(dòng),每30秒執(zhí)行一次 

3. 服務(wù)端檢測(cè)心跳

socket.setSoTimeout(45000);  // 超時(shí)時(shí)間略大于心跳間隔
try {
    InputStream in = socket.getInputStream();
    while (true) {
        int data = in.read();  // 阻塞等待數(shù)據(jù)
        if (data == 0) {
            System.out.println("收到心跳包");
        }
    }
} catch (SocketTimeoutException e) {
    System.err.println("心跳超時(shí),連接斷開(kāi)");
    socket.close();
}

五、完整實(shí)戰(zhàn)案例:帶超時(shí)與心跳的Echo服務(wù)

服務(wù)端代碼(多線程版)

public class EchoServer {
    public static void main(String[] args) throws IOException {
        ExecutorService pool = Executors.newCachedThreadPool();  // 線程池處理并發(fā)
        try (ServerSocket server = new ServerSocket(8080)) {
            System.out.println("服務(wù)端啟動(dòng),端口8080");
            while (true) {
                Socket client = server.accept();
                client.setSoTimeout(45000);  // 設(shè)置讀取超時(shí)45秒 
                pool.submit(() -> handleClient(client));
            }
        }
    }
    private static void handleClient(Socket client) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
             PrintWriter out = new PrintWriter(client.getOutputStream(), true)) {
            String input;
            while ((input = in.readLine()) != null) {
                if ("HEARTBEAT".equals(input)) {  // 識(shí)別心跳包
                    System.out.println("收到心跳包");
                    continue;
                }
                out.println("Echo: " + input);  // 回顯消息
            }
        } catch (SocketTimeoutException e) {
            System.err.println("客戶(hù)端超時(shí)未響應(yīng),連接關(guān)閉");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try { client.close(); } catch (IOException e) {}
        }
    }
}

客戶(hù)端代碼(帶心跳與超時(shí))

public class EchoClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket()) {
            // 連接超時(shí)5秒
            socket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000);
            // 讀取超時(shí)3秒
            socket.setSoTimeout(3000);
            // 啟動(dòng)心跳線程(每30秒一次)
            startHeartbeat(socket.getOutputStream());
            Scanner scanner = new Scanner(System.in);
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                System.out.print("輸入消息:");
                String msg = scanner.nextLine();
                out.println(msg);  // 發(fā)送消息
                System.out.println("服務(wù)端響應(yīng):" + in.readLine());
            }
        } catch (SocketTimeoutException e) {
            System.err.println("操作超時(shí):" + e.getMessage());
        } catch (IOException e) {
            System.err.println("連接異常:" + e.getMessage());
        }
    }
    private static void startHeartbeat(OutputStream out) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    out.write("HEARTBEAT\n".getBytes());  // 發(fā)送心跳標(biāo)識(shí)
                    out.flush();
                } catch (IOException e) {
                    timer.cancel();
                }
            }
        }, 0, 30000);
    }
}

六、常見(jiàn)問(wèn)題與解決方案速查表

問(wèn)題現(xiàn)象可能原因解決方案
Connection refused服務(wù)端未啟動(dòng)或端口錯(cuò)誤檢查服務(wù)端代碼是否運(yùn)行,確認(rèn)端口一致
Read timed out網(wǎng)絡(luò)延遲或服務(wù)端未及時(shí)響應(yīng)增加超時(shí)時(shí)間或優(yōu)化服務(wù)端代碼
Broken pipe連接已關(guān)閉仍嘗試寫(xiě)數(shù)據(jù)發(fā)送前檢查socket.isClosed(),捕獲異常后重連
內(nèi)存泄漏未關(guān)閉Socket或流使用try-with-resources自動(dòng)關(guān)閉資源

七、Java Socket核心方法速查表

方法名所屬類(lèi)功能描述參數(shù)說(shuō)明返回值常見(jiàn)異常使用示例
ServerSocket(int port)ServerSocket創(chuàng)建服務(wù)端Socket并綁定指定端口port:監(jiān)聽(tīng)的端口號(hào)(0-65535)無(wú)BindException(端口被占用)new ServerSocket(8080);
accept()ServerSocket阻塞等待客戶(hù)端連接,返回通信用的Socket對(duì)象無(wú)Socket(客戶(hù)端連接對(duì)象)IOExceptionSocket client = serverSocket.accept();
close()ServerSocket關(guān)閉服務(wù)端Socket,釋放端口資源無(wú)無(wú)IOExceptionserverSocket.close();
Socket(String host, int port)Socket客戶(hù)端主動(dòng)連接服務(wù)端(構(gòu)造函數(shù)隱式調(diào)用connect()host:服務(wù)端IP;port:服務(wù)端端口無(wú)UnknownHostException, IOExceptionSocket socket = new Socket("127.0.0.1", 8080);
connect(SocketAddress addr, int timeout)Socket顯式連接服務(wù)端,可設(shè)置超時(shí)時(shí)間addr:服務(wù)端地址;timeout:超時(shí)毫秒無(wú)SocketTimeoutExceptionsocket.connect(new InetSocketAddress("127.0.0.1", 8080), 5000);
getInputStream()Socket獲取輸入流,用于接收數(shù)據(jù)無(wú)InputStreamIOExceptionInputStream in = socket.getInputStream();
getOutputStream()Socket獲取輸出流,用于發(fā)送數(shù)據(jù)無(wú)OutputStreamIOExceptionOutputStream out = socket.getOutputStream();
setSoTimeout(int timeout)Socket設(shè)置讀取超時(shí)時(shí)間(單位:毫秒),超時(shí)后拋出SocketTimeoutExceptiontimeout:超時(shí)時(shí)間(0表示無(wú)限等待)無(wú)SocketExceptionsocket.setSoTimeout(3000);
setKeepAlive(boolean on)Socket啟用/禁用TCP保活機(jī)制(默認(rèn)關(guān)閉),自動(dòng)檢測(cè)連接是否存活on:true啟用,false禁用無(wú)SocketExceptionsocket.setKeepAlive(true);
shutdownOutput()Socket關(guān)閉輸出流(發(fā)送FIN包),通知對(duì)方數(shù)據(jù)發(fā)送完畢,但不關(guān)閉Socket無(wú)無(wú)IOExceptionsocket.shutdownOutput();
close()Socket關(guān)閉Socket連接,釋放資源無(wú)無(wú)IOExceptionsocket.close();
readInt()DataInputStream從輸入流讀取4字節(jié)的int值(常用于解析長(zhǎng)度頭)無(wú)intEOFException, IOExceptionint length = new DataInputStream(in).readInt();
writeInt(int v)DataOutputStream向輸出流寫(xiě)入4字節(jié)的int值(常用于發(fā)送長(zhǎng)度頭)v:要寫(xiě)入的整數(shù)值無(wú)IOExceptionnew DataOutputStream(out).writeInt(1024);

到此這篇關(guān)于Java Socket編程從零到實(shí)戰(zhàn)詳解的文章就介紹到這了,更多相關(guān)Java Socket編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹(shù)

    Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹(shù)

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之樹(shù),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java數(shù)據(jù)結(jié)構(gòu)的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • intellij idea 2021.2 打包并上傳運(yùn)行spring boot項(xiàng)目的詳細(xì)過(guò)程(spring boot 2.5.4)

    intellij idea 2021.2 打包并上傳運(yùn)行spring boot項(xiàng)目的詳細(xì)過(guò)程(spring boot 2

    這篇文章主要介紹了intellij idea 2021.2 打包并上傳運(yùn)行一個(gè)spring boot項(xiàng)目(spring boot 2.5.4),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • java 單例模式容易忽略的細(xì)節(jié)

    java 單例模式容易忽略的細(xì)節(jié)

    這篇文章主要介紹了java 單例模式容易忽略的細(xì)節(jié),幫助大家更好的理解和使用java 單例模式,感興趣的朋友可以了解下
    2020-12-12
  • Java 實(shí)現(xiàn)LZ78壓縮算法的示例代碼

    Java 實(shí)現(xiàn)LZ78壓縮算法的示例代碼

    這篇文章主要介紹了Java 實(shí)現(xiàn)LZ78壓縮算法的示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-05-05
  • 淺談Arrays.asList() 和ArrayList類(lèi)型區(qū)別

    淺談Arrays.asList() 和ArrayList類(lèi)型區(qū)別

    下面小編就為大家?guī)?lái)一篇Arrays.asList() 和ArrayList類(lèi)型區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-10-10
  • 最新評(píng)論