socket獲取數據 獲取流超時

 

首先請查看一下JavaAPI,能夠看到InputStream讀取流有三個方法,分別爲read(),read(byte[] b),read(byte[] b, int off, int len)。其中read()方法是一次讀取一個字節,鬼都知道效率是很是低的。因此最好是使用後面兩個方法。java

例如如下代碼:程序員

Java代碼  收藏代碼編程

  1. /** 
  2.  * 讀取流 
  3.  *  
  4.  * @param inStream 
  5.  * @return 字節數組 
  6.  * @throws Exception 
  7.  */  
  8. public static byte[] readStream(InputStream inStream) throws Exception {  
  9.     ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
  10.     byte[] buffer = new byte[1024];  
  11.     int len = -1;  
  12.     while ((len = inStream.read(buffer)) != -1) {  
  13.         outSteam.write(buffer, 0, len);  
  14.     }  
  15.     outSteam.close();  
  16.     inStream.close();  
  17.     return outSteam.toByteArray();  
  18. }  

 

咱們來測試一下:數組

Java代碼  收藏代碼網絡

  1. public static void main(String[] args) {  
  2.     try {  
  3.         File file = new File("C:\\ceshi.txt");  
  4.         FileInputStream fin = new FileInputStream(file);  
  5.         byte[] filebt = readStream(fin);  
  6.         System.out.println(filebt.length);  
  7.     } catch (Exception e) {  
  8.         e.printStackTrace();  
  9.     }     
  10. }  

後臺會打印這個文本的字節大小。看起來,這個是沒有問題的。socket

 

 

關於InputStream類的available()方法
這個方法的意思是返回此輸入流下一個方法調用能夠不受阻塞地今後輸入流讀取(或跳過)的估計字節數。爲何須要這個方法?由於在一些網絡應用中,數據流並非一次性就能傳遞的,若是咱們仍是像上面那樣去將這個流轉換,會出問題的。咱們來作一個例子,這是一個Socket編程的簡單例子,具體Socket內容我會在後面文章中解釋的。工具

首先編寫兩個類,一個用戶初始化Socket服務,而且處理每一個請求都有新的線程去處理,代碼以下:測試

Java代碼  收藏代碼this

  1. package com.service;  
  2. import java.net.*;  
  3. public class DstService {  
  4.     public static void main(String[] args) {  
  5.         try {  
  6.             // 啓動監聽端口 8001  
  7.             ServerSocket ss = new ServerSocket(8001);  
  8.             boolean bRunning = true;  
  9.             while (bRunning) {  
  10.                 // 接收請求  
  11.                 Socket s = ss.accept();  
  12.                 // 將請求指定一個線程去執行  
  13.                 new Thread(new DstServiceImpl(s)).start();  
  14.             }  
  15.         } catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19. }  

那麼處理類咱們也來看一下:.net

Java代碼  收藏代碼

  1. package com.service;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. import com.util.*;  
  5. public class DstServiceImpl implements Runnable {  
  6.     Socket socket = null;  
  7.     public DstServiceImpl(Socket s) {  
  8.         this.socket = s;  
  9.     }  
  10.     public void run() {  
  11.         try {  
  12.             InputStream ips = socket.getInputStream();  
  13.             OutputStream ops = socket.getOutputStream();  
  14.             while (true) {  
  15.                 byte[] bt = StreamTool.readStream(ips);  
  16.                 String str = new String(bt);  
  17.                 System.out.println("主機收到信息:" + str);  
  18.                 String restr = "你好,主機已經收到信息!";  
  19.                 ops.write(restr.getBytes());  
  20.                 ops.flush();  
  21.             }  
  22.         } catch (Exception e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26. }  

 至於工具類,我就直接給代碼了:

Java代碼  收藏代碼

  1. package com.util;  
  2. import java.io.*;  
  3. public class StreamTool {     
  4.     public static void main(String[] args) {  
  5.         try {  
  6.             File file = new File("C:\\ceshi.txt");  
  7.             FileInputStream fin = new FileInputStream(file);  
  8.             byte[] filebt = readStream(fin);  
  9.             System.out.println(filebt.length);  
  10.         } catch (Exception e) {  
  11.             e.printStackTrace();  
  12.         }     
  13.     }     
  14.     /** 
  15.      * @功能 讀取流 
  16.      * @param inStream 
  17.      * @return 字節數組 
  18.      * @throws Exception 
  19.      */  
  20.     public static byte[] readStream(InputStream inStream) throws Exception {  
  21.         ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
  22.         byte[] buffer = new byte[1024];  
  23.         int len = -1;  
  24.         while ((len = inStream.read(buffer)) != -1) {  
  25.             outSteam.write(buffer, 0, len);  
  26.         }  
  27.         outSteam.close();  
  28.         inStream.close();  
  29.         return outSteam.toByteArray();  
  30.     }  
  31. }  

 你能夠直接運行這個類,會看到流被轉換的效果。

咱們來寫一個Socket客戶端測試一下:

Java代碼  收藏代碼

  1. package com.client;  
  2. import java.io.*;  
  3. import java.net.*;  
  4. import com.util.*;  
  5. public class DstClient {  
  6.     public static void main(String[] args) {  
  7.         try {  
  8.             Socket socket = new Socket("127.0.0.1", 8001);  
  9.             // 開啓保持活動狀態的套接字  
  10.             socket.setKeepAlive(true);  
  11.             // 設置讀取超時時間  
  12.             socket.setSoTimeout(30 * 1000);  
  13.             OutputStream ops = socket.getOutputStream();  
  14.             String mess = "你好,我是崔素強!";  
  15.             ops.write(mess.getBytes());  
  16.             InputStream ips = socket.getInputStream();  
  17.             byte[] rebyte = StreamTool.readStream(ips);  
  18.             String remess = new String(rebyte);  
  19.             System.out.println("收到主機消息:" + remess);  
  20.             socket.close();  
  21.         } catch (Exception e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.     }  
  25. }  

 先運行DstService,而後運行客戶端,看效果。會發現,控制檯沒有任何輸出。通過調試發現,由於請求死在了

Java代碼  收藏代碼

  1. while ((len = inStream.read(buffer)) != -1) {  

這行代碼上面。這就是在網絡應用中會形成的後果。那麼如何解決呢?有的人給出了以下代碼:

Java代碼  收藏代碼

  1. int count = in.available();  
  2. byte[] b = new byte[count];  
  3. in.read(b);  

但是在進行網絡操做時每每出錯,由於你調用available()方法時,對發發送的數據可能尚未到達,你獲得的count是0。須要作以下修改,是咱們的讀取流方法改爲以下:

Java代碼  收藏代碼

  1. /** 
  2.  * @功能 讀取流 
  3.  * @param inStream 
  4.  * @return 字節數組 
  5.  * @throws Exception 
  6.  */  
  7. public static byte[] readStream(InputStream inStream) throws Exception {  
  8.     int count = 0;  
  9.     while (count == 0) {  
  10.         count = inStream.available();  
  11.     }  
  12.     byte[] b = new byte[count];  
  13.     inStream.read(b);  
  14.     return b;  
  15. }  

下面你在運行,會看到服務端和客戶端都收到了消息。 

 

 

關於InputStream.read(byte[] b)和InputStream.read(byte[] b,int off,int len)這兩個方法都是用來從流裏讀取多個字節的,有經驗的程序員就會發現,這兩個方法常常 讀取不到本身想要讀取的個數的字節。好比第一個方法,程序員每每但願程序能讀取到b.length個字節,而實際狀況是,系統每每讀取不了這麼多。仔細閱讀Java的API說明就發現了,這個方法 並不保證能讀取這麼多個字節,它只能保證最多讀取這麼多個字節(最少1個)。所以,若是要讓程序讀取count個字節,最好用如下代碼:

Java代碼  收藏代碼

  1. int count = 100;  
  2. byte[] b = new byte[count];  
  3. int readCount = 0; // 已經成功讀取的字節的個數  
  4. while (readCount < count) {  
  5.     readCount += inStream.read(b, readCount, count - readCount);  
  6. }  

 這樣就能保證讀取100個字節,除非中途遇到IO異常或者到了數據流的結尾狀況!

相關文章
相關標籤/搜索