【问题标题】:Why is my in.ready() taking so long?为什么我的 in.ready() 需要这么长时间?
【发布时间】:2016-04-29 18:45:19
【问题描述】:

我正在创建一个多人游戏,它为每个客户端创建一个新线程,然后通过输入和输出流在套接字上侦听字符串。在客户端,我还有一个与主游戏线程分开的线程,但是这个线程每秒将更新发送到服务器 60 次,其中两个字母的字符串作为我想要发生的事情的关键。当我在客户端请求我的“一般更新”(代码 RP)时,服务器发送实体数组的大小 OK,然后流似乎有很长的延迟才能再次准备好。

经过大量测试调试,我比较确定这是问题区域的代码。

在服务器端:

if(inputLine.equals("RP")){ //this is the main update
            timetoupdate = System.currentTimeMillis(); //start timer
               ArrayList<Player> currentplayers =world.getPlayers(); //get the list of players
                out.println(currentplayers.size()); //send the size
                for(int pl = 0; pl<currentplayers.size();pl++){ //loop through
                    Player player = currentplayers.get(pl); //get the player object
                    out.println(""+pl);
                    out.println(player.getUsername());
                    out.println(player.getType());
                    out.println(player.getXloc());
                    out.println(player.getYloc());
                    out.println(player.getXvel());
                    out.println(player.getYvel());
                    out.println(player.getPressing());
                    out.println(player.getLookingxloc());
                    out.println(player.getLookingyloc());
                    out.println(player.getTeam());
                    out.flush();
                    /*
                     id
                     username
                     class(type)
                     xloc
                     yloc
                     lxloc
                     lyloc
                     */ 
                    // Just gaveout all info on player.getUsername()
                }

                System.out.println("Time this ("+inputLine+") part takes:"+(System.currentTimeMillis()-timetoupdate)); //stop timer

在客户端

out.println("RP"); //tell the server we are generally updating

    String playercountstring = in.readLine(); //get the array size
    int playercount = Integer.valueOf(playercountstring); //convert to int

    for (int i = 0; i < playercount; i++) {//loop through all players

        long timetoupdate = System.currentTimeMillis(); //start timer
        while(!in.ready()){}//wait for input to become ready
        System.out.println("time this (to ready) part takes:"+(System.currentTimeMillis()-timetoupdate));//stop timer


        String toprocess = in.readLine(); //get id of player
... and continues to get all info on this player

您会注意到,我正在计算违规区域完成所需的时间: 服务器用时不到 1 毫秒,而客户端的一个区域只用了 200 毫秒,特别奇怪,因为收集数组大小的前一行也用了不到 1 毫秒。对于问题所在,我真的很茫然,我什至不需要解决方案,只要解释一下就可以了。谢谢!

【问题讨论】:

  • 您是否正在刷新输出并且您是否在套接字上设置了 TCP_NODELAY?
  • 我正在刷新输出,就像在服务器代码中一样。套接字确实设置为 TCP_NODELAY
  • 双方都有一个出站 tcp 缓冲区,所以你需要在任何地方设置它。您看到的也可能是客户端将“RP”缓冲 200 毫秒
  • 当然-双方都是连接的,我把no delay放在serverside,然后reflush了RP,延迟好像没了! (有没有办法将此问题标记为已解决?)
  • 摆脱它。 readLine() 将阻塞直到数据到达。你对ready() 循环所做的所有事情都是在消耗CPU。没有任何好处。

标签: java multithreading network-programming


【解决方案1】:

TCP连接有2点经常出问题:

  • Java 中的缓冲区,用于在将数据移交给系统之前收集数据,例如在BufferedOutputStream 内
    • 一旦需要发送数据,需要拨打flush()
  • 系统的出站 TCP 缓冲区

那些地方在连接的两边和两边都存在。

一个小例子:

class Server {
    public void serve(int port) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            for(;;) {
                Socket clientSocket = serverSocket.accept();
                clientSocket.setTcpNoDelay(true);
                Thread clientThread = new Thread(() -> handleClient(clientSocket));
                clientThread.start();
            }
        }
    }

    private void handleClient(Socket clientSocket) {
        try (
            Socket socket = clientSocket; // tricks try-with-resource into handling the socket
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
             ) {

            while (true) {
                String command = reader.readLine();
                switch (command) {
                    case "quit":
                        Util.send(writer, "ok");
                        return; // ends this thread.
                    case "time":
                        Util.send(writer, String.valueOf(System.currentTimeMillis()));
                        break;
                }
            }

        } catch (IOException e) {
            System.out.println("Client connection closed due to " + e);
            // maybe do something
        }
    }
}

还有客户

class Client {
    public void connect(String host, int port) throws UnknownHostException, IOException {
        try (
            Socket socket = new Socket(host, port);
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
            ) {

            socket.setTcpNoDelay(true);

            Util.send(writer, "time");
            String time = reader.readLine();
            System.out.println("Server time: " + time);
            Util.send(writer, "quit");
            System.out.println(reader.readLine());
            return;
        }
    }
}

注意双方如何调用socket.setTcpNoDelay(true)。 最后是编写行和调用flush() 的代码,以确保数据不会卡在应用程序级缓冲区中。

class Util {
    public static void send(BufferedWriter writer, String command) throws IOException {
        writer.write(command);
        writer.newLine();
        writer.flush();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    相关资源
    最近更新 更多