java - 多线程服务器随机抛出java.net.SocketTimeoutException : Read timed out

标签 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 分钟没有发送任何数据包,然后当我再次发送数据包时,另一个客户端超时。

最佳答案

事实证明,未使用超过一定时间的 tcp 套接字将被对等方破坏,从而失去连接。 我解决了我的问题,每分钟发送一个几乎空的 TCP 数据包,让所有程序和服务清楚地知道这些套接字是 Activity 的!

关于java - 多线程服务器随机抛出java.net.SocketTimeoutException : Read timed out,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45860400/

相关文章:

java - 如何在Java中获取类型参数

Java : ReentrantReadWriteLock with priority

java - 如何处理Android应用程序的更多用户(拥有自己的帐户)?多线程?

php - 使用 php 和 delphi 的简单套接字?

java - 如何在 Debug模式下运行我的世界

c - 线程执行时陷入无限循环

java - 什么时候需要锁定

c - c中的简单套接字编程

linux - 如何将一个服务器套接字的 'pipe' 或 'chain' 输出发送到另一个服务器套接字的输入(类似于sendfile)?

java - 在 Android 中发送短信