什么是Java中time_wait连接增加的原因?

3
 class LogWriter implements Runnable {

Socket client;

private static ThreadLocal<Date> date = new ThreadLocal<Date>() {
@Override
protected Date initialValue() {
    return new Date();
};
};

private static ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
    return new SimpleDateFormat("yyyy_MM_dd");
};
};

public LogWriter(Socket client) {
this.client = client;
}

public void run() {
try {
    write(this.client.getInputStream(), new File(
        CentralizedLogging.logsDir + File.separator
            + client.getInetAddress().getHostName() + "_"
            + getFormattedDate() + ".log"));
    this.client.close();
} catch (Exception e) {
    try {
    e.printStackTrace();
    write(new ByteArrayInputStream(e.getMessage().getBytes()),
        new File(CentralizedLogging.logsDir + File.separator
            + "centralLoggingError.log"));
    } catch (IOException io) {

    }
}
}

  public synchronized void write(InputStream in, File file)
    throws IOException {
RandomAccessFile writer = new RandomAccessFile(file, "rw");
writer.seek(file.length()); // append the file content into existing if it not exist creating a new one.
writer.write(read(in));
writer.close();
}

public static byte[] read(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int read = -1;
byte[] buffer = new byte[1024];
read = in.read(buffer);
do {
    out.write(buffer, 0, read);
} while((read = in.read(buffer)) > -1);
return out.toByteArray();
}

主方法

   public static void main(String[] args) throws InterruptedException,
    IOException {
// CentralizedLogging centralLogServer = new CentralizedLogging(args[0],Integer.parseInt(args[1]));
// new Thread(centralLogServer).start();
long start = System.currentTimeMillis() + 1000 * 20 * 60;
while(start >= System.currentTimeMillis()) {
    Socket client = new Socket("bharathi-1397", 10000);
    OutputStream os = client.getOutputStream();
    DataOutputStream outToServer = new DataOutputStream(os);
    outToServer
        .write("Centralized Logging is working......".getBytes());
    client.close();
}
}

我已经正确关闭了连接,但是time_wait连接数量正在增加。我从我的终端中拍摄了一个快照,我贴出了其中的一部分。我是否遗漏了什么?

 tcp6       0      0 127.0.0.1:46146         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:57645         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:47961         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:56716         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:49469         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:54078         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:51626         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:50143         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:59214         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:54192         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:53436         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:54547         127.0.0.1:10000         TIME_WAIT   -               
 tcp6       0      0 127.0.0.1:55032         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:51273         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:48381         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:47532         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:56811         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:55293         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:56664         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:49242         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:51225         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59265         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59378         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:47997         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:47955         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59453         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:48092         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:52462         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59579         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:54921         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:55675         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:51140         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:57321         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:51656         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:54740         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:53600         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59862         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:54766         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:59062         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:55702         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:50942         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:53732         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:52757         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:56430         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:49179         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:48689         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:53313         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:51161         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:57033         127.0.0.1:10000         TIME_WAIT   -               
tcp6       0      0 127.0.0.1:58607         127.0.0.1:10000         TIME_WAIT   - 

这会对我的服务器造成任何问题吗?这是什么原因?

与https://dev59.com/jnRC5IYBdhLWcg3wUvQS相关的内容。 - Gray
1个回答

3
我正确关闭了连接,但是 time_wait 连接数量正在增加。我是否遗漏了什么?
内核使用 TIME_WAIT 确保您的 TCP 流不会重新使用可能仍然有数据包从远程侧发送到其的端口。TIME_WAIT 时间通常设置为基于网络设置的数据包最大年龄的两倍。
如果您正在获得许多新连接,则会看到 TIME_WAIT 端口号的数量增加,但在某些时候它应该稳定下来,除非连接速率也在增加。要做的一件事是搜索所有 TIME_WAIT 行,将它们排序并查看端口号是否在更改。如果正在添加新端口并且旧端口正在消失,则一切正常。
尝试的一件事是确保您的客户端发起close()调用。这将使TIME_WAIT留在客户端而不是服务器上。请参见此关于TCPTIME_WAIT对服务器的影响的优秀页面。
由于您的服务器似乎正在向客户端发送文件(如果我正确地阅读了您的代码),因此您应该考虑更改协议,如以下内容。这将导致客户端首先关闭,因此大部分TIME_WAIT将在每个客户端上。
  1. 服务器首先发送文件长度
  2. 服务器发送文件
  3. 客户端读取长度
  4. 客户端读取文件字节
  5. 客户端发送“收到”
  6. 客户端关闭
  7. 服务器读取客户端的“收到”消息
  8. 服务器关闭

你看到了关于让客户端首先执行close()的部分吗@kannan?如果服务器正在发送文件,则我会让它发送文件,然后等待客户端发送“收到了”消息。然后客户端可以读取文件,读取EOF标记,发送“已收到”,然后立即关闭。

此外,顺便提一下,您应该在代码中使用try ... finally模式。类似这样:

public void run() {
    try {
        write(this.client.getInputStream(), new File(
            CentralizedLogging.logsDir + File.separator
            + client.getInetAddress().getHostName() + "_"
            + getFormattedDate() + ".log"));
    } catch (Exception e) {
        try {
            e.printStackTrace();
            write(new ByteArrayInputStream(e.getMessage().getBytes()),
                new File(CentralizedLogging.logsDir + File.separator
                + "centralLoggingError.log"));
        } catch (IOException io) {
        }
    } finally {
        // always close in the finally
        this.client.close();
    }
}

独特的TimeWait连接 netstat -anp | grep 10000 | awk '{print }' | cut -d ':' -f2 | sort -u | wc -l (无法识别所有进程,未拥有的进程信息不会显示,您必须是root用户才能查看所有内容。) 20730 总TimeWait连接 netstat -anp | grep 10000 | awk '{print }' | cut -d ':' -f2 | wc -l (无法识别所有进程,未拥有的进程信息不会显示,您必须是root用户才能查看所有内容。) 20730如果我的客户增加了,这会有什么问题吗?我还遗漏了什么吗?这个结果是否正确? - kannanrbk
将其保存到文件中,然后在30秒内再次执行并将其保存到文件中。然后对比这两个文件。确保TIME_WAIT行正在消失并被替换。如果它们从未消失,则可能需要调整内核中的网络设置。 - Gray
我在我的答案 @kannan 中添加了一个部分,展示你如何更改客户端/服务器,使得 TIME_WAIT 由客户端而不是服务器承担。 - Gray
谢谢。我只发送20或30字节的内容到服务器,不会发送任何文件。 Socket client = new Socket("bharathi-1397", 10000); OutputStream os = client.getOutputStream(); DataOutputStream outToServer = new DataOutputStream(os); outToServer.write("中央日志记录正在工作……".getBytes()); client.close();我也在我的代码中遵循了您的try catch finally块。 - kannanrbk
这是为了确保您不重复使用可能仍有来自另一端发送的数据包的端口端口 处于 TIME_WAIT 状态,而不是套接字,netstat 显示端口号,而不是套接字。目前的陈述是无意义的。确保客户端关闭的基本技术不需要来自客户端的最终 'ACK':它与连接池完全相同:编写服务器以处理每个连接的多个请求,并在客户端使用连接池关闭空闲连接。这样,服务器将成为接收第一个 FIN 的终点。 - user207421
套接字和端口之间的关系。哦,懂了。修复了。谢谢 @EJP. 不明白所谓的“当前语句”。 - Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接