2011-03-12 73 views
3

我一直在調查這個問題幾個小時,我不能拿出一個體面的解決方案或解釋爲什麼這個例外(java.net.SocketException:套接字關閉)被拋出。我現在最後的辦法是問你們。Java多線程服務器*有時*拋出SocketException(Socket關閉)在ServerSocket.accept()方法

我爲測試目的創建了一個簡單的服務器 - 客戶端應用程序('真正'應用程序使用相同的邏輯,但請參閱下文)。

如果我重複地調用(通過TestNG的invocationcount註釋參數或通過使用一個簡單的for循環例如)相同的測試情況下的,在某些時候會出現一個java.net.SocketException異常:插座關閉

下面的測試用例基本上只是啓動服務器(打開服務器套接字),等待幾毫秒然後再關閉套接字。關閉服務器套接字涉及打開套接字,以便服務器將從ServerSocket.accept()方法返回(請參閱Server#shutdown())。

雖然它可能是一個多線程問題,緊跟在ServerSocket.accept()行後面的代碼。所以我用一個同步塊臨時包圍它 - 也沒有幫助。

你知道爲什麼拋出這個異常嗎?

最佳, 克里斯

Server.java看起來是這樣的:

package multithreading; 
import java.io.IOException; 
import java.net.ServerSocket; 
import java.net.Socket; 
import org.apache.log4j.Logger; 

public class Server { 

private ServerSocket serverSocket; 
private boolean isShuttingDown; 
private final static Logger logger = Logger.getLogger(Server.class); 

public void start() throws Exception { 
    try { 
     serverSocket = new ServerSocket(5000); 
     isShuttingDown = false; 
    } catch (Exception e) { 
     throw new RuntimeException("Starting up the server failed - aborting", e); 
    } 

    while (true) { 
     try { 
      Socket socket = serverSocket.accept(); 

      if (!isShuttingDown) { 
       new Thread(new EchoRequestHandler(socket)).start(); 
      } else { 
       logger.info("Server is going to shutdown"); 
       break; 
      } 
     } catch (IOException e) { 
      logger.error("Error occured while waiting for new connections, stopping server", e); 
      throw e; 
     } 
    } 
} 

public synchronized boolean isRunning() { 
    if (serverSocket != null && serverSocket.isBound() && !serverSocket.isClosed() && !isShuttingDown) { 
     return true; 
    } 
    return false; 
} 

public synchronized void shutdown() throws IOException { 
    if (isRunning()) { 
     isShuttingDown = true; 
     if (serverSocket != null && !serverSocket.isClosed()) { 
      try { 
       /* 
       * since the server socket is still waiting in it's accept() 
       * method, just closing the server socket would cause an 
       * exception to be thrown. By quickly opening a socket 
       * (connection) to the server socket and immediately closing 
       * it again, the server socket's accept method will return 
       * and since the isShuttingDown flag is then false, the 
       * socket will be closed. 
       */ 
       new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()).close(); 

       serverSocket.close(); 
      } catch (IOException e) { 
       logger.error("Closing the server socket has failed - aborting now.", e); 
       throw e; 
      } 
     } 
    } else { 
     throw new IOException("Server socket is already closed which should not be the case."); 
    } 
} 
} 

測試類執行以下操作:

package multithreading; 

import java.io.IOException; 

import org.testng.annotations.Test; 

public class Testing { 

// @Test(invocationCount=10, skipFailedInvocations=true) 
@Test 
public void loadTest() throws InterruptedException, IOException { 
    for (int i = 0; i < 10; i++) { 
     final Server s = new Server(); 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        s.start(); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 

     }).start(); 
     Thread.sleep(500); 
     gracefullyShutdownServer(s); 
     Thread.sleep(1000); 
    } 
} 

private void gracefullyShutdownServer(final Server server) throws InterruptedException { 
    try { 
     server.shutdown(); 
     while (server.isRunning()) { 
      Thread.sleep(500); 
     } 
    } catch (IOException e) { 
     System.err.println(e); 
    } 
} 

} 

堆棧跟蹤如下所示:

ERROR 2011-03-13 16:14:23,537 [Thread-6] multithreading.Server: Error occured while waiting for new connections, stopping server 
java.net.SocketException: Socket closed 
at java.net.PlainSocketImpl.socketAccept(Native Method) 
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:390) 
at java.net.ServerSocket.implAccept(ServerSocket.java:453) 
at java.net.ServerSocket.accept(ServerSocket.java:421) 
at multithreading.Server.start(Server.java:26) 
at multithreading.Testing$1.run(Testing.java:18) 
at java.lang.Thread.run(Thread.java:680) 
java.net.SocketException: Socket closed 
at java.net.PlainSocketImpl.socketAccept(Native Method) 
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:390) 
at java.net.ServerSocket.implAccept(ServerSocket.java:453) 
at java.net.ServerSocket.accept(ServerSocket.java:421) 
at multithreading.Server.start(Server.java:26) 
at multithreading.Testing$1.run(Testing.java:18) 
at java.lang.Thread.run(Thread.java:680) 
+1

提供令你煩惱的異常的堆棧跟蹤會很好。 – Mat 2011-03-12 11:14:54

+0

@Mat:剛剛添加了堆棧跟蹤(對不起,沒有張貼在第一個地方,愚蠢的我) – Christof 2011-03-13 05:18:41

回答

5

J.N.關於你應該如何處理封閉的方法是正確的。

至於爲什麼這不起作用,我認爲比賽是你的服務器代碼讀取isShuttingDown沒有同步。我不明白爲什麼應該立即向服務器線程顯示值更改。所以它可能很好去另一輪。

所以作爲J.N.說:在接受服務器時處理異常。如果您想知道例外情況是可能是被您的代碼在套接字上執行close引發,請確保您的isShuttingDown安全地訪問它。 (任一個synchronized (this) {}塊,或寫一個非常短的同步訪問。)

在我想使isShuttingDown揮發性是足夠的,因爲這developerWorks文章Java theory and practice: Managing volatility中詳述該特定情況下。但要小心,這不是一個神奇的子彈。

+0

給出的鏈接不再有效,感謝信息雖然:) – AndrewBourgeois 2011-07-21 18:34:52

4

按照此javadoc鏈接中的說明打開套接字以關閉另一個套接字是一個錯誤。調用關閉你的套接字應該通過拋出一個你能夠安全地捕獲和忽略的異常來自己取消「接受」。

相關問題