做個筆記,在接收16進制數(shù)據(jù)的時候亂碼了。原因是Socket在接收數(shù)據(jù)的時候需要根據(jù)不同的數(shù)據(jù)定義不同的接收方式,也就是約定好傳輸協(xié)議(具體體現(xiàn)在后面服務(wù)端接收16進制那里)。
字符串發(fā)送:
字符串接收:
客戶端代碼:
沒什么好說的,復(fù)制粘貼導(dǎo)包。
public static void main(String[] args) { try { Socket socket = new Socket("localhost", 8888); //得到一個輸出流,用于向服務(wù)器發(fā)送數(shù)據(jù) OutputStream outputStream = socket.getOutputStream(); //將寫入的字符編碼成字節(jié)后寫入一個字節(jié)流 OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); System.out.println("請輸入數(shù)據(jù):"); while (true) { Scanner sc = new Scanner(System.in); String data = sc.nextLine(); writer.write(data); //刷新緩沖 writer.flush(); //只關(guān)閉輸出流而不關(guān)閉連接 socket.shutdownOutput(); //獲取服務(wù)器端的響應(yīng)數(shù)據(jù) //得到一個輸入流,用于接收服務(wù)器響應(yīng)的數(shù)據(jù) InputStream inputStream = socket.getInputStream(); //字節(jié)流以UTF-8的編碼轉(zhuǎn)換為字符流 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); //為輸入流添加緩沖 BufferedReader bufferedReader = new BufferedReader(inputStreamReader); System.out.println("客戶端IP地址:" socket.getInetAddress().getHostAddress()); String info; //輸出服務(wù)器端響應(yīng)數(shù)據(jù) while ((info = bufferedReader.readLine()) != null) { System.out.println("收到來自服務(wù)端的信息:" info); } //關(guān)閉資源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); writer.close(); outputStream.close(); socket.close(); } } catch (IOException e) { e.printStackTrace(); } }
服務(wù)端代碼:
注意readLine()方法,客戶端發(fā)送的內(nèi)容結(jié)尾一定要有換行符,不然會一直阻塞導(dǎo)致客戶端得不到響應(yīng)。其它注釋里都有。
try { System.out.println(">>>服務(wù)啟動,等待終端的連接\n"); ServerSocket server = new ServerSocket(8888); int count = 0; while (true) { //開啟監(jiān)聽 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() { //字節(jié)輸入流 InputStream inputStream = null; //字節(jié)輸出流 OutputStream outputStream = null; //字節(jié)輸入流到字符輸入流的轉(zhuǎn)換 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()方法如果沒有讀到報文結(jié)束符(換行)會一直阻塞 while ((info = bufferedReader.readLine()) != null) { System.out.println(">>>線程" this.getId() "收到來自終端的信息:" info); } //關(guān)閉終端的輸入流(不關(guān)閉服務(wù)端的輸出流),此時m_socket雖然沒有關(guān)閉,但是客戶端已經(jīng)不能再發(fā)送消息 m_socket.shutdownInput(); //解析終端的信息 String responseStr = "Null..."; if (null != info && !"".equals(info)) { //模擬業(yè)務(wù)處理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(); } //關(guān)閉資源 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測試工具來完成,網(wǎng)上大把,自行下載。
服務(wù)端代碼:
開頭說過使用Socket通信需要約定好協(xié)議,否則服務(wù)端不知道你客戶端內(nèi)容是否發(fā)送完畢,就會一直阻塞。前面的字符串的發(fā)送和接收其實也是有協(xié)議存在的,這個協(xié)議就是字符串和換行符,服務(wù)端讀取的時候?qū)⒆止?jié)流轉(zhuǎn)為字符流,然后一行行的讀取,讀到換行符的時候就表示客戶端的消息已發(fā)送完畢。
但是當(dāng)客戶端發(fā)送的是16進制數(shù)據(jù)的時候,這個時候服務(wù)端仍然用讀取字符串的方式去處理就會亂碼。所以這里采用的方法是將字節(jié)流以字節(jié)的形式一個一個的讀出來,然后每個字節(jié)轉(zhuǎn)為16進制的字符串。
byte[] bytes = new byte[1];while ((dataInputStream.read(bytes)) != -1) String tempStr = ConvertUtil.ByteArrayToHexStr(bytes);
同理,也需要一個標(biāo)識符表示客戶端的內(nèi)容已經(jīng)發(fā)送完畢,否則read()方法也會一直阻塞,導(dǎo)致客戶端得不到響應(yīng)。比如客戶端發(fā)送內(nèi)容的末尾加個7E,當(dāng)服務(wù)端讀到7E就跳出While循環(huán),但前提是要保證發(fā)送內(nèi)容里面7E是唯一的,如果有其它需要用7E表示的自行轉(zhuǎn)義,然后服務(wù)端讀取完內(nèi)容以后再轉(zhuǎn)義回來。
這里我使用的是另外一種方法判斷客戶端的內(nèi)容是否發(fā)送完畢:InputStream對象的available()方法。
dataInputStream.available()
官方解釋:返回此輸入流下一個方法調(diào)用可以不受阻塞地從此輸入流讀取(或跳過)的估計字節(jié)數(shù)。下一個調(diào)用可能是同一個線程,也可能是另一個線程。一次讀取或跳過此估計數(shù)個字節(jié)不會受阻塞,但讀取或跳過的字節(jié)數(shù)可能小于該數(shù)。大白話就是:返回剩余未讀長度
至于其它的注釋里都有寫
/** * 短連接 */public class ServerThread extends Thread{ private Socket m_socket; public ServerThread(Socket socket) { this.m_socket = socket; } @Override public void run() { //字節(jié)輸入流 InputStream inputStream = null; //字節(jié)輸出流 OutputStream outputStream = null; //緩沖輸入流 BufferedInputStream bufferedInputStream = null; //數(shù)據(jù)輸入流 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()方法如果沒有數(shù)據(jù)會一直阻塞,也就是永遠(yuǎn)不會等于-1,除非客戶端調(diào)用close(),如果想在while循環(huán)外部獲取數(shù)據(jù)則需要設(shè)定跳出條件 while ((dataInputStream.read(bytes)) != -1) { String tempStr = ConvertUtil.ByteArrayToHexStr(bytes) " "; info = tempStr; //返回下次調(diào)用可以不受阻塞地從此流讀取或跳過的估計字節(jié)數(shù),如果等于0則表示已經(jīng)讀完 if (dataInputStream.available() == 0) { System.out.println(">>>終端信息讀取完畢,最后一位:" tempStr); break; } } System.out.println(">>>線程" this.getId() "收到來自終端的信息:" info); //關(guān)閉終端的輸入流(不關(guān)閉服務(wù)端的輸出流),此時m_socket雖然沒有關(guān)閉,但是客戶端已經(jīng)不能再發(fā)送消息 m_socket.shutdownInput(); //解析終端的信息 String responseStr = "Null..."; //模擬業(yè)務(wù)處理 Thread.sleep(10000); outputStream = m_socket.getOutputStream(); printWriter = new PrintWriter(outputStream); printWriter.write(responseStr); printWriter.flush(); } catch (Exception e) { e.printStackTrace(); } //關(guān)閉資源 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(); } } }}
自定義的轉(zhuǎn)換類:
/** * 普通byte[]轉(zhuǎn)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