做个笔记,在接收16进制数据的时候乱码了,原因是没有转byte[]发送。

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

发送时:将16进制的String转byte[]

接收时:将读取到的byte[]转为16进制的String

运行结果

dd21d625074a4d6b9ccba987fbdb37b8.jpg

客户端

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

}

}

}

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐