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

java整合socket通信的完整步驟記錄

 更新時(shí)間:2025年07月25日 10:50:24   作者:kimloner  
在Java編程中Socket通信是實(shí)現(xiàn)網(wǎng)絡(luò)應(yīng)用程序之間通信的基礎(chǔ),Socket是TCP/IP協(xié)議的一部分,它提供了進(jìn)程間的網(wǎng)絡(luò)通信,這篇文章主要介紹了java整合socket通信的相關(guān)資料,需要的朋友可以參考下

前言

大家好,由于工作上業(yè)務(wù)的需要,在java項(xiàng)目中引入了socket通信,特此記錄一下,用以備份,本文章中的socket通信實(shí)現(xiàn)了,服務(wù)端與客戶端的雙向通訊,以及二者之間的心跳通信,服務(wù)端重啟之后,客戶端的自動(dòng)重連功能。

原理

Socket通信是計(jì)算機(jī)網(wǎng)絡(luò)中常用的一種通信機(jī)制,它是基于TCP/IP協(xié)議實(shí)現(xiàn)的,提供了兩個(gè)應(yīng)用程序之間通過(guò)網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)交換的能力。Socket本質(zhì)上是一種抽象概念,為網(wǎng)絡(luò)服務(wù)提供了一組API接口。

  • Socket通信模型

Socket通信模型通常包括客戶端和服務(wù)器端兩部分。
服務(wù)器端:負(fù)責(zé)在特定的端口監(jiān)聽(tīng)來(lái)自客戶端的連接請(qǐng)求,當(dāng)一個(gè)請(qǐng)求到達(dá)時(shí),服務(wù)器會(huì)與客戶端建立連接,并為客戶端提供相應(yīng)的服務(wù)。
客戶端:主動(dòng)向服務(wù)器的特定IP地址和端口發(fā)起連接請(qǐng)求,連接成功后,客戶端可以通過(guò)建立的連接向服務(wù)器發(fā)送請(qǐng)求并接收響應(yīng)。

  • Socket通信過(guò)程

Socket通信過(guò)程一般包括以下幾個(gè)步驟:

  • 服務(wù)器監(jiān)聽(tīng):

服務(wù)器通過(guò)socket()函數(shù)創(chuàng)建一個(gè)Socket,并通過(guò)bind()函數(shù)將其綁定到一個(gè)IP地址和端口上。然后,服務(wù)器調(diào)用listen()函數(shù)開(kāi)始監(jiān)聽(tīng)該端口上的連接請(qǐng)求。

  • 客戶端請(qǐng)求連接:

客戶端也通過(guò)socket()函數(shù)創(chuàng)建一個(gè)Socket,然后調(diào)用connect()函數(shù)嘗試與服務(wù)器的指定IP地址和端口建立連接。

  • 服務(wù)器接受連接:

服務(wù)器在接收到客戶端的連接請(qǐng)求后,通過(guò)accept()函數(shù)接受這個(gè)連接。如果成功,accept()函數(shù)會(huì)返回一個(gè)新的Socket(通常稱為“子Socket”),用于與該客戶端進(jìn)行通信。

數(shù)據(jù)傳輸:連接建立成功后,客戶端和服務(wù)器就可以通過(guò)新建立的Socket進(jìn)行數(shù)據(jù)傳輸了。數(shù)據(jù)傳輸可以是單向的也可以是雙向的。應(yīng)用程序可以使用send(), write(), recv(), read()等函數(shù)進(jìn)行數(shù)據(jù)發(fā)送和接收操作。

  • 斷開(kāi)連接:

當(dāng)通信結(jié)束后,客戶端和服務(wù)器都可以調(diào)用close()函數(shù)來(lái)關(guān)閉自己持有的Socket,從而斷開(kāi)兩者之間的連接。
TCP vs UDP
在實(shí)際使用中,基于Socket的通信方式主要有兩種:基于TCP和基于UDP。
TCP Socket:提供可靠、面向連接、基于字節(jié)流的通信方式。適用對(duì)數(shù)據(jù)完整性和順序有要求的應(yīng)用場(chǎng)景。
UDP Socket:提供無(wú)連接、不保證可靠性、基于消息(數(shù)據(jù)報(bào))的通信方式。適用于對(duì)實(shí)時(shí)性要求高、容忍部分?jǐn)?shù)據(jù)丟失或亂序的應(yīng)用場(chǎng)景。

代碼實(shí)現(xiàn)

服務(wù)端

服務(wù)端主體邏輯:和每個(gè)接入的客戶端都會(huì)使用獨(dú)立線程建立起長(zhǎng)連接,二者之間使用心跳保持聯(lián)系,使用clientSockets 存儲(chǔ)了每個(gè)客戶端的信息便于和客戶端建立起聯(lián)系。

package com.example.demo2.server.socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author kim
 */
@Component
public class TcpServer implements DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(TcpServer.class);

    private final SocketServerConfig config;
    private ServerSocket serverSocket;
    private ExecutorService executorService;
    private volatile boolean running = true;

    // 存儲(chǔ)客戶端連接
    private final Map<String, Socket> clientSockets = new ConcurrentHashMap<>();

    public TcpServer(SocketServerConfig config) {
        this.config = config;
    }

    @PostConstruct
    public void start() throws IOException {
        executorService = Executors.newFixedThreadPool(config.getMaxThreads());
        serverSocket = new ServerSocket(config.getPort());
        logger.info("平臺(tái)socket服務(wù)已啟動(dòng), 監(jiān)聽(tīng)端口為 {}", config.getPort());

        new Thread(this::acceptConnections).start();
    }

    private void acceptConnections() {
        while (running) {
            try {
                Socket clientSocket = serverSocket.accept();
                String clientAddress = clientSocket.getInetAddress().getHostAddress();
                clientSockets.put(clientAddress, clientSocket);
                executorService.execute(new ClientHandler(clientSocket, clientAddress));
            } catch (IOException e) {
                if (running) {
                    logger.error("Connection accept error", e);
                }
            }
        }
    }

    // 用于發(fā)送消息到特定客戶端
    public void sendMessageToClient(String clientAddress, String message) throws IOException {
        Socket socket = clientSockets.get(clientAddress);
        if (socket != null && !socket.isClosed()) {
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            out.println(message);
            logger.info("Sent message to {}: {}", clientAddress, message);
        } else {
            logger.warn("Client {} is not connected or socket is closed", clientAddress);
        }
    }

    @Override
    public void destroy() throws Exception {
        running = false;
        executorService.shutdown();
        for (Socket socket : clientSockets.values()) {
            if (!socket.isClosed()) {
                socket.close();
            }
        }
        if (serverSocket != null && !serverSocket.isClosed()) {
            serverSocket.close();
        }
        logger.info("TCP Server stopped");
    }

    private class ClientHandler implements Runnable {
        private final Socket clientSocket;
        private final String clientAddress;

        ClientHandler(Socket socket, String address) {
            this.clientSocket = socket;
            this.clientAddress = address;
        }

        @Override
        public void run() {
            try (BufferedReader in = new BufferedReader(
                    new InputStreamReader(clientSocket.getInputStream()));
                 PrintWriter out = new PrintWriter(
                         clientSocket.getOutputStream(), true)) {
                logger.info("Client connected: {}", clientAddress);
                String input;
                while ((input = in.readLine()) != null) {
                    logger.debug("Received: {}", input);
                    out.println(input);
                    logger.info("Client connected: {}", clientAddress);
                }
            } catch (IOException e) {
                logger.warn("Client connection closed: {}", e.getMessage());
            } finally {
                try {
                    clientSockets.remove(clientAddress);
                    clientSocket.close();
                } catch (IOException e) {
                    logger.error("Error closing socket", e);
                }
            }
        }


    }
}

配置類

package com.example.demo2.server.socket;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "socket.server")
@Data
public class SocketServerConfig {
    private int port;
    private int maxThreads;
    
    // Getters and Setters
}

配置文件

server:
  port: 8080

socket:
  server:
    port: 8088
    maxThreads: 50

向客戶端發(fā)送測(cè)試信息

 @GetMapping("/send")
    public String sendMessage(String clientAddress) throws IOException {
       tcpServer.sendMessageToClient("192.168.3.8","77777777777");
       return "success";
    }

服務(wù)端發(fā)送日志

客戶端接收日志

客戶端

客戶端主體邏輯,使用自己設(shè)計(jì)的心跳機(jī)制,監(jiān)聽(tīng)服務(wù)端狀態(tài),如果服務(wù)端斷開(kāi)連接,則客戶端會(huì)嘗試重新建立聯(lián)系。

package com.example.demo1.socketclient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

@Service
public class TcpClientService implements ApplicationRunner, DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(TcpClientService.class);

    private final SocketClientConfig config;
    private Socket socket;
    private PrintWriter out;
    private BufferedReader in;
    private final AtomicBoolean running = new AtomicBoolean(true);
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final MessageListener messageListener;

    @Autowired
    public TcpClientService(SocketClientConfig config, MessageListener messageListener) {
        this.config = config;
        this.messageListener = messageListener;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        initializeConnection();
    }

    @Override
    public void destroy() throws Exception {
        running.set(false);
        closeResources();
        executor.shutdown();
    }

    private synchronized void initializeConnection() {
        new Thread(() -> {
            while (running.get()) {
                try {
                    socket = new Socket();
                    socket.setKeepAlive(true);
                    socket.setSoTimeout(config.getHeartbeatTimeout());
                    socket.connect(new InetSocketAddress(config.getHost(), config.getPort()), config.getTimeout());

                    out = new PrintWriter(socket.getOutputStream(), true);
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                    logger.info("Connected to server {}:{}", config.getHost(), config.getPort());

                    executor.execute(this::listenToServer);

                    startHeartbeat();

                    while (!socket.isClosed() && socket.isConnected()) {
                        Thread.sleep(1000);
                    }
                } catch (Exception e) {
                    logger.warn("Connection error: {}", e.getMessage());
                } finally {
                    closeResources();
                    if (running.get()) {
                        logger.info("Attempting to reconnect in 5 seconds...");
                        sleepSafely(5000);
                    }
                }
            }
        }, "tcp-client-connector").start();
    }

    private void listenToServer() {
        try {
            String response;
            while (running.get() && !socket.isClosed()) {
                try {
                    response = in.readLine();
                    if (response == null) {
                        logger.warn("Server closed connection");
                        break; // 終止循環(huán),表示連接已關(guān)閉
                    }
                    logger.debug("Received server message: {}", response);
                    messageListener.onMessage(response);
                } catch (SocketTimeoutException e) {
                    logger.debug("Socket read timeout");
                } catch (IOException e) {
                    if (!socket.isClosed()) {
                        logger.warn("Connection lost: {}", e.getMessage());
                        break; // 終止循環(huán),表示連接已中斷
                    }
                }
            }
        } finally {
            closeResources(); // 確保資源關(guān)閉
        }
    }

    private void startHeartbeat() {
        new Thread(() -> {
            while (running.get() && !socket.isClosed()) {
                try {
                    sendMessageInternal("HEARTBEAT");
                    sleepSafely(config.getHeartbeatInterval());
                } catch (Exception e) {
                    logger.warn("Heartbeat failed: {}", e.getMessage());
                    break;
                }
            }
        }, "heartbeat-thread").start();
    }

    public synchronized void sendMessage(String message) throws IOException {
        if (socket == null || !socket.isConnected()) {
            throw new IOException("Not connected to server");
        }
        out.println(message);
        logger.debug("Sent message: {}", message);
    }

    private synchronized void sendMessageInternal(String message) {
        try {
            if (socket != null && socket.isConnected()) {
                out.println(message);
            }
        } catch (Exception e) {
            logger.warn("Failed to send heartbeat");
        }
    }

    private synchronized void closeResources() {
        try {
            if (out != null) out.close();
            if (in != null) in.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            logger.warn("Error closing resources: {}", e.getMessage());
        }
    }

    private void sleepSafely(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public interface MessageListener {
        void onMessage(String message);
    }
}

消息監(jiān)聽(tīng):監(jiān)聽(tīng)服務(wù)發(fā)送的消息

package com.example.demo1.socketclient;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class ServerMessageHandler implements TcpClientService.MessageListener {
    private static final Logger logger = LoggerFactory.getLogger(ServerMessageHandler.class);

    @Override
    public void onMessage(String message) {
       if(StringUtils.isNotEmpty(message)){
           if (!message.contains("HEARTBEAT")){
               //處理其他邏輯
               System.out.println("接收服務(wù)端消息成功:"+message);
           }else{
               //心跳消息
               System.out.println(message);
           }
       }
    }


}

配置類

package com.example.demo1.socketclient;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "socket.client")
@Data
public class SocketClientConfig {
    private String host;
    private int port;
    private int timeout;
    private int heartbeatInterval;
    private int heartbeatTimeout;

    // Getters and Setters
}

發(fā)送測(cè)試方法

 @GetMapping("/send")
    public ResponseEntity<String> sendMessage() {
        try {
            tcpClient.sendMessage("客戶端發(fā)送信息");
            return ResponseEntity.ok("Message sent");
        } catch (IOException e) {
            return ResponseEntity.status(503).body("Server unavailable");
        }
    }

配置文件

socket:
  client:
    host: 192.168.3.8 #服務(wù)端ip地址
    port: 8088 #監(jiān)聽(tīng)端口
    timeout: 5000
    heartbeat-interval: 3000    # 心跳間隔30秒
    heartbeat-timeout: 6000     # 心跳超時(shí)60秒
server:
  port: 8082


客戶端發(fā)送信息后,服務(wù)端會(huì)接收到信息。

總結(jié)

以上就是java接入socket通信服務(wù)端與客戶端的全部代碼,二者實(shí)現(xiàn)了互相通信,具體的業(yè)務(wù)場(chǎng)景則需要小伙伴們?cè)诖嘶A(chǔ)上額外的設(shè)計(jì)邏輯了,有其他疑問(wèn)或者想要測(cè)試demo的可以后臺(tái)私我,看到會(huì)回復(fù)。

到此這篇關(guān)于java整合socket通信的文章就介紹到這了,更多相關(guān)java整合socket通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • intellij idea 將模塊打jar包的步驟詳解

    intellij idea 將模塊打jar包的步驟詳解

    這篇文章主要介紹了intellij idea 將模塊打jar包的步驟,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-09-09
  • Spring MVC url提交參數(shù)和獲取參數(shù)

    Spring MVC url提交參數(shù)和獲取參數(shù)

    本文重要講述通過(guò)url提交參數(shù)和獲取參數(shù)的具體操作與實(shí)現(xiàn)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-04-04
  • SpringBoot3中使用虛擬線程的完整步驟

    SpringBoot3中使用虛擬線程的完整步驟

    在 Spring Boot 3 中使用 Java 21+ 的虛擬線程(Virtual Threads)可以顯著提升 I/O 密集型應(yīng)用的并發(fā)能力,這篇文章為大家介紹了詳細(xì)的實(shí)現(xiàn)步驟,希望對(duì)大家有所幫助
    2025-06-06
  • springboot對(duì)接minio的webhook完整步驟記錄

    springboot對(duì)接minio的webhook完整步驟記錄

    Minio是一款開(kāi)源的對(duì)象存儲(chǔ)服務(wù),它致力于為開(kāi)發(fā)者提供簡(jiǎn)單、高性能、高可用的云存儲(chǔ)解決方案,下面這篇文章主要給大家介紹了關(guān)于springboot對(duì)接minio的webhook的相關(guān)資料,需要的朋友可以參考下
    2024-07-07
  • Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫(xiě)文件寫(xiě)不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java編程代碼性能優(yōu)化

    Java編程代碼性能優(yōu)化

    本文介紹了 Java 代碼優(yōu)化的過(guò)程,總結(jié)了優(yōu)化 Java 程序的一些最佳實(shí)踐,分析了進(jìn)行優(yōu)化的方法,并解釋了性能提升的原因,需要的朋友可以參考下
    2015-11-11
  • Intellij Idea 多模塊Maven工程中模塊之間無(wú)法相互引用問(wèn)題

    Intellij Idea 多模塊Maven工程中模塊之間無(wú)法相互引用問(wèn)題

    這篇文章主要介紹了Intellij Idea 多模塊Maven工程中模塊之間無(wú)法相互引用問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • java保證一個(gè)方法只能執(zhí)行一次的問(wèn)題

    java保證一個(gè)方法只能執(zhí)行一次的問(wèn)題

    這篇文章主要介紹了java保證一個(gè)方法只能執(zhí)行一次的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • SpringBoot攔截器原理解析及使用方法

    SpringBoot攔截器原理解析及使用方法

    這篇文章主要介紹了SpringBoot攔截器原理解析及使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • SpringBoot實(shí)現(xiàn)AOP日志切面功能的詳細(xì)教程

    SpringBoot實(shí)現(xiàn)AOP日志切面功能的詳細(xì)教程

    AOP是Spring框架的核心特性之一,常用于日志記錄、權(quán)限校驗(yàn)、事務(wù)管理等場(chǎng)景,本文將手把手教你如何在Spring Boot項(xiàng)目中實(shí)現(xiàn)AOP日志切面功能,包括依賴引入、切點(diǎn)定義、切面實(shí)現(xiàn)、注解自定義等內(nèi)容,需要的朋友可以參考下
    2025-07-07

最新評(píng)論