Java的Socket实现长连接以及数据的发送和接收

既然是长连接就免不了心跳检测,这里使用了一种比较简单的做法:服务端对当前线程计时,重要的话说三遍,服务端、服务端、服务端!如果超时没有收到任何数据就关闭该线程对应的Socket。代码复制粘贴即可运行。

发送时:将String转byte[]

接收时:将byte[]转String

效果图

客户端代码



import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class SocketClient
{
    public static void main(String[] args)
    {
        try
        {
            Socket socket = new Socket("localhost", 8888);
            //得到一个输出流,用于向服务器发送数据
            OutputStream outputStream = socket.getOutputStream();
            System.out.println("请输入16进制数据:");
            Scanner sc = new Scanner(System.in);
            while (true)
            {
                String data = sc.nextLine();
                if ("exit".equals(data))
                {
                    return;
                }
                byte[] byteArray = HexStrToByteArray(data);
                outputStream.write(byteArray);
                //刷新缓冲
                outputStream.flush();
                //得到一个输入流,用于接收服务器响应的数据
                InputStream inputStream = socket.getInputStream();
                byte[] bytes = new byte[1]; // 一次读取一个byte
                String info = "";
                while (true)
                {
                    if (inputStream.available() > 0)
                    {
                        inputStream.read(bytes);
                        String hexStr = ByteArrayToHexStr(bytes);
                        info += HexStrToStr(hexStr);
                        //已经读完
                        if (inputStream.available() == 0)
                        {
                            System.out.println("收到来自服务端的信息:" + info);
                            break;
                        }
                    }
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 16进制Str转byte[]
     *
     * @param hexStr
     * @return
     */
    public static byte[] HexStrToByteArray(String hexStr)
    {
        if (hexStr == null)
        {
            return null;
        }
        if (hexStr.length() == 0)
        {
            return new byte[0];
        }
        byte[] byteArray = new byte[hexStr.length() / 2];
        for (int i = 0; i < byteArray.length; i++)
        {
            String subStr = hexStr.substring(2 * i, 2 * i + 2);
            byteArray[i] = ((byte) Integer.parseInt(subStr, 16));
        }
        return byteArray;
    }

    /**
     * byte[]转16进制Str
     *
     * @param byteArray
     */
    public static String ByteArrayToHexStr(byte[] byteArray)
    {
        if (byteArray == null)
        {
            return null;
        }
        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[byteArray.length * 2];
        for (int i = 0; i < byteArray.length; i++)
        {
            int temp = byteArray[i] & 0xFF;
            hexChars[i * 2] = hexArray[temp >>> 4];
            hexChars[i * 2 + 1] = hexArray[temp & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * 16进制的Str转Str
     *
     * @param hexStr
     * @return
     */
    public static String HexStrToStr(String hexStr)
    {
        //能被16整除,肯定可以被2整除
        byte[] array = new byte[hexStr.length() / 2];
        try
        {
            for (int i = 0; i < array.length; i++)
            {
                array[i] = (byte) (0xff & Integer.parseInt(hexStr.substring(i * 2, i * 2 + 2), 16));
            }
            hexStr = new String(array, "UTF-8");
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return "";
        }
        return hexStr;
    }

}

服务端代码

使用InputStream对象的available()方法判断客户端的内容是否发送完毕

dataInputStream.available()

官方解释:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。下一个调用可能是同一个线程,也可能是另一个线程。一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。用我的大白话就是:返回剩余未读长度


import java.io.*;
import java.net.Socket;

/**
 * 长连接
 */
public class ServerThread extends Thread
{
    //16进制数字字符集
    public static final String HEXSTRING = "0123456789ABCDEF";
    //心跳超时时间
    private static final int TIMEOUT = 60 * 1000;
    private Socket m_socket;
    //接收到数据的最新时间
    private long m_lastReceiveTime = System.currentTimeMillis();
    //该线程是否正在运行
    private boolean m_isRuning = false;

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

    @Override
    public void start()
    {
        if (m_isRuning)
        {
            System.out.println(">>>线程" + this.getId() + "启动失败,该线程正在执行");
            return;
        }
        else
        {
            m_isRuning = true;
            super.start();
        }
    }

    @Override
    public void run()
    {
        //字节输入流
        InputStream inputStream = null;
        //字节输出流
        OutputStream outputStream = null;
        try
        {
            inputStream = m_socket.getInputStream();
            outputStream = m_socket.getOutputStream();
            String info = "";
            //按byte读
            byte[] bytes = new byte[1];
            while (m_isRuning)
            {
                //检测心跳
                if (System.currentTimeMillis() - m_lastReceiveTime > TIMEOUT)
                {
                    m_isRuning = false;
                    //跳出,执行finally块
                    break;
                }
                //返回下次调用可以不受阻塞地从此流读取或跳过的估计字节数,如果等于0则表示已经读完
                if (inputStream.available() > 0)
                {
                    //重置接收到数据的最新时间
                    m_lastReceiveTime = System.currentTimeMillis();
                    inputStream.read(bytes);
                    String tempStr = ByteArrayToHexStr(bytes) ;
                    info += tempStr;
                    //已经读完
                    if (inputStream.available() == 0)
                    {
                        System.out.println(">>>线程" + this.getId() + "收到:" + info);
                        String responseStr = "Hello";
                        //响应内容
                        String hexStr = StrToHexStr(responseStr);
                        hexStr = hexStr.replaceAll("0[x|X]|,","");
                        byte[] byteArray = HexStrToByteArray(hexStr);
                        outputStream.write(byteArray);
                        outputStream.flush();
                        //重置,不然每次收到的数据都会累加起来
                        info = "";
                        System.out.println(">>>线程" + this.getId() + "回应:" + responseStr);
                    }
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        //关闭资源
        finally
        {
            System.out.println(">>>线程" + this.getId() + "的连接已断开\n");
            try
            {
                if (outputStream != null)
                    outputStream.close();
                if (inputStream != null)
                    inputStream.close();
                if (m_socket != null)
                    m_socket.close();
                m_isRuning = false;
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * byte[]转16进制Str
     *
     * @param byteArray
     */
    public static String ByteArrayToHexStr(byte[] byteArray)
    {
        if (byteArray == null)
        {
            return null;
        }
        char[] hexArray = HEXSTRING.toCharArray();
        char[] hexChars = new char[byteArray.length * 2];
        for (int i = 0; i < byteArray.length; i++)
        {
            int temp = byteArray[i] & 0xFF;
            hexChars[i * 2] = hexArray[temp >>> 4];
            hexChars[i * 2 + 1] = hexArray[temp & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * Str转16进制Str
     *
     * @param str
     * @return
     */
    public static String StrToHexStr(String str)
    {
        //根据默认编码获取字节数组
        byte[] bytes = str.getBytes();
        StringBuilder stringBuilder = new StringBuilder(bytes.length * 2);
        //将字节数组中每个字节拆解成2位16进制整数
        for (int i = 0; i < bytes.length; i++)
        {
            stringBuilder.append("0x");
            stringBuilder.append(HEXSTRING.charAt((bytes[i] & 0xf0) >> 4));
            stringBuilder.append(HEXSTRING.charAt((bytes[i] & 0x0f) >> 0));
            //去掉末尾的逗号
            if (i != bytes.length - 1)
            {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 16进制Str转byte[]
     *
     * @param hexStr 不带空格、不带0x、不带逗号的16进制Str,如:06EEF7F1
     * @return
     */
    public static byte[] HexStrToByteArray(String hexStr)
    {
        byte[] byteArray = new byte[hexStr.length() / 2];
        for (int i = 0; i < byteArray.length; i++)
        {
            String subStr = hexStr.substring(2 * i, 2 * i + 2);
            byteArray[i] = ((byte) Integer.parseInt(subStr, 16));
        }
        return byteArray;
    }

}

开启服务端


import java.net.ServerSocket;
import java.net.Socket;

public class MySocketServer
{
    public static void main(String[] args)
    {
        try
        {
            System.out.println(">>>服务启动,等待终端的连接\n");
            ServerSocket server = new ServerSocket(8888);
            int count = 0;
            while (true)
            {
                //开启监听
                Socket socket = server.accept();
                count++;
                System.out.println(">>>开启第" + count + "次长连接...");
                ServerThread thread = new ServerThread(socket);
                thread.start();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

}

  • 6
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是 Java Socket 客户端实现断线重连的代码示例: ```java public class SocketClient { private String host; private int port; private Socket socket; private boolean isConnected = false; private int maxReconnectTimes = 5; private int currentReconnectTimes = 0; public SocketClient(String host, int port) { this.host = host; this.port = port; } public void connect() { while (true) { try { socket = new Socket(host, port); isConnected = true; currentReconnectTimes = 0; // 连接成功后进行通信 communicate(); } catch (IOException e) { isConnected = false; currentReconnectTimes++; if (currentReconnectTimes > maxReconnectTimes) { // 达到最大重连次数后退出重连 System.out.println("重连次数已达到最大值,退出重连"); break; } try { // 重连间隔为1秒,可以根据实际情况进行调整 Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("连接失败,正在进行第" + currentReconnectTimes + "次重连..."); } } } private void communicate() throws IOException { // 连接成功后进行通信 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); while (isConnected) { // 发送数据 out.println("Hello, server!"); // 接收数据 String line = in.readLine(); System.out.println("Received from server: " + line); // 模拟连接 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 在主程序中,可以创建 SocketClient 对象并进行连接: ```java public class Main { public static void main(String[] args) { SocketClient client = new SocketClient("127.0.0.1", 8000); client.connect(); } } ``` 在 SocketClient 中,使用了 while 循环进行断线重连,并且在重连失败后使用 Thread.sleep() 方法等待一段时间再进行重连,避免频繁重连导致服务器受到过多的连接请求。在每次连接成功后,使用 communicate() 方法进行通信,并在通信过程中模拟连接

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值