当前位置:

SpringBoot整合Socket _

故人归 |  2025-07-13
 129人浏览

前言

前段时间公司一个物联网项目需要通过TCP连接设备收发消息,现在我把代码整理出来,分享一下。

源代码已发布在GitHub

创建Socket

使用ServerSocket绑定IP和端口,

TcpSocket实现Java的Runnable的类,在run方法中使用Accept监听端口是否有客户端发送连接请求,如果有连接来了就创建SocketReceive对象然后将他扔给线程池执行。 |

package com.example.socket_demo.socket;  

import lombok.extern.slf4j.Slf4j;  
import org.springframework.stereotype.Component;  

import java.io.IOException;  
import java.net.ServerSocket;  
import java.net.Socket;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  

/*     */  
@Component  
@Slf4j  
public class TcpSocket implements Runnable {  
    public Integer port;  
    private ServerSocket server;  
    private ExecutorService threadPool;  

    public TcpSocket() {  
        try {  
            port = 8081;  
            threadPool = Executors.newCachedThreadPool();  
            server = new ServerSocket(port);  
        } catch (Exception e) {  
            log.error(e.getMessage());  
        }  
    }  

    @Override  
    public void run() {  
        while (true) {  
            try {  
                Socket socket = server.accept();  
                if (socket != null) {  
                    SocketReceive socketReceive = new SocketReceive(socket);  
                    threadPool.submit(socketReceive);  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}  


 __JAVA  

---|---

在线连接

AllClientsMap类存放了所有的在线连接,通过hostAddress为key,Socket为Value。

|

package com.example.socket_demo.socket;  


import lombok.extern.slf4j.Slf4j;  

import java.net.Socket;  
import java.util.concurrent.ConcurrentHashMap;  
import java.util.concurrent.ConcurrentMap;  

@Slf4j  
public class AllClientsMap {  

    /*         */  
    private static final ConcurrentMap<String, Socket> ALLCLIENTS = new ConcurrentHashMap<>();  

    /*         */  
    public static ConcurrentMap<String, Socket> getAllClients() {  
        return ALLCLIENTS;  
    }  

    /*         */  
    public static Socket getSocketByKey(String key) {  
        return ALLCLIENTS.get(key);  
    }  

    /*         */  
    public static void put(String key, Socket socket) {  
        ALLCLIENTS.put(key, socket);  
        log.info("设备Key:{}========ip:{}已加入列表", key, socket.getInetAddress().getHostAddress());  
    }  

    /*         */  
    public static void remove(String key) {  
        ALLCLIENTS.remove(key);  
        log.info("已移除设备Key:{}", key);  
    }  

    /*         */  
    public static int size() {  
        log.info("当前设备数:{}", ALLCLIENTS.size());  
        return ALLCLIENTS.size();  
    }  

    /*         */  
    public static void print() {  
        log.info("当前设备列表信息:长度:{}", ALLCLIENTS.size());  
        ALLCLIENTS.forEach((key, socket) -> {  
            log.info("设备Key:{}========ip:{}", key, socket.getInetAddress().getHostAddress());  
        });  
    }  

    /*         */  
    public static boolean contains(String key) {  
        return ALLCLIENTS.containsKey(key);  
    }  
}  



 __JAVA  

---|---

创建SocketReceive

在SocketReceive类中,我们可以执行相关的接收消息,以及业务操作;在第64行代码的位置,可以通过ApplicationContext获取Spring Bean执行业务代码

|

package com.example.socket_demo.socket;  

import lombok.extern.slf4j.Slf4j;  

import java.io.DataInputStream;  
import java.io.IOException;  
import java.io.OutputStream;  
import java.net.Socket;  

@Slf4j  
public class SocketReceive implements Runnable {  
    private Socket socket;  

    public SocketReceive() {  
    }  

    public SocketReceive(Socket socket) {  
        this.socket = socket;  
    }  

    @Override  
    public void run() {  
        while (true) {  
            if (null == socket) {  
                log.info("socket为空");  
                return;  
            }  
            boolean isClosed = socket.isClosed();  
            String hostAddress = socket.getInetAddress().getHostAddress();  
            if (isClosed) {  
                log.info("socket检测到关闭了");  
                if (AllClientsMap.contains(hostAddress)) {  
                    AllClientsMap.remove(hostAddress);  
                    AllClientsMap.print();  
                }  
                return;  
            }  
            String hostAddress = socket.getInetAddress().getHostAddress();  
            try {  
                //建立客户端信息输入流  
                DataInputStream in = new DataInputStream(socket.getInputStream());  
                //定义字节数组读取数据  
                byte[] bytes = new byte[1024];  
                int len = in.read(bytes);  
                if (len == -1) {  
                    return;  
                }  
                //定义一个新数组copy,解决读取出来的数据字节不够全是0的问题  
                byte[] bytes1 = new byte[len];  
                System.arraycopy(bytes, 0, bytes1, 0, len);  
                log.info("客户端传的byte字节数组:" + printBytesByStringBuilder(bytes1));  
                String s = new String(bytes1);  
                log.info("客户端传的byte字节数组转换成字符串打印:" + s);  
                //转换hex数据  
                String data = byteArrayToHex(bytes1);  
                log.info("接收的16进制数据:" + data);  
                //如果服务端没有保存该socket  
                if(!AllClientsMap.contains(hostAddress)){  
                    AllClientsMap.put(hostAddress, socket);  
                    AllClientsMap.print();  
                }  
                log.debug("客户端" + hostAddress + "发送数据:{}", data);  
                //执行业务  
                System.out.println("执行业务");  

                //从map中获取客户端发送消息  
                response(AllClientsMap.getSocketByKey(hostAddress), data);  

            } catch (IOException e) {  
                if (AllClientsMap.contains(hostAddress)) {  
                    AllClientsMap.remove(hostAddress);  
                    AllClientsMap.print();  
                }  
                try {  
                    socket.close();  
                    log.error("{}断开连接", hostAddress);  
                    return;  
                } catch (IOException ioException) {  
                    log.error(ioException.getMessage());  
                }  
            }  
        }  
    }  


    /*         */  
    public static String printBytesByStringBuilder(byte[] bytes) {  
        StringBuilder stringBuilder = new StringBuilder();  
        for (byte aByte : bytes) {  
            stringBuilder.append(byte2String(aByte));  
        }  
        return stringBuilder.toString();  
    }  

    public static String byte2String(byte b) {  
        return String.format("%02x ", b);  
    }  

    /*         */  
    public static void response(Socket socket, String msg) {  
        log.debug("向设备IP:{}发送消息:{}", socket.getInetAddress().getHostAddress(), msg);  
        OutputStream outputStream = null;  
        try {  
            outputStream = socket.getOutputStream();  
            outputStream.write(hexStringToByteArray(msg));  
        } catch (IOException e) {  
            try {  
                socket.close();  
            } catch (IOException ioException) {  
                log.error(ioException.getMessage());  
            }  
            log.error(e.getMessage());  
        }  
    }  

    /*         */  
    public static String byteArrayToHex(byte[] bytes) {  
        StringBuilder result = new StringBuilder();  
        for (int index = 0, len = bytes.length; index <= len - 1; index += 1) {  
            int char1 = ((bytes[index] >> 4) & 0xF);  
            char chara1 = Character.forDigit(char1, 16);  
            int char2 = ((bytes[index]) & 0xF);  
            char chara2 = Character.forDigit(char2, 16);  
            result.append(chara1);  
            result.append(chara2);  
        }  
        return result.toString();  
    }  


    /*         */  
    public static byte[] hexStringToByteArray(String hexString) {  
        hexString = hexString.replaceAll(" ", "");  
        int len = hexString.length();  
        byte[] bytes = new byte[len / 2];  
        for (int i = 0; i < len; i += 2) {  
            // 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节  
            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character  
                    .digit(hexString.charAt(i + 1), 16));  
        }  
        return bytes;  
    }  
}  



 __JAVA  

---|---

启动

通过继承Spring的InitializingBean类,重写afterPropertiesSet方法,这个方法将在所有的属性被初始化后调用。
然后会创建一个线程执行ServerSocket的监听,初始化我们的TcpSocket对象,一旦Server接收到了连接请求后,会创建一个SocketReceive对象将其扔给线程池执行,在线程池中的SocketReceive对象可以通过ApplicationContext获取Spring Bean执行业务代码。

|

package com.example.socket_demo.socket;  

import org.springframework.beans.factory.InitializingBean;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  

@Component  
public class SpringFinishedListener implements InitializingBean {  
    @Autowired  
    private TcpSocket tcpsocket;  

    @Override  
    public void afterPropertiesSet() {  
        Thread serverThread = new Thread(tcpsocket);  
        serverThread.start();  
    }  
}  



 __JAVA  

---|---

测试工具

这里推荐一个测试工具,还挺好用的。下载连接

文章评论