Socket類的getInputStream方法與getOutputStream方法的使用

anthony_ju發表於2018-08-29

Socket類的getInputStream方法與getOutputStream方法的使用

客戶端上的使用

  1. getInputStream方法可以得到一個輸入流,客戶端的Socket物件上的getInputStream方法得到輸入流其實就是從伺服器端發回的資料。
  2. getOutputStream方法得到的是一個輸出流,客戶端的Socket物件上的getOutputStream方法得到的輸出流其實就是傳送給伺服器端的資料。

伺服器端上的使用

  1. getInputStream方法得到的是一個輸入流,服務端的Socket物件上的getInputStream方法得到的輸入流其實就是從客戶端傳送給伺服器端的資料流。

  2. getOutputStream方法得到的是一個輸出流,服務端的Socket物件上的getOutputStream方法得到的輸出流其實就是傳送給客戶端的資料。


read()阻塞

從Socket上讀取對端發過來的資料一般有兩種方法:
1)按照位元組流讀取

BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
int r = -1;
List<Byte> l = new LinkedList<Byte>();
        while ((r = in.read()) != -1) {
            l.add(Byte.valueOf((byte) r));
        }

2)按照字元流讀取

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
        String s;
        while ((s = in.readLine()) != null) {
            System.out.println("Reveived: " + s);
        }

這兩個方法read()和readLine()都會讀取對端傳送過來的資料,如果無資料可讀,就會阻塞直到有資料可讀。或者到達流的末尾,這個時候分別返回-1和null。
這個特性使得程式設計非常方便也很高效。
但是這樣也有一個問題,就是如何讓程式從這兩個方法的阻塞呼叫中返回。

總結一下,有這麼幾個方法:

  1. 傳送完後呼叫Socket的shutdownOutput()方法關閉輸出流,這樣對端的輸入流上的read操作就會返回-1。
    注意不能呼叫socket.getInputStream().close()。這樣會導致socket被關閉。
    當然如果不需要繼續在socket上進行讀操作,也可以直接關閉socket。
    但是這個方法不能用於通訊雙方需要多次互動的情況。
  2. 傳送資料時,約定資料的首部固定位元組數為資料長度。這樣讀取到這個長度的資料後,就不繼續呼叫read方法。
  3. 為了防止read操作造成程式永久掛起,還可以給socket設定超時。
    如果read()方法在設定時間內沒有讀取到資料,就會丟擲一個java.net.SocketTimeoutException異常。
    例如下面的方法設定超時3秒。
    socket.setSoTimeout(3000);

程式碼例項:

public class TCPClient {
    public static void main(String[] args)throws IOException {
        Socket socket = new Socket("127.0.0.1",5000);
        OutputStream out = socket.getOutputStream();
   //建立檔案物件     如果檔案路徑寫錯   Client報找不到檔案異常,Server報Connection Reset異常
        File file = new File("E:\\Pictures\\010.jpg");  
           //位元組輸入流,讀取本地檔案到java程式中
        FileInputStream fis = new FileInputStream(file);
        byte[] data = new byte[1024];              //陣列,增強傳輸效率
        int len = 0;
        while((len = fis.read(data)) != -1)   //read方法返回陣列的有效字元個數
            out.write(data, 0, len);                              
        socket.shutdownOutput();  //資料傳輸完畢,關閉socket輸出流,避免伺服器端read方法阻塞

        InputStream in = socket.getInputStream();      //位元組輸入流,讀取伺服器返回的資料
        len = in.read(data);
        System.out.println(new String(data,0,len));

        socket.close();
        fis.close();
    }

   }

---------------------------------------------------------------------------------
public class TCPServer {
    public static void main(String[] args)throws IOException {
        ServerSocket server = new ServerSocket(5000);//開啟伺服器制定埠,等待客戶端連線
        //獲得與伺服器相連的套接字物件     套接字:繫結ip地址和埠號的網路物件
        Socket socket = server.accept();
        //檢視該地址資料夾是否存在,如果不存在,建立一個
        File file = new File("E:\\TestFolder\\upload"); 
        if(!file.exists()){
            boolean b = file.mkdirs();
            System.out.println(b);
        }   
        InputStream in = socket.getInputStream();  //套接字的位元組輸入流,讀取客戶端傳過來的資料

        String name = System.currentTimeMillis()+""+new Random().nextInt(9999);        //隨機檔名
        FileOutputStream fos = new FileOutputStream(file+File.separator+name+".jpg");  //File.separator表示分隔符,windows是\,linux是/             
        byte[] data = new byte[1024];
        int len = 0;
//如果客戶端沒有關閉socket輸出流,這裡的read方法會一直讀取,對於socket流沒有流末尾之說,不可能返回-1
        while((len = in.read(data)) != -1)  
            fos.write(data, 0, len);
        data = "上傳成功!".getBytes();      //字串轉化為位元組陣列
        OutputStream out = socket.getOutputStream();     //建立位元組輸出流
        out.write(data);                //寫入上傳成功  ,反饋給客戶端   
        server.close();
        fos.close();
    }

}

相關文章