做个笔记,在接收16进制数据的时候乱码了。原因是Socket在接收数据的时候需要根据不同的数据定义不同的接收方式,也就是约定好传输协议(具体体现在后面服务端接收16进制那里)。
字符串发送:
字符串接收:
客户端代码:
没什么好说的,复制粘贴导包。
public static void main(String[] args) { try { Socket socket = new Socket("localhost", 8888); //得到一个输出流,用于向服务器发送数据 OutputStream outputStream = socket.getOutputStream(); //将写入的字符编码成字节后写入一个字节流 OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); System.out.println("请输入数据:"); while (true) { Scanner sc = new Scanner(System.in); String data = sc.nextLine(); writer.write(data); //刷新缓冲 writer.flush(); //只关闭输出流而不关闭连接 socket.shutdownOutput(); //获取服务器端的响应数据 //得到一个输入流,用于接收服务器响应的数据 InputStream inputStream = socket.getInputStream(); //字节流以UTF-8的编码转换为字符流 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); //为输入流添加缓冲 BufferedReader bufferedReader = new BufferedReader(inputStreamReader); System.out.println("客户端IP地址:" socket.getInetAddress().getHostAddress()); String info; //输出服务器端响应数据 while ((info = bufferedReader.readLine()) != null) { System.out.println("收到来自服务端的信息:" info); } //关闭资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); writer.close(); outputStream.close(); socket.close(); } } catch (IOException e) { e.printStackTrace(); } }
服务端代码:
注意readLine()方法,客户端发送的内容结尾一定要有换行符,不然会一直阻塞导致客户端得不到响应。其它注释里都有。
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 (IOException e) { e.printStackTrace(); }
/** * 短连接 */public class ServerThread extends Thread{ private Socket m_socket; public ServerThread(Socket socket) { this.m_socket = socket; } @Override public void run() { //字节输入流 InputStream inputStream = null; //字节输出流 OutputStream outputStream = null; //字节输入流到字符输入流的转换 InputStreamReader inputStreamReader = null; //加快字符读取速度 BufferedReader bufferedReader = null; //打印输出流 PrintWriter printWriter = null; try { inputStream = m_socket.getInputStream(); String info; inputStreamReader = new InputStreamReader(inputStream); bufferedReader = new BufferedReader(inputStreamReader); //注意,readLine()方法如果没有读到报文结束符(换行)会一直阻塞 while ((info = bufferedReader.readLine()) != null) { System.out.println(">>>线程" this.getId() "收到来自终端的信息:" info); } //关闭终端的输入流(不关闭服务端的输出流),此时m_socket虽然没有关闭,但是客户端已经不能再发送消息 m_socket.shutdownInput(); //解析终端的信息 String responseStr = "Null..."; if (null != info && !"".equals(info)) { //模拟业务处理Thread.sleep(10000); responseStr = new MessageReceive().RecevieHexStr(info); } outputStream = m_socket.getOutputStream(); printWriter = new PrintWriter(outputStream); printWriter.write(responseStr); printWriter.flush(); } catch (Exception e) { e.printStackTrace(); } //关闭资源 finally { System.out.println(">>>本次连接已断开\n"); try { if (printWriter != null) printWriter.close(); if (outputStream != null) outputStream.close(); if (bufferedReader != null) bufferedReader.close(); if (inputStreamReader != null) inputStreamReader.close(); if (inputStream != null) inputStream.close(); if (m_socket != null) m_socket.close(); } catch (IOException e) { e.printStackTrace(); } } }}
客户端代码:无
由Socket测试工具来完成,网上大把,自行下载。
服务端代码:
开头说过使用Socket通信需要约定好协议,否则服务端不知道你客户端内容是否发送完毕,就会一直阻塞。前面的字符串的发送和接收其实也是有协议存在的,这个协议就是字符串和换行符,服务端读取的时候将字节流转为字符流,然后一行行的读取,读到换行符的时候就表示客户端的消息已发送完毕。
但是当客户端发送的是16进制数据的时候,这个时候服务端仍然用读取字符串的方式去处理就会乱码。所以这里采用的方法是将字节流以字节的形式一个一个的读出来,然后每个字节转为16进制的字符串。
byte[] bytes = new byte[1];while ((dataInputStream.read(bytes)) != -1) String tempStr = ConvertUtil.ByteArrayToHexStr(bytes);
同理,也需要一个标识符表示客户端的内容已经发送完毕,否则read()方法也会一直阻塞,导致客户端得不到响应。比如客户端发送内容的末尾加个7E,当服务端读到7E就跳出While循环,但前提是要保证发送内容里面7E是唯一的,如果有其它需要用7E表示的自行转义,然后服务端读取完内容以后再转义回来。
这里我使用的是另外一种方法判断客户端的内容是否发送完毕:InputStream对象的available()方法。
dataInputStream.available()
官方解释:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。下一个调用可能是同一个线程,也可能是另一个线程。一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。大白话就是:返回剩余未读长度
至于其它的注释里都有写
/** * 短连接 */public class ServerThread extends Thread{ private Socket m_socket; public ServerThread(Socket socket) { this.m_socket = socket; } @Override public void run() { //字节输入流 InputStream inputStream = null; //字节输出流 OutputStream outputStream = null; //缓冲输入流 BufferedInputStream bufferedInputStream = null; //数据输入流 DataInputStream dataInputStream = null; //打印输出流 PrintWriter printWriter = null; try { inputStream = m_socket.getInputStream(); String info = ""; bufferedInputStream = new BufferedInputStream(inputStream); dataInputStream = new DataInputStream(bufferedInputStream); //一次读取一个byte byte[] bytes = new byte[1]; //注意,read()方法如果没有数据会一直阻塞,也就是永远不会等于-1,除非客户端调用close(),如果想在while循环外部获取数据则需要设定跳出条件 while ((dataInputStream.read(bytes)) != -1) { String tempStr = ConvertUtil.ByteArrayToHexStr(bytes) " "; info = tempStr; //返回下次调用可以不受阻塞地从此流读取或跳过的估计字节数,如果等于0则表示已经读完 if (dataInputStream.available() == 0) { System.out.println(">>>终端信息读取完毕,最后一位:" tempStr); break; } } System.out.println(">>>线程" this.getId() "收到来自终端的信息:" info); //关闭终端的输入流(不关闭服务端的输出流),此时m_socket虽然没有关闭,但是客户端已经不能再发送消息 m_socket.shutdownInput(); //解析终端的信息 String responseStr = "Null..."; //模拟业务处理 Thread.sleep(10000); outputStream = m_socket.getOutputStream(); printWriter = new PrintWriter(outputStream); printWriter.write(responseStr); printWriter.flush(); } catch (Exception e) { e.printStackTrace(); } //关闭资源 finally { System.out.println(">>>本次连接已断开\n"); try { if (printWriter != null) printWriter.close(); if (outputStream != null) outputStream.close(); if (inputStream != null) inputStream.close(); if (bufferedInputStream != null) bufferedInputStream.close(); if (dataInputStream != null) dataInputStream.close(); if (m_socket != null) m_socket.close(); } catch (IOException e) { e.printStackTrace(); } } }}
自定义的转换类:
/** * 普通byte[]转16进制Str * * @param array */ public static String ByteArrayToHexStr(byte[] array) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < array.length; i ) { String hex = Integer.toHexString(array[i] & 0xFF); if (hex.length() == 1) { stringBuilder.append("0"); } stringBuilder.append(hex); } return stringBuilder.toString(); }
来源:https://www.icode9.com/content-1-373801.html
联系客服