2014-06-04 35 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