2011-05-07 65 views
3

我用Java編寫了一個簡單的TCP客戶端類。它用於連接到用Python編寫的TCP服務器,並在新線程中處理傳入的消息。它看起來像這樣:Android上的套接字未關閉

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.InetSocketAddress; 
import java.net.Socket; 

public class TCPTestClient { 
    private String mHost; 
    private int mPort; 

    private Socket mSocket; 
    private PrintWriter mWriter; 
    private BufferedReader mReader; 

    public TCPTestClient(String host, int port) { 
     mHost = host; 
     mPort = port; 
    } 

    public void connect() throws IOException { 
     if (mSocket == null || mSocket.isClosed()) { 
      mSocket = new Socket(); 
      mSocket.connect(new InetSocketAddress(mHost, mPort), 5000); 
      mWriter = new PrintWriter(mSocket.getOutputStream()); 
      mReader = new BufferedReader(new InputStreamReader(mSocket 
        .getInputStream())); 
      new Thread(new InputHandler(mReader, this)).start(); 
     } 
    } 

    public void close() throws IOException { 
     if (mSocket != null && !mSocket.isClosed()) { 
      mSocket.close(); 
     } 
    } 

    class InputHandler implements Runnable { 
     BufferedReader mReader; 
     TCPTestClient mClient; 

     public InputHandler(BufferedReader reader, TCPTestClient client) { 
      mReader = reader; 
      mClient = client; 
     } 

     public void run() { 
      String line = null; 
      try { 
       while ((line = mReader.readLine()) != null) { 
        System.out.println(line); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

的TCP服務器簡單地打印「連接的客戶端」和「客戶端斷開連接」到標準輸出,如果一個新的客戶端已連接或斷開。在正常的java應用程序中運行TCPTestClient時,這很好用:連接在調用connect()時建立,在調用close()時關閉,InputHandler內的等待readLine()將因SocketException聲明套接字爲關閉(java.net.SocketException: Socket closed)。這是我期望的行爲。

但是,當我在Android上運行此代碼時,連接不會被關閉:readLine()仍然會阻塞而不會拋出SocketException,並且服務器不顯示「客戶端斷開連接」消息。

這裏是我的活動:

import java.io.IOException; 

import android.app.Activity; 
import android.util.Log; 

public class Foo extends Activity { 
    private TCPTestClient mClient; 

    private static final String TAG = "Foo"; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.foo); 

     mClient = new TCPTestClient("192.168.1.2", 3456); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     Log.d(TAG, "onPause"); 

     try { 
      mClient.close(); 
     } catch (IOException e) { 
      Log.d(TAG, "Disconnect failed", e); 
     } 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     Log.d(TAG, "onResume"); 

     try { 
      mClient.connect(); 
     } catch (IOException e) { 
      Log.d(TAG, "Connect failed", e); 
     } 
    } 
} 

因此,當活動開始時/恢復時,將建立連接。當用戶點擊後退按鈕時,連接應該關閉。然而,onPause()和close()被調用,但是由於BufferedReader仍然阻塞等待輸入,因此套接字未關閉。在close-method塊內也可以調用mReader.close()

有誰知道如何解決此問題,以便連接將在活動暫停時成功關閉?

+0

OK,我想我找到:-)現在我打電話\t \t \t'mSocket.shutdownInput()在我的特寫解決方案'和'mSocket.shutdownOutput()'方法。這會導致readLine()返回,並且我的InputHandler-Thread將退出,並且服務器將打印「已斷開」的消息。 – Biggie 2011-05-07 11:40:43

+0

你只需要shutdownInput()。 – EJP 2014-04-29 08:43:11

回答

0

您是如何驗證客戶端套接字未關閉的?

檢測服務器端封閉連接的唯一可靠方法是寫入數據,並向協議添加心跳。

+0

正如我寫道:服務器會打印一個「客戶端斷開連接」消息到標準輸出。而且InputHandler-Thread應該終止,但它仍然掛在readLine()上。所以也許只有流不能正確關閉。這就是我想要的:)我認爲當InputStream關閉時,服務器將看到這個並打印預期的消息。但我不知道如何關閉流,因爲如前所述,mReader.close()也會阻塞。編輯:並與「正常的Java」它按預期工作;) – Biggie 2011-05-07 10:42:26

0

是的,在2.3之前,你必須使用mSocket.shutdownInput()和mSocket.shutdownOutput()來拋出異常。但目前在2.2.2中,它不適合我。 :(還是尋找一種方法來拋出異常。

+0

你有沒有解決這個問題?我有類似的問題,不得不將'minSdkVersion'升到'9',因爲我無法弄清楚如何在Froyo和以下版本上使用它::/ – 2012-12-07 19:23:26