2017-08-24 144 views
0

我有一個多線程的tcp服務器,處理多個客戶端。 每個客戶端在服務器端都有它的線程來保持套接字連接。 從理論上講,所有東西都可以正常工作很長時間,但在極少數情況下,當連接多個客戶端時,會發生以下情況: 其中一個客戶端向服務器發送tcp數據包,服務器端讀取超時。我發現了很多問題,在客戶端解決讀取超時問題,但在我的情況下,這從來沒有發生過。在我的情況下,服務器從客戶端收到數據包時超時。 我的問題是,爲什麼以及如何發生這種情況,我能做些什麼來解決這個問題?多線程服務器隨機拋出java.net.SocketTimeoutException:讀取超時

這裏是我的服務器監聽器:

public class GameServerTCP extends Thread { 

//TCP 
private ServerSocket serverSocket; 
public Server server; 
public int amountOfTCPConnections = 0; 

ClassLoader classLoader = getClass().getClassLoader(); 
File myFile = new File(classLoader.getResource("Sprites/sprite_sheet.png").getFile()); 

public GameServerTCP(Server game) { 
    this.server = game; 

    //TCP 
    try { 
     serverSocket = new ServerSocket(6336); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

public void run() { 
    while(true) { 
     //TCP 
     Socket socket = null; 

     try { 
      socket = serverSocket.accept(); 
      Toolkit.getDefaultToolkit().beep(); 
      System.out.println(socket.getRemoteSocketAddress() + " has connected to server."); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 

     new TCPConnection(socket, this); 
     amountOfTCPConnections++; 

     if (amountOfTCPConnections > 500) { 
      System.err.println("Too many clients error! (unsolved)"); 
      server.frame.dispatchEvent(new WindowEvent(server.frame, WindowEvent.WINDOW_CLOSING)); 
     } 
    } 
} 

}

這裏是保存每個單獨的連接我的服務器線程:

public class TCPConnection implements Runnable { 

Socket socket; 
private Thread thread; 
private boolean isRunning = false; 
public GameServerTCP serverTCP; 
private String gamename = "-1"; 
public String username; 

/** 
* This is the future! 
* Contains an exact imprint of the player of client side. 
* Cheats can be detected here. 
*/ 
private PlayerMP playerMP; 

String clientSentence; 

TCPConnection(Socket socket, GameServerTCP serverTCP) { 
    this.socket = socket; 
    this.serverTCP = serverTCP; 
    isRunning = true; 
    thread = new Thread(this); 
    thread.start(); 
} 

public synchronized void closeConnection() { 
    if (MasterConnections.connectionsTCP.containsKey(getUniqueConnectionIdentifier())) MasterConnections.connectionsTCP.remove(getUniqueConnectionIdentifier()); 
    if (this.username != null && MasterConnections.currentlyLoggedOnAccounts.contains(this.username)) MasterConnections.currentlyLoggedOnAccounts.remove(this.username); 

    if (this.gamename != null && serverTCP.server.games.containsKey(this.gamename)) { 
     Level game = serverTCP.server.games.get(this.gamename); 
     for (String p : game.playersInLevel) { 
      if (p.equals(getUniqueConnectionIdentifier())) { 
       game.playersInLevel.remove(p); 
       System.out.println(this.username + " has been been removed from game " + this.gamename + "."); 
      } 
     } 

     PacketTCP02LeaveGame tellOthersPacket = new PacketTCP02LeaveGame(this.gamename, this.username); 
     game.writeDataTCPToAllPlayersInThisLevel(tellOthersPacket); 
    } 

    try { 
     this.socket.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

    System.out.println(socket.getRemoteSocketAddress() + " has been disconnected from server."); 
    this.serverTCP.amountOfTCPConnections--; 
    this.stop(); 
} 

public String getUniqueConnectionIdentifier() { 
    return socket.getInetAddress() + ":" + socket.getPort(); 
} 

public String generateUniqueUDPConnectionIdentifier(InetAddress inetAddess, int udpPort) { 
    System.out.println("uuc created: "); 
    System.out.println(inetAddess + "/" + udpPort); 
    return inetAddess + ":" + udpPort; 
} 

public void run() { 
    //version check first 
    PacketTCP00VersionCheck packetVersionCheck = new PacketTCP00VersionCheck(serverTCP.server.getVersion()); 

    if (MasterConnections.connectionsTCP.containsKey(getUniqueConnectionIdentifier())) { 
     this.closeConnection(); 
    } 
    else { 
     MasterConnections.connectionsTCP.put(getUniqueConnectionIdentifier(), this); 
     packetVersionCheck.writeData(this); 
    } 

    BufferedReader inFromClient; 
    try { 
     inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
    } catch (IOException e1) { 
     e1.printStackTrace(); 
     closeConnection(); 
     return; 
    } 

    while(isRunning) { 
     try { 
      clientSentence = inFromClient.readLine(); 
      if (clientSentence == null) { 
       inFromClient.close(); 
       closeConnection(); 
      } 
      else { 
       System.out.println("tcprec -> " + (new Date(System.currentTimeMillis())) + " -> " + this.username + " -> " + clientSentence); 
       this.parsePacket(clientSentence.getBytes()); 
      } 
     } 
     catch (SocketTimeoutException ste) { 
      /** 
      * TODO: 
      */ 
      ste.printStackTrace(); 
      System.err.println("YOU CAN DO SOMETHING HERE!!!!!!!"); 
      closeConnection(); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
      closeConnection(); 
     } 
    } 
} 

public void stop() { 
    isRunning = false; 
    try { 
     thread.join(); 
    } 
    catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

}

這裏是我的客戶:

public class GameClientTCP extends Thread { 

public String gamestate = "logged out"; 

private Game game; 
public Socket tcpSocket; 
public boolean isRunning = false; 
private String serverSentence; 
public boolean hasBeenStarted = false; 

public int boundUDPPort = -1; 

public static String[] characters = new String[5]; 
public static boolean charactersAreLoaded = false; 

private PrintWriter toServer; 

public GameClientTCP(Game game, String ipAddress) { 
    this.game = game; 
} 

public boolean tryConnect() { 
    try { 
     tcpSocket = new Socket(); 
     tcpSocket.connect(new InetSocketAddress(Settings.SERVER_ADDRESS, 6336), 1000); 
     System.out.println("Just connected to " + tcpSocket.getRemoteSocketAddress()); 

     game.getSocketClientUDP().prepareBeforeStart(); 
     game.getSocketClientUDP().start(); 

     return true; 
    } catch (UnknownHostException e1) { 
     try { 
      tcpSocket.close(); 
     } catch (IOException e) { 
      GameError.appendToErrorLog(e); 
      return false; 
     } 
     return false; 
    } catch (IOException e1) { 
     try { 
      tcpSocket.close(); 
     } catch (IOException e) { 
      GameError.appendToErrorLog(e); 
      return false; 
     } 
     GameError.appendToErrorLog(e1); 
     return false; 
    } 
} 

public void run() { 
    BufferedReader fromServer; 
    try { 
     fromServer = new BufferedReader(new InputStreamReader(tcpSocket.getInputStream())); 
     toServer = new PrintWriter(tcpSocket.getOutputStream(),true); 
    } catch (IOException e1) { 
     GameError.appendToErrorLog(e1); 
     return; 
    } 

    while(isRunning) { 
     try { 
      serverSentence = fromServer.readLine(); 
      //System.out.println("Received: " + serverSentence); 
      if (serverSentence != null) this.parsePacket(serverSentence.getBytes()); 
     } 
     catch(UnknownHostException ex) { 
      GameError.appendToErrorLog(ex); 
     } 
     catch(IOException e){ 
      GameError.appendToErrorLog(e); 
     } 
     catch(Exception e) { 
      GameError.appendToErrorLog(e); 
     } 
    } 
} 

public void sendMessageToServer(String message) { 
    try { 
     toServer.println(message); 
     toServer.flush(); 
    } 
    catch (Exception e) { 
     GameError.appendToErrorLog(e); 
     System.exit(-1); 
    } 
} 

}

我希望能更多地瞭解這個問題,請大家幫忙! :)

編輯:這可能很重要,說我的程序運行時,它可能會發生,沒有tcp數據包發送過一段較長的時間。超時總是發生,當我不發送任何數據包至少20或30分鐘,然後當我再次發送一個,另一個客戶端超時。

+0

閱讀超時沒有任何隨機。對方在超時期限內沒有發送任何內容。要麼它太短,要麼對等體或網絡出現問題。 – EJP

+0

但不是tcp連接的默認超時設置爲無限? 對等和網絡都很好。 – Madness

回答

0

事實證明,未使用超過一定時間的tcp套接字會被同級銷燬,從而失去連接。 我解決了我的問題,通過每分鐘發送一個幾乎空的tcp數據包,向所有程序和服務清楚說明這些套接字是活的!

相關問題