2014-06-04 74 views
1

我在寫一個IRC客戶端。通過服務處理到IRC服務器的套接字連接。我已經設法在定向更改期間穩定所討論的活動的所有UI元素,但不知何故,由該服務維護的套接字在更改過程中正在關閉。背景上的服務套接字斷開 - >前臺開關

這是我相信是相關的代碼。如果您需要了解更多信息,請告訴我們。

//This is the Service in question 
public class ConnectionService extends Service{ 

private BlockingQueue<String> MessageQueue; 

public final IBinder myBind = new ConnectionBinder(); 

public class ConnectionBinder extends Binder { 
    ConnectionService getService() { 
     return ConnectionService.this; 
    } 
} 

private Socket socket; 
private BufferedWriter writer; 
private BufferedReader reader; 

private IRCServer server; 

private WifiManager.WifiLock wLock; 

private Thread readThread = new Thread(new Runnable() { 

    @Override 
    public void run() { 
     try { 
      String line; 
      while ((line = reader.readLine()) != null) { 
       if (line.toUpperCase().startsWith("PING ")) { 
        SendMessage("PONG " + line.substring(5)); 
       } 
       else 
        queueMessage(line); 
      } 
     } 
     catch (Exception e) {} 
    } 

}); 

@Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 

    if(MessageQueue == null) 
     MessageQueue = new LinkedBlockingQueue<String>(); 

    return Service.START_STICKY; 
    } 

@Override 
public IBinder onBind(Intent arg0) { 
    return myBind; 
} 

@Override 
public boolean stopService(Intent name) { 
    try { 
     socket.close(); 
     wLock.release(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    return super.stopService(name); 
} 

    @Override 
public void onDestroy() 
{//I put this here so I had a breakpoint in place to make sure this wasn't firing instead of stopService 
    try { 
     socket.close(); 
     wLock.release(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    super.onDestroy(); 
} 

public void SendMessage(String message) 
{ 
    try { 
     writer.write(message + "\r\n"); 
     writer.flush(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

public String readLine() 
{ 
    try { 
     if(!isConnected()) 
      return null; 
     else 
      return MessageQueue.take(); 
    } catch (InterruptedException e) { 
     return ""; 
    } 
} 

public boolean ConnectToServer(IRCServer newServer) 
{ 
    try { 
     //create a new message queue (connecting to a new server) 
     MessageQueue = new LinkedBlockingQueue<String>(); 

     //lock the wifi 
     WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 
     wLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "LockTag"); 
     wLock.acquire(); 

     server = newServer; 

     //connect to server 
     socket = new Socket(); 
     socket.setKeepAlive(true); 
     socket.setSoTimeout(60000); 

     socket.connect(new InetSocketAddress(server.NAME, Integer.parseInt(server.PORT)), 10000); 

     writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 
     reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     //run basic login scripts. 
     if(server.PASS != "") 
      SendMessage("PASS " + server.PASS); 

     //write nickname 
     SendMessage("NICK " + server.NICK); 

     //write username login 
     SendMessage("USER " + server.NICK + " 0 * :Fluffy IRC"); 

     String line; 
     while ((line = reader.readLine()) != null) { 
      if (line.indexOf("004") >= 0) { 
       // We are now logged in. 
       break; 
      } 
      else if (line.indexOf("433") >= 0) { 
       //change to alt Nick 
       if(!server.NICK.equals(server.ALT_NICK) && !server.ALT_NICK.equals("")) 
       { 
        server.NICK = server.ALT_NICK; 
        SendMessage("NICK " + server.NICK); 
       } 
       else 
       { 
        queueMessage("Nickname already in use"); 
        socket.close(); 
        return false; 
       } 
      } 
      else if (line.toUpperCase().startsWith("PING ")) { 
       SendMessage("PONG " + line.substring(5)); 
      } 
      else 
      { 
       queueMessage(line); 
      } 
     } 

     //start the reader thread AFTER the primary login!!! 
     CheckStartReader(); 

     if(server.START_CHANNEL == null || server.START_CHANNEL == "") 
     { 
      server.WriteCommand("/join " + server.START_CHANNEL); 
     } 
     //we're done here, go home everyone 
    } catch (NumberFormatException e) { 
     return false; 
    } catch (IOException e) { 
     return false; 
    } 

    return true; 

} 

private void queueMessage(String line) { 
    try { 
     MessageQueue.put(line); 
    } catch (InterruptedException e) { 
    } 
} 

public boolean isConnected() 
{ 
    return socket.isConnected(); 
} 

public void CheckStartReader() 
{ 
    if(this.isConnected() && !readThread.isAlive()) 
     readThread.start(); 
} 
} 

//Here are the relevant portions of the hosting Activity that connects to the service 
//NOTE: THE FOLLOWING CODE IS PART OF THE ACTIVITY, NOT THE SERVICE 
private ConnectionService conn; 

private ServiceConnection mConnection = new ServiceConnection() { 

    @Override 
    public void onServiceConnected(ComponentName name, IBinder service) { 
     conn = ((ConnectionService.ConnectionBinder)service).getService(); 
     Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT) 
      .show(); 
     synchronized (_serviceConnWait) { 
      _serviceConnWait.notify(); 
     } 
    } 

    @Override 
    public void onServiceDisconnected(ComponentName name) { 
     conn = null; 
    } 
    }; 

@Override 
protected void onSaveInstanceState(Bundle state){ 
    super.onSaveInstanceState(state); 
    state.putParcelable("Server", server); 
    state.putString("Window", CurrentTabWindow.GetName()); 

    unbindService(mConnection); 
} 

     @Override 
protected void onDestroy() 
{ 
    super.onDestroy(); 
    if(this.isFinishing()) 
     stopService(new Intent(this, ConnectionService.class)); 
} 

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

    localTabHost = (TabHost)findViewById(R.id.tabHostMain); 
    localTabHost.setup(); 
    localTabHost.setOnTabChangedListener(new tabChange()); 

    _serviceConnWait = new Object(); 

    if(savedInstanceState == null) 
    {//initial startup, coming from Intent to start 
     //get server definition 
     server = (IRCServer)this.getIntent().getParcelableExtra(IRC_WINDOW); 
     server.addObserver(this); 
     AddTabView(server); 

      startService(new Intent(this, ConnectionService.class)); 
    } 
    else 
    { 
     server = (IRCServer)savedInstanceState.getParcelable("Server"); 
     String windowName = savedInstanceState.getString("Window"); 

     //Add Needed Tabs 
     //Server 
     if(!(windowName.equals(server.GetName()))) 
      AddTabView(server); 
     //channels 
     for(IRCChannel c : server.GetAllChannels()) 
      if(!(windowName.equals(c.GetName()))) 
       AddTabView(c); 
     //reset each view's text (handled by tabChange) 

     if(windowName.equals(server.GetName())) 
      SetCurrentTab(server.NAME); 
     else 
      SetCurrentTab(windowName); 

     ResetMainView(CurrentTabWindow.GetWindowTextSpan()); 

     //Rebind to service 
     BindToService(new Intent(this, ConnectionService.class)); 
    } 
} 

@Override 
protected void onStart() 
{ 
    super.onStart(); 

    final Intent ServiceIntent = new Intent(this, ConnectionService.class); 

    //check start connection service 
    final Thread serverConnect = new Thread(new Runnable() { 

     @Override 
     public void run() { 

      if(!BindToService(ServiceIntent)) 
       return; 

      server.conn = conn; 
      conn.ConnectToServer(server); 
      server.StartReader(); 

      if(server.START_CHANNEL != null && !server.START_CHANNEL.equals("")) 
      { 
       IRCChannel chan = server.FindChannel(server.START_CHANNEL); 

       if(chan != null) 
       { 
        AddTabView(chan); 
       } 
       else 
       { 
        server.JoinChannel(server.START_CHANNEL); 
        chan = server.FindChannel(server.START_CHANNEL); 
        AddTabView(chan); 
       } 

      } 
     } 

    }); 

    serverConnect.start(); 
} 

private boolean BindToService(Intent ServiceIntent) 
{ 
    int tryCount = 0; 
    bindService(ServiceIntent, mConnection, Context.BIND_AUTO_CREATE); 
    while(conn == null && tryCount < 10) 
    { 
     tryCount++; 
     try { 
      synchronized (_serviceConnWait) { 
       _serviceConnWait.wait(1500); 
      } 
     } 
     catch (InterruptedException e) { 
      //do nothing 
     } 
    } 

    return conn != null; 
} 

我不完全確定我在做什麼錯。很顯然,我錯過了一些東西,還沒有找到,或者甚至沒有想過要檢查。會發生什麼事雖然是方向改變後,我的發送命令給了我這個消息,並沒有任何反應:

06-04 22:02:27.637: W/System.err(1024): java.net.SocketException: Socket closed 
06-04 22:02:27.982: W/System.err(1024): at com.fluffyirc.ConnectionService.SendMessage(ConnectionService.java:90) 

我當插座是越來越封閉,或者爲什麼不知道。

更新

我已經改變了代碼,這樣,而不是綁定到服務並用它來啓動它,而不是我叫startServicestopService在適當的點以及結合到它,在思想該服務在綁定丟失時被銷燬。這和我在改變它之前完全一樣。套接字仍然關閉方向的變化,我不知道爲什麼。

更新: - 代碼和描述

我加入了代碼更改爲最近啓動/停止服務和START_STICKY製造。我最近還閱讀了一篇很好的文章,解釋瞭如何改變流程的流程,以及爲什麼將android:configChanges="orientation|screenSize"行添加到清單不是一個好主意。因此,這固定了定位問題,但是如果我將活動置於後臺模式,然後將其恢復到前臺,它仍然會執行相同的操作。這仍然遵循相同的保存/銷燬/創建過程,方向沒有明顯的線......並且它仍然關閉我的套接字,我仍然不知道爲什麼。

我知道,它不會關閉套接字,直到重新創建過程...我知道這是因爲消息隊列將顯示應用程序在後臺時收到的消息,但一旦我帶上它向前退出它關閉套接字並且沒有其他東西可以被髮送或接收。

+0

你是如何開始你的服務?您需要通過意向啓動它並返回START_STICKY。 – dcow

+0

從OnStartCommand覆蓋中返回START_NOT_STICKY和START_STICKY有什麼區別?我是通過意向啓動它......我是否應該保存該特定意圖並在方向更改後重新使用它以重新綁定該服務? – Nevyn

+0

您的服務將通過解除綁定/綁定循環被銷燬並重新創建,除非您還使用Intent啓動它並返回START_STICKY' – dcow

回答

1

'插座關閉'表示關閉了插座,然後繼續使用它。這不是'斷開'。

你需要把東西放入該catch塊。永遠不要忽略一個例外。當你看到什麼是異常時,你可能會感到驚訝。

NB Socket.isConnected()不會告訴你任何關於連接狀態的信息:只有你是否曾經連接Socket.你有,所以它返回true。

+0

套接字關閉是一個例外,這就是堆棧跟蹤是什麼,爲什麼我知道它在做什麼......或者說,沒有做。我不知道什麼是關閉插座 – Nevyn

+0

您正在關閉插座。除了連接失敗之外,JDK中沒有任何內容會自動關閉套接字。可能您不知道關閉套接字的輸入或輸出流會關閉另一個流和套接字。 – EJP

+0

我正在做的是改變方向。關於這一點,會導致綁定到活動的服務中的套接字被關閉。我從來沒有,甚至沒有,調用Socket.Close()。這意味着別的什麼東西在該過程中的某個時刻關閉它。我正在尋找一些可能的援助......如果有人有。 – Nevyn