Socket 搭建服务端,支持多个客户端连接,解决粘包问题

首先在项目启动的时候开启 socket 服务,我这是使用的springboot

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

@Component
@Order(value = 2)
public class SocketServiceRun implements CommandLineRunner {
    private final static Logger logger = LoggerFactory.getLogger(SocketServiceRun.class);

    private static final int PORT = 99999;

    @Override
    public void run(String... args) throws Exception {
        try {
            //建立服务器连接,设定客户连接请求队列的长度 ↓↓↓↓
            ServerSocket server = new ServerSocket(PORT,50);
            logger.info("Socket 服务已开启,等待连接:"+ PORT);
            while (true) {
                // 等待客户连接
                Socket socket = server.accept();
                //连接后30秒断开连接
                //socket.setSoTimeout(30000);
                // 每个客户端都给单独开一个线程
                new Thread(new ServerThread(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

线程实现类
这个地方有很多人直接使用 InputStream 来进行读流 ,会遇到的问题就是:不知道是否全部读取完毕,所以需要在参数中添加特殊符号进行分割,也就是大部分博客中解决粘包问题的方案,我这边直接使用 DataInputStream 来进行读取,有提供 available() 判断是否是一个完整的请求,这样就很好解决粘包问题了

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ServerThread implements Runnable {
    private final static Logger logger = LoggerFactory.getLogger(ServerThread.class);
    private Socket socket;

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

    // 任务是为一个用户提供服务
    @Override
    public void run() {
        try {
            //封装输入流(接收客户端的流)
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            // 读取客户端传过来信息的DataInputStream
            DataInputStream dis = new DataInputStream(bis);
            // 向客户端发送信息的DataOutputStream
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());

            /*int count = in.available(); // 获取整个请求字节长度
            byte[] bytes = new byte[count];
            dis.read(bytes);
            在进行网络操作时往往出错,因为你调用available()方法时,对方发送的数据可能还没有到达,你得到的count是0。
            需要改成这样:
            int count = 0;
            while (count == 0) {
                count = dis.available();
            }*/
            byte[] bytes = new byte[1]; // 一次性读取一个字节
            byte[] newBytes = new byte[0];
            String message = "";
            while (dis.read(bytes) != -1) {
                newBytes = StringUtil.byteMerger(newBytes, bytes);
                if (dis.available() == 0) { // read(bytes) 之后 dis 中没有字节了 ,表示一个请求全部读取完毕了
                    message = new String(newBytes, "GBK"); // 客户端传过来的是 ascii 码
                    // message = StringUtil.bytesToHexString(bytes) + ""; // 客户端传过来的是 hex(十六进制) 格式数据
                    // message = new String(newBytes);  //客户端传过来的是 文本格式
                    logger.info("接收到客户端数据:" + message);
                    // 以下进行业务操作
                    /*XXXServiceImpl service = SpringUtils.getBean("XXXServiceImpl");
                    String result = service.addData(message.trim()); // 除去首尾空格

                    // 清空这次请求的数据 方便下次接收
                    newBytes = new byte[0];
                    message = new String();
                    out.write(result.getBytes());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }
    }
}

下面是 StringUtil 工具类

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by AnZhi_Liu on 2021/1/15 0015 16:00
 */
public class StringUtil {
    /**
     * 去除字符串中的空格 、回车 、换行符、制表符
     *
     * @param str
     * @return
     */
    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }

    /**
     * 判断Object对象为空或空字符串
     *
     * @param obj
     * @return
     */
    public static Boolean isObjectNotEmpty(Object obj) {
        String str = ObjectUtils.toString(obj, "");
        Boolean flag = StringUtils.isNotBlank(str);
        return flag;
    }

    /**
     * 判断Object对象为空或空字符串
     *
     * @param obj
     * @return
     */
    public static Boolean isObjectEmpty(Object obj) {
        String str = ObjectUtils.toString(obj, "");
        return !StringUtils.isNotBlank(str);
    }

    /**
     * byte[]数组转换为16进制的字符串
     *
     * @param bytes 要转换的字节数组
     * @return 转换后的结果
     */
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 16进制的字符串转成字节数组
     *
     * @param hexString 16进制格式的字符串
     * @return 转换后的字节数组
     **/
    public static byte[] toByteArray(String hexString) {
        hexString = hexString.replaceAll("", "");
        final byte[] byteArray = new byte[hexString.length() / 2];
        int k = 0;
        for (int i = 0; i < byteArray.length; i++) {//因为是16进制,最多只会占用4位,转换成字节需要两个16进制的字符,高位在先
            byte high = (byte) (Character.digit(hexString.charAt(k), 16) & 0xff);
            byte low = (byte) (Character.digit(hexString.charAt(k + 1), 16) & 0xff);
            byteArray[i] = (byte) (high << 4 | low);
            k += 2;
        }
        return byteArray;
    }

    /**
     * 字符串转换为 ASCLL
     *
     * @param value
     * @return
     */
    public static String stringToAscii(String value) {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (i != chars.length - 1) {
                sbu.append((int) chars[i]).append(",");
            } else {
                sbu.append((int) chars[i]);
            }
        }
        return sbu.toString();

    }

    /**
     * ASCLL 转换为字符串
     *
     * @param value
     * @return
     */
    public static String asciiToString(String value) {
        StringBuffer sbu = new StringBuffer();
        String[] chars = value.split(",");
        for (int i = 0; i < chars.length; i++) {
            sbu.append((char) Integer.parseInt(chars[i]));
        }
        return sbu.toString();
    }

    /**
     * 16进制字符串 转换成为string类型字符串
     *
     * @param s
     * @return
     */
    public static String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

    /**
     * 字符串转换成为16进制(无需Unicode编码)
     *
     * @param str
     * @return
     */
    public static String str2HexStr(String str) {
        char[] chars = "0123456789ABCDEF".toCharArray();
        StringBuilder sb = new StringBuilder("");
        byte[] bs = str.getBytes();
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i] & 0x0f0) >> 4;
            sb.append(chars[bit]);
            bit = bs[i] & 0x0f;
            sb.append(chars[bit]);
            // sb.append(' ');
        }
        return sb.toString().trim();
    }

    /**
     * 合并两个byte数组
     *
     * @param byte_1
     * @param byte_2
     * @return
     */
    public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) {
        byte[] byte_3 = new byte[byte_1.length + byte_2.length];
        System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);
        System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);
        return byte_3;
    }
}

你可能感兴趣的:(java工具类,多线程,socket,java)