以前在工做中寫過一些Socket客戶端與服務端的代碼,可是當時沒有時間仔細研究,只能不報錯先過的態度,對其細節瞭解不深,寫的代碼有各類問題也渾然不知,只是業務量級以及對接方對接代碼沒有出現出格的狀況因此問題不得暴露。app
首先經過單線程Socket作服務端是一種BIO的作法,這種作法會致使服務端只能同時接收一筆請求,性能很是差dom
下面我把BIO的代碼帖一下,有須要的同窗能夠參考socket
public class SocketIO { //客戶端編碼,客戶端發送編碼與服務端一致,則服務端無需進行解碼特殊處理 private static final String CLIENTENCODEING = "UTF-8"; private static final int PORT = 7777; private static AtomicInteger count = new AtomicInteger(); public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("端口啓動:"+PORT); while (true) { Socket socket = null; InputStream inputStream = null; OutputStream outputStream = null; try { //無數據時會阻塞在這裏,有數據即accept事件會觸發accept方法的調用 //當一個鏈接進來,第二個鏈接進不來,由於單線程時,還阻塞在下面的read,線程沒法再回到accept上繼續等待 socket = serverSocket.accept(); int ccount = count.incrementAndGet(); System.out.println("新的客戶端已鏈接,當前No."+ccount+" "+System.currentTimeMillis()); inputStream = new BufferedInputStream(socket.getInputStream()); outputStream = new BufferedOutputStream(socket.getOutputStream()); //讀取正文內容 byte[] flush = new byte[1024]; int length = 0; StringBuffer rec = new StringBuffer(); while ((length = inputStream.read(flush)) != -1) { rec.append(new String(flush, 0, length)); } //寫法2 //客戶端不通知關閉socket.shutdownOutput();時,用下面這種方法,不通知關閉read方法會死循環 //available()方法能夠在讀寫操做前先得知數據流裏有多少個字節能夠讀取 //但若是客戶端分批發送可能有問題,可能沒法得到第二批及之後的數據 //因此最好仍是讓客戶端通知一下 // int count = 0; // while(count == 0){ // count = inputStream.available(); // } // byte[] flush = new byte[count]; // inputStream.read(flush); // String rec = new String(flush, 0, count, CLIENTENCODEING); String back = "["+ccount+"]"+UUID.randomUUID() + ""; System.out.println("收到數據:" + rec.toString() + " 即將返回數據:" + back); //返回數據 outputStream.write(back.getBytes(), 0, back.getBytes().length); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } finally { close(socket,inputStream,outputStream); } } } private static void close(Socket socket, InputStream inputStream, OutputStream outputStream) throws IOException { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (socket != null) { socket.close(); } } }
列幾點須要注意的點:性能
一、最好經過read方法來讀取客戶端發來的內容,代碼中available我實測客戶端發送屢次的狀況下會丟消息,不可靠。編碼
二、最好經過緩衝流Buffered*Stream來配合InputStream、OutputStream使用spa
三、write後記得flush線程
四、關閉流code
//服務端編碼 private static final String SERVERENCODEING = "UTF-8"; public static void main(String[] args){ for (int i = 0; i < 1; i++) { new Thread(() -> { try { doo(); } catch (IOException e) { e.printStackTrace(); } }).start(); } } public static void doo() throws IOException { Socket socket = null; InputStream in = null; OutputStream out = null; String msg = "你好你好你好好好!"; try { //發送數據 socket = new Socket("127.0.0.1", 7777); out = new BufferedOutputStream(socket.getOutputStream()); in = new BufferedInputStream(socket.getInputStream()); out.write(msg.getBytes()); out.flush(); out.write("還有點".getBytes()); out.flush(); // //任何的輸入流或輸出流的close()都會形成Socket關閉 可是不關閉又致使服務端沒法接收到-1 // out.close(); socket.shutdownOutput(); //讀取正文內容 byte[] flush = new byte[1024]; int length = 0; StringBuffer rec = new StringBuffer(); while ((length = in.read(flush)) != -1) { rec.append(new String(flush, 0, length, SERVERENCODEING));//以服務端編碼標準發送 } System.out.println("客戶端收到回覆:" + rec.toString()); in.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } finally { close(socket, in, out); } } private static void close(Socket socket, InputStream inputStream, OutputStream outputStream) throws IOException { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (socket != null) { socket.close(); } }
列幾點須要注意的點:server
一、out.close();任何的輸入流或輸出流的close()都會形成Socket關閉 可是不關閉又致使服務端沒法接收到-1blog
二、使用socket.shutdownOutput();來通知服務端發送完成
三、write後記得flush
四、關閉流