Java的Socket实现长连接以及数据的发送和接收方式
作者:谁把我名字用了!
这篇文章主要介绍了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(); } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。