如何找到哪个进程绑定了一个套接字但不监听?

Posted

技术标签:

【中文标题】如何找到哪个进程绑定了一个套接字但不监听?【英文标题】:how to find which process bind a socket but not listen? 【发布时间】:2018-11-23 17:42:10 【问题描述】:

当我使用 nc 监听一个端口时,它会显示

nc -l -vv -p 21000

retrying local 0.0.0.0:21000 : Address already in use Can't grab 0.0.0.0:21000 with bind

但是我用工具 netstat / ss 找不到哪个任务占用了这个端口

netstat -an|grep 21000 

;没有找到

ss -a|grep 21000 

;没有找到

这个端口被我的java程序占用了,代码是:

public class Test1 

        public static void main(String[] args) throws InterruptedException 
        Socket s = new Socket();
        try 
            s.bind(new InetSocketAddress("127.0.0.1",21000));
         catch (IOException e) 
            e.printStackTrace();

        
        Thread.sleep(500000000000L);
    

当我绑定一个套接字时,不要将它与连接或监听一起使用。 我进入 /proc/[java task id]/fd ,发现这个socket的inode是“socket:[3073501]” 但即使在 /proc/net/tcp 或 /proc/net/tcp6 中我也找不到 inode 或端口

有什么方法可以找到绑定socket但不监听或连接的进程。

谢谢。

我看到了 linux 3.10.0-327 源代码。我认为文件/proc/net/tcp 的内容来自net/ipv4/tcp_ipv4.c。

在 tcp_proc_register 方法中,

static void *tcp_get_idx(struct seq_file *seq, loff_t pos)      

        void *rc;
        struct tcp_iter_state *st = seq->private;

        st->state = TCP_SEQ_STATE_LISTENING;
        rc        = listening_get_idx(seq, &pos);

        if (!rc) 
                st->state = TCP_SEQ_STATE_ESTABLISHED;
                rc        = established_get_idx(seq, pos);
        

        return rc;

它只显示正在侦听或从 tcp_hashinfo 建立的袜子。但是 tcp_hashinfo 有三个结构

struct inet_bind_hashbucket     *bhash; 
struct inet_listen_hashbucket   listening_hash[INET_LHTABLE_SIZE];
struct inet_ehash_bucket        *ehash;

bhash 可用于绑定。 但是不会在 /proc/net/tcp 中导出。

【问题讨论】:

哇。我可以完全按照描述重现这个问题,这不是与/proc/net/tcp 样式十六进制表示法或基于/etc/services 的名称的简单混淆。我也找不到lsof 的端口号。 nc -l 仍然说它正在使用中,与发布的完全一样。 简单的解决方案是不要写那样的代码,确定吗?通常你会立即连接Socket,而且绑定它通常没什么意义。 How to investigate ports opened by a certain process in linux?、Find original owning process of a Linux socket、How to kill a process running on particular port in Linux?、How do I find and kill process running on a certain port? 等可能重复 @jww 令人惊讶的是,这些都不适用 【参考方案1】:

我在 Ubuntu 下测试了你的 Java 程序。

如何找到绑定socket但不监听也不连接的进程:

lsof

lsof | grep "can't identify protocol"

你会得到如下结果:

COMMAND     PID   TID       USER   FD      TYPE             DEVICE SIZE/OFF    NODE NAME
java      29644 29653    stephan   12u     sock                0,7      0t0  312066 can't identify protocol

请注意类型sock 和名称can't identify protocol

这是如何工作的?看看lsof的FAQ:

为什么基于 /proc 的 lsof 报告某些套接字文件“无法识别协议”?

/proc-based lsof 可能会报告:

  COMMAND PID ... TYPE ... NODE NAME
  pump    226 ... sock ...  309 can't identify protocol

这意味着它无法识别协议(即 AF_* 指定)被打开的套接字文件使用。 Lsof 标识 协议通过匹配与关联的节点号 /proc//fd 条目到在选定文件中找到的节点号 /proc/net 子目录。

...

您可能无法找到所需的节点号,因为并非所有 内核协议模块完全支持 /proc/net 信息。

验证过程

lsof 输出中的 PID 为 29644。

ls -l /proc/29644/fd   

导致:

...
lrwx------ 1 stephan stephan 64 Jul  7 22:52 11 -> socket:[312064]
lrwx------ 1 stephan stephan 64 Jul  7 22:52 12 -> socket:[312066]
...

grep 312066 /proc/net/*

给出一个空结果。

【讨论】:

我的lsof 显示27401296 protocol: TCPTCP *:21000 (LISTEN) 否则(没有“无法识别协议”)。这并不理想,因为它不允许查看哪个程序保留哪个端口,但至少这可以识别一些候选者! 是的,使用第二列中 PID 的 lsof 输出,您只能获得潜在的候选人。但应该不会太多。该问题应该仅在两种情况下出现:套接字已绑定但未侦听/连接或使用后未正确关闭套接字。我发现你的 lsof 输出很有趣。我需要看看我明天是否可以更新我的 lsof 版本。 这是来自 Debian 测试的 4.89。

以上是关于如何找到哪个进程绑定了一个套接字但不监听?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法让多个进程共享一个监听套接字?

我必须将侦听套接字绑定到哪个主机?

网络:多个进程能否监听同一个端口号?

httpd Apache服务

找出异步套接字绑定的端口?

在不相关的进程之间共享套接字(监听)