我的ISP强迫我在发送之前缓冲tcp数据

时间:2012-03-09 05:58:04

标签: java networking tcp wireshark

我有一个Java TCP游戏服务器,我使用java.net.ServerSocket并且一切运行得很好,但最近我的ISP做了某种升级,如果你为同一个TCP连接发送两个数据包非常快,他们强行关闭它。

这就是为什么我的很多玩家在游戏中有大量流量时会随机断开连接(当服务器有很多机会同时为同一个人发送2个数据包时)

这是我的意思的一个例子: 如果我做这样的事情,我的ISP将无缘无故地关闭连接到客户端和服务器端:

tcpOut.print("Hello.");
tcpOut.flush();

tcpOut.print("How are you?");
tcpOut.flush();

但如果我做这样的话,它会工作得很好:

tcpOut.print("Hello.");
tcpOut.flush();

Thread.sleep(200);

tcpOut.print("How are you?");
tcpOut.flush();

或者这个:

tcpOut.print("Hello.");
tcpOut.print("How are you?");
tcpOut.flush();

这仅在几周前开始,当时他们(ISP)对服务和网络进行了一些更改。我注意到使用Wireshark你必须在两个数据包之间至少有大约150ms的时间用于相同的TCP连接,否则它将关闭。

1)你们知道这叫什么吗?是甚至有名字?这是合法的吗?

现在我必须重新编写我的游戏服务器,因为我知道我使用的方法是:send(PrintWriter out, String packetData);

2)在将数据发送给客户端之前,是否有任何简单的解决方案要求java缓冲数据?或者在每次发送前等待150ms而不必重写整个事情?我做了一些谷歌搜索,但我找不到任何处理这个问题。任何有关此问题的提示或信息都会非常感激,顺便提一下速度优化非常关键。谢谢。

2 个答案:

答案 0 :(得分:5)

如果您的ISP施加了这样的服务质量策略,并且您无法与之协商,我建议您使用TCP / IP堆栈QoS配置来强制执行该规则。

flush将TCP数据包标记为紧急(URG标志),以便无论缓冲区/ TCP窗口状态如何都会发送。现在您必须告诉您的操作系统或线路上的任何网络设备

  • 在最后150毫秒内发送前一个数据包时忽略(或简单地重置)紧急标志,并在必要时进行一些缓冲
  • 延迟连续紧急数据包的传递以兑现150毫秒的约束。

可能存在一个昂贵的Windows软件。就个人而言,我认为在Windows工作站和调制解调器之间使用Linux框作为路由器与iptables和qdisc中的appropriate QoS settings一起使用。

答案 1 :(得分:3)

您可以创建一个Writer包装器实现来跟踪最后flush个呼叫时间戳。一个快速实现是添加一个等待调用,以纪念两次连续刷新之间的150 ms延迟。

public class ControlledFlushWriter extends Writer {
    private long enforcedDelay = 150;
    private long lastFlush = 0;
    private Writer delegated;

    public ControlledFlushWriter(Writer writer, long flushDelay) {
        this.delegated = writer:
        this.enforcedDelay = flushDelay;
    }

    /* simple delegation for other abstract methods... */

    public void flush() {
        long now = System.currentTimeMillis();
        if (now < lastFlush + enforcedDelay) {
            try {
                Thread.sleep(lastFlush + enforcedDelay - now);
            } catch (InterruptedException e) {
                // probably prefer to give up flushing 
                // instead of risking a connection reset !
                return;
            }
        }
        lastFlush = System.currentTimeMillis();
        this.delegated.flush();
    }

}

现在应该足以将现有的PrintWriter与此ControlledFlushWriter一起包装,以解决您的ISP QoS问题,而无需重新编写所有应用程序。

毕竟,防止连接将其任何数据包标记为紧急是合理的......在这种情况下,很难实现公平的QoS链接共享。