多线程服务器随机抛出java.net.SocketTimeoutException:读取超时

时间:2017-08-24 11:26:17

标签: java multithreading sockets tcp

我有一个多线程tcp服务器,可以处理多个客户端。 每个客户端都在服务器端具有保持套接字连接的线程。 理论上一切都可以正常工作很多分钟,但在极少数情况下,当连接多个客户端时,会发生以下情况: 其中一个客户端向服务器发送tcp数据包,服务器端读取超时。我发现了许多问题,解决了客户端的读取超时问题,但就我而言,这种情况从未发生过。就我而言,当从客户端接收数据包时,服务器会在读取时超时。 我的问题是,为什么以及如何发生这种情况,我该怎么做才能解决这个问题呢?

这是我的服务器监听器:

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分钟,然后当我再次发送一个,另一个客户端超时。

1 个答案:

答案 0 :(得分:1)

事实证明,使用时间超过一定时间的tcp套接字会被同行破坏,从而失去连接。 我解决了我的问题,通过每分钟发送一个几乎空的tcp数据包,使所有程序和服务清楚,这些套接字是活着的!