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

 更新时间:2023年09月28日 10:14:42   作者:谁把我名字用了!  
这篇文章主要介绍了Java的Socket实现长连接以及数据的发送和接收方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

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();
        }
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中POST、GET、@RequestBody和@RequestParam区别详析

    Java中POST、GET、@RequestBody和@RequestParam区别详析

    在前后端传json数据进行交互的时候,同学们会经常用到的两个注解,@RequestBody和@RequestParam主要是用来接收前端传给后端的json数据,下面这篇文章主要给大家介绍了关于Java中POST、GET、@RequestBody和@RequestParam区别的相关资料,需要的朋友可以参考下
    2022-10-10
  • Java面试题冲刺第十天--MyBatis2

    Java面试题冲刺第十天--MyBatis2

    这篇文章主要为大家分享了最有价值的三道MyBatis框架面试题,涵盖内容全面,包括数据结构和算法相关的题目、经典面试编程题等,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Java基础第五篇 实施接口

    Java基础第五篇 实施接口

    在public和private的封装机制,我们实际上同时定义了类和接口,类和接口混合在一起。Java还提供了interface这一语法。这一语法将接口从类的具体定义中剥离出来,构成一个独立的主体,下面文章内容将为大家做详细介绍
    2021-09-09
  • Java字符串处理全解析(String、StringBuilder与StringBuffer)

    Java字符串处理全解析(String、StringBuilder与StringBuffer)

    这篇文章主要介绍了Java字符串处理全解析(String、StringBuilder与StringBuffer),本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-04-04
  • java使用poi读取doc和docx文件的实现示例

    java使用poi读取doc和docx文件的实现示例

    这篇文章主要介绍了java使用poi读取doc和docx文件的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • SpringBoot和Springfox(Swagger)版本不兼容的解决方案

    SpringBoot和Springfox(Swagger)版本不兼容的解决方案

    documentationPluginsBootstrapper这个 bean 无法正常启动,原因是遇到了空指针异常(NullPointerException),这通常是由于 Spring Boot 和 Springfox 的版本不兼容导致的路径匹配策略冲突,本文给大家介绍了SpringBoot和Springfox(Swagger)版本不兼容的解决方案
    2024-12-12
  • 关于scanner.nextInt()等next()和scanner.nextIine()连用注意事项

    关于scanner.nextInt()等next()和scanner.nextIine()连用注意事项

    这篇文章主要介绍了关于scanner.nextInt()等next()和scanner.nextIine()连用注意事项,具有很好的参考价值,希望对大家有所帮助。
    2023-04-04
  • Java设计模式之迭代器模式解析

    Java设计模式之迭代器模式解析

    这篇文章主要介绍了Java设计模式之迭代器模式解析,迭代器模式提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示,本文提供了部分代码,需要的朋友可以参考下
    2023-09-09
  • 详解Java HashMap实现原理

    详解Java HashMap实现原理

    HashMap是基于哈希表的Map接口实现,提供了所有可选的映射操作,并允许使用null值和null建,不同步且不保证映射顺序。本文将记录一下研究HashMap实现原理。
    2017-01-01
  • Java Spring Dubbo三种SPI机制的区别

    Java Spring Dubbo三种SPI机制的区别

    这篇文章主要介绍了Java Spring Dubbo三种SPI机制的区别,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-08-08

最新评论