2013-07-17 54 views
0

我正在開發Java中的多線程非阻塞tcp服務器和客戶端。我遇到了服務器問題,意識到客戶端套接字已關閉。對他來說,它總是開放。下面是代碼和我的代碼的輸出:如何在JAVA中正確關閉SocketChannel java?

服務器:

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package androidnonblockingnewesttest; 

import globalvariables.*; 
import interface_package.*; 
import java.io.IOException; 
import java.io.ObjectOutputStream; 
import java.net.InetSocketAddress; 
import java.nio.channels.Channels; 
import java.nio.channels.ServerSocketChannel; 
import java.nio.channels.SocketChannel; 

/** 
* 
* @author wsserver 
*/ 
public class AndroidNonBlockingNewestTest { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     //Prevzemi staticen interface 
     GlobalVariables.sinterface = new ServerInterface(); 
     GlobalVariables.sinterface.show(); 

     //POSTAVI SERVER 
     //INFINITE LOOP 
     //NE MOZAM POSLE OVA DA POVIKUVAM NISTO BIDEJKI NEMA DA STIGNE DO NEGO PORADI while(true) 
     int port = GlobalVariables.portNo; 

     ServerSocketChannel serverSocket = null; 
     try { 
      serverSocket = ServerSocketChannel.open(); 
      serverSocket.socket().bind(new InetSocketAddress(port)); 
      serverSocket.configureBlocking(false); 
      System.out.println("Server has started listening on port " + port); 
      GlobalVariables.sinterface.setServerStatus("Server has started listening on port " + port); 
      GlobalVariables.sinterface.setReceivedText("Server has started listening on port " + port); 
     } catch (IOException e) { 
      System.out.println("Error: Cannot listen on port " + port + " : " + e); 
      GlobalVariables.sinterface.setServerStatus("Error: Cannot listen on port " + port + " : " + e); 
      GlobalVariables.sinterface.setReceivedText("Error: Cannot listen on port " + port + " : " + e); 
      System.exit(1); 
     } 
     while (true) // infinite loop - loops once for each client 
     { 
      SocketChannel clientSocket = null; 
      try { 
       clientSocket = serverSocket.accept(); //waits here (forever) until a client connects 

       if (clientSocket == null) { 
        // No connections came . 
       } else { 
        clientSocket.configureBlocking(false); 
        // You got a connection. Do something 
        System.out.println("Server has just accepted socket connection from a client"); 
        GlobalVariables.sinterface.setServerStatus("Server has just accepted socket connection from a client"); 
        GlobalVariables.sinterface.setReceivedText("Server has just accepted socket connection from a client"); 

        // Create the Handle Connection object - our new thread object - only create it 
        ThreadedHandleConnection con = new ThreadedHandleConnection(clientSocket); 

        if (con == null) //If it failed send and error message 
        { 
         try { 
          ObjectOutputStream os = new ObjectOutputStream(Channels.newOutputStream(clientSocket)); 
          os.writeObject("error: Cannot open socket thread"); 
          GlobalVariables.sinterface.setReceivedText("error: Cannot open socket thread"); 
          os.flush(); 
          os.close(); 
         } catch (Exception ex) //failed to even send an error message 
         { 
          System.out.println("Cannot send error back to client: " + ex); 
          GlobalVariables.sinterface.setServerStatus("Cannot send error back to client: " + ex); 
          GlobalVariables.sinterface.setReceivedText("Cannot send error back to client: " + ex); 
         } 
        } else { 
         con.start(); 
        } // otherwise we have not failed to create the HandleConnection object 
        // start this thread now 
       } 
      } catch (IOException e) { 
       System.out.println("Accept failed: " + e); 
       GlobalVariables.sinterface.setServerStatus("Accept failed: " + e); 
       GlobalVariables.sinterface.setReceivedText("Accept failed: " + e); 
       break; 
      } 
     } 

     try // do not get here at the moment 
     { 
      System.out.println("Closing server socket."); 
      GlobalVariables.sinterface.setServerStatus("Closing server socket."); 
      GlobalVariables.sinterface.setReceivedText("Closing server socket."); 
      serverSocket.close(); 
     } catch (IOException e) { 
      System.err.println("Could not close server socket. " + e.getMessage()); 
      GlobalVariables.sinterface.setServerStatus("Could not close server socket. " + e.getMessage()); 
      GlobalVariables.sinterface.setReceivedText("Could not close server socket. " + e.getMessage()); 
     } 
    } 
} 

連接處理程序:

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package androidnonblockingnewesttest; 

import java.io.*; 
import java.nio.ByteBuffer; 
import java.nio.channels.SocketChannel; 
import java.nio.charset.Charset; 
import java.nio.charset.CharsetDecoder; 
import java.nio.charset.CharsetEncoder; 

/** 
* 
* @author wsserver 
*/ 
public class ThreadedHandleConnection extends Thread { 

    private SocketChannel clientSocket; 
    Charset charset = Charset.forName("ISO-8859-1"); 
    CharsetEncoder encoder = charset.newEncoder(); 
    CharsetDecoder decoder = charset.newDecoder(); 
    ByteBuffer buffer = ByteBuffer.allocate(1024); 

    // The constructor for the connecton handler 
    public ThreadedHandleConnection(SocketChannel clientSocket) { 
     try { 
      this.clientSocket = clientSocket; 
      clientSocket.configureBlocking(false); 
     } catch (IOException ex) { 
      System.out.println("SocketChannel blocking exception: " + ex.getMessage()); 
     } 
    } 

    // The main thread execution method 
    @Override 
    public void run() { 
     while (!clientSocket.socket().isClosed()) { 

      try { 

       System.out.println("Socket status:clientSocket.isConnected():" + clientSocket.isConnected()); 
       System.out.println("Socket status:clientSocket.finishConnect():" + clientSocket.finishConnect()); 
       System.out.println("Socket status:clientSocket.isOpen():" + clientSocket.isOpen()); 
       System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isClosed()); 
       System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isConnected()); 


       int bytesread = clientSocket.read(buffer); 
       if (!(bytesread > 0)) { 
        System.out.println("Nothing to read"); 
       } else { 
        buffer.flip(); 

        String request = decoder.decode(buffer).toString(); 

        System.out.println("Request:" + request); 
        buffer.clear(); 
       } 
       Thread.sleep(3000); 
      } catch (IOException ex) { 
       System.out.println("Socket run exception " + ex.getMessage()); 
      } catch (Exception ex) { 
       System.out.println("Exception " + ex.getMessage()); 
      } 
     } 
    } 
} 

客戶:

package androidnonblockingnewesttest; 

import globalvariables.GlobalVariables; 
import java.io.*; 
import java.net.*; 
import java.nio.ByteBuffer; 
import java.nio.CharBuffer; 
import java.nio.channels.SocketChannel; 
import java.nio.charset.Charset; 
import java.nio.charset.CharsetDecoder; 
import java.nio.charset.CharsetEncoder; 

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
/** 
* 
* @author wsserver 
*/ 
public class ThreadedTCPClient { 

    private SocketChannel socket = null; 
    String serverIP = GlobalVariables.serverIP; 
    int port = GlobalVariables.portNo; 

    Charset charset = Charset.forName("ISO-8859-1"); 
    CharsetEncoder encoder = charset.newEncoder(); 
    CharsetDecoder decoder = charset.newDecoder(); 
    ByteBuffer buffer = ByteBuffer.allocate(1024); 

    // the constructor expects the IP address of the server 
    public ThreadedTCPClient() { 
     if (!connectToServer()) { 
      System.out.println("Cannot open socket connection..."); 
     } 
    } 

    private boolean connectToServer() { 
     try // open a new socket to port: and create streams 
     { 
      this.socket = SocketChannel.open(); 
      this.socket.configureBlocking(false); 
      this.socket.connect(new InetSocketAddress("localhost", port)); 
      while(!this.socket.finishConnect()){ 

      } 
      System.out.print("Connected to Server\n"); 

     } catch (Exception ex) { 
      System.out.print("Failed to Connect to Server\n" + ex.toString()); 
      System.out.println(ex.toString()); 
      return false; 
     } 
     return true; 
    } 

    public void sendTest() { 
     try { 
      buffer = encoder.encode(CharBuffer.wrap("Hellow from client")); 
      socket.write(buffer); 
      buffer.clear(); 
     } catch (IOException ex) { 
      System.out.println("Write exception: "+ex.getMessage()); 
     } 
    } 

    public void closeConnection(){ 
     try{ 
      this.socket.socket().close(); 
      this.socket.close(); 
      System.out.println("Close"); 
     }catch(Exception ex){ 
      System.out.print("Failed to close connection to Server\n" + ex.toString()); 
      System.out.println(ex.toString()); 
     } 
    } 

    public static void main(String args[]) { 

     ThreadedTCPClient theApp = new ThreadedTCPClient(); 
     try { 
      Thread.sleep(10000); 
      theApp.sendTest(); 
      theApp.closeConnection(); 
     } catch (Exception ex) { 
      System.out.println(ex.toString()); 
     } 

    } 
} 

這裏是我的outpusts。從客戶端 輸出:從服務器

Connected to Server 
Close 

輸出:

Server has started listening on port 10001 
Server has just accepted socket connection from a client 
Socket status:clientSocket.isConnected():true 
Socket status:clientSocket.finishConnect():true 
Socket status:clientSocket.isOpen():true 
Socket status:clientSocket.socket().isClosed():false 
Socket status:clientSocket.socket().isClosed():true 
Nothing to read 
Socket status:clientSocket.isConnected():true 
Socket status:clientSocket.finishConnect():true 
Socket status:clientSocket.isOpen():true 
Socket status:clientSocket.socket().isClosed():false 
Socket status:clientSocket.socket().isClosed():true 
Nothing to read 
... 
Socket status:clientSocket.isConnected():true 
Socket status:clientSocket.finishConnect():true 
Socket status:clientSocket.isOpen():true 
Socket status:clientSocket.socket().isClosed():false 
Socket status:clientSocket.socket().isClosed():true 
Request:Hellow from client 
Socket status:clientSocket.isConnected():true 
Socket status:clientSocket.finishConnect():true 
Socket status:clientSocket.isOpen():true 
Socket status:clientSocket.socket().isClosed():false 
Socket status:clientSocket.socket().isClosed():true 
Nothing to read 
... 

閉從所有的SocketChannel和SocketChannel.socket()狀態被不改變客戶端的連接之後。請幫忙。

+1

不確定關於java,但在其他環境中,返回0字節的read讀取是遠程對等關閉連接的信號,因此'Nothing to read'表示客戶端關閉。 –

+1

另外,什麼是所有的睡眠()電話? –

+1

我也提高了EJP,主要是因爲'你爲什麼設置非阻塞?',我沒有注意到(主要是因爲它在每個客戶端的單線程服務器中是如此出乎意料)。 –

回答

1

您的代碼在許多方面都是錯誤的。

  1. 如果您使用非阻塞I/O你當然應該也可以使用Selector而不是accept()瘋狂旋轉,finishConnect()

  2. isClosed()不會告訴你是否同行已關閉。它告訴你已關閉。如果對方已關閉,則read()將返回-1。相似,isConnected()只告訴你是否有曾經連接過的插座。你做到了,所以反覆調用是毫無意義的。

  3. 在非阻塞模式連接正確的方法是:(a)調用connect(),(b)中選擇上OP_CONNECT,(c)中,當它觸發呼叫finishConnect(),和(d)當且僅當,則返回true,取消OP_CONNECT.

  4. 如果您在非阻塞模式下收到任何IOException,則必須關閉該通道。

  5. '多線程​​非阻塞服務器'近乎矛盾。如果你有線程,阻止。如果您有非阻塞模式,請選擇。

+1

+1是的 - 我沒有注意到非阻塞套接字。每個客戶端啓動一個線程,然後使用非阻塞,怪異的:(( –

+0

)感謝您的答覆,我嘗試使用選擇器,但我失敗了,今天晚些時候,我發現一個xSocket庫女巫幫助我很多。使用。 下載:http:// xsocket。org/ 教程:http://xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm – AdrianES

+0

@AdrianES在我看來,帶有線程的'java.net.Socket'就像你可以獲得的一樣簡單。我沒有看到你需要使用任何第三方的東西。我只是覺得你在這裏從錯誤的地方開始。除非你計劃成千上萬的連接,否則你不需要NIO。 – EJP