2011-11-13 78 views
1

我編寫客戶端 - 服務器應用程序。客戶端將在普通的java上運行在android和服務器上。我需要將數據從客戶端交換到服務器,然後從服務器交換到客戶端。我的問題是,當我從客戶端發送文件到服務器時,服務器停留在從套接字接收數據上。這裏是我的代碼:android java和套接字

客戶端發送文件:

Log.i("======", "sendToServer=============="); 
    OutputStream output = sk.getOutputStream();  

    String pathToOurFile = directory + File.separator + file; 

    FileInputStream fileInputStream = new FileInputStream(pathToOurFile); 
    byte[] buffer = new byte[sk.getSendBufferSize()]; 
    int bytesRead = 0; 

    while((bytesRead = fileInputStream.read(buffer))>0) 
    { 
     output.write(buffer,0,bytesRead); 
    } 

    fileInputStream.close(); 

服務器獲取數據:

System.out.println("I get data"); 
    File file=null; 

    InputStream input = sk.getInputStream(); 

    file = new File("C://protocolFIle/" + "temp.xml"); 
    FileOutputStream out = new FileOutputStream(file); 

    byte[] buffer = new byte[sk.getReceiveBufferSize()]; 

    int bytesReceived = 0; 

    while((bytesReceived = input.read(buffer))>0) { 
     out.write(buffer,0,bytesReceived); 
    } 

    return file; 

服務器停留在服務器端線

while((bytesReceived = input.read(buffer))>0) { 
     out.write(buffer,0,bytesReceived); 
    } 

。當我發送數據時,我想我需要在客戶端以某種方式發出信號。我知道我可以在客戶端編寫「output.close()」,然後服務器獲取文件並在while循環中斷開,但我將來需要此輸出。有誰知道我怎麼能發送這個文件,服務器不卡住?也許我需要以另一種方式發送此文件。謝謝你的幫助。

回答

2

在客戶端使用close()實際上不會有太大的幫助,因爲諸如close()之類的TCP/IP API方法幾乎沒有任何意義,因爲它是在應用層發信號結束消息的手段。原因是在客戶端關閉TCP流不會立即關閉整個TCP連接直到對端客戶端。這與TCP的底層體系結構有關。

你需要做的是實現一個應用協議級別的方法來識別消息的長度,或者其他一些其他手段的消息「框架」。要做的最簡單的事情可能是在消息的開始處實現一個基本的頭文件,它可能只包含四個字節,代表U32消息長度(例如)。

我會再次強調TCP的一個重要的事情是,TCP協議本身,特別是像close()這樣的調用,並不提供可靠地指示您通過TCP發送的應用程序消息開始/停止的方式。 TCP必須被認爲是協議,因爲這正是它的原理;因此,您還必須在該流中放入一些帶有長度字段的特殊標題或幀字符,或者其他方式指示接收端在撥打電話時是否已讀取足夠的內容,以使其具有完整的消息。

我最近創建了一個使用TCP套接字與服務器C#應用程序對話的Android應用程序,即使我只是通過流發送簡單且相對較短的消息,我發現實現非常基本的消息成幀和消息頭是絕對必要。

信令消息的長度或結束應該在應用協議級完成。

此外,通知兩端應關閉TCP連接也應在應用程序協議級別完成。當您搜索StackOverflow時,您經常會遇到許多人在檢測TCP連接是否已關閉時遇到問題。問題在於,這是一種固有的錯誤的方式,任何語言的TCP套接字API都不能可靠地告訴你一個流已經關閉。

如果你看一下HTTP,這是一個使用TCP套接字的流行協議的例子,你將會看到這個協議使用了指示長度的標題,指示連接的標誌應該關閉,以及一條消息是碎片化等等。

回到您的特定問題,您應該在Android側的第一個write()中執行什麼操作,然後編寫一個包含長度字段的簡單消息頭。在服務器端,第一個read()應該在文件有效載荷開始之前看到這個標題。然後使用該標題中提供的長度來確定何時有read()所有文件。

您可以稍後擴展您的標題以包含CRC值等內容。