专栏首页专注网络研发一道腾讯面试题目:没有listen,能否建立TCP连接

一道腾讯面试题目:没有listen,能否建立TCP连接

这个题目是之前在我的QQ群里一个同学在腾讯面试过程中被问到的。当时在群里做了简单的讨论,今天系统的把这个问题分析一遍。

TCP与UDP最大的不同,就是有连接的概念,而连接的建立是由内核完成的。系统调用listen,就是为了告诉内核,它要处理发给这个TCP端口的连接请求。所以对于这个题目,最直接的想法就是由应用层自己负责TCP的连接。为了能够收到TCP的握手数据包,可以尝试使用原始套接字来接收IP报文,这样就可以在应用层替代内核做TCP的三次握手了。这个想法不错,可惜现实比较残酷,七年前我已经试过这个方案了。请看当时年轻的我写的一篇记录:http://dyanmate555.blog.chinaunix.net/uid-23629988-id-85891.html。

当没有对于TCP 套接字处于listen状态时,使用raw socket处理握手报文时,即使收到了syn报文并给对端发送了syn+ack报文,也无法完成连接。因为内核一般会提前发送RST中断该连接。七年前,我当时只知道这个结果。现在呢,可以明确的知道为什么内核会发送RST报文,中断连接。内核在ip_local_deliver_finish先将报文复制一份给原始套接字,然后会继续后面的处理,进入tcp的接收函数tcp_v4_rcv。在这个函数中,要进行套接字的查找。

因为没有监听的tcp套接字,自然无法找到对应的套接字。于是跳转到no_tcp_socket。

在这个错误处理中,只要数据包skb的校验和没错,内核就会调用tcp_v4_send_reset发送RST中止这个连接。因此,这个单独使用raw socket的方案是行不通的。我们需要找到一种方法,禁止内核处理该TCP报文。这时,可以使用iptables的NFQUEUE,在网络层将数据包取到应用层,并回复syn+ack,同时在reinject时通知内核该数据包被“偷”了,即NF_STOLEN。这样内核就不会继续处理该数据包skb了。虽然当时我没有继续实验尝试,但理论上,通过这个IPtables的NFQUEUE+NFSTOLEN的方案,是可以实现“没有listen,建立TCP连接”的。

可惜,在与那位同学的讨论中,腾讯面试题目的本意不是这个意思,而是对于普通的TCP套接字来说,如果没有listen调用,是否可以创建连接。即使限定了条件,答案依然是肯定的。只不过限定了条件之后,我们需要确定2个事情:

  1. 与前面类似,如何避免内核发送RST。在不能使用iptable的前提下,这意味着在tcp_v4_rcv中,要能够找到对应的套接字。
  2. 没有listen状态的套接字,内核是否能够完成TCP的三次握手呢?

确定第一个问题,比较简单。只需要对三次握手深入的思考一下,就可以得到答案。在正常的三次握手中,当服务端回复syn+ack时,客户端实际上也没有处于listen状态的套接字,但却可以完成三次握手。这意味着,客户端进行connect调用后,该套接字一定被加入到某个表中,并可以被匹配到。跟踪内核源码tcp_v4_connect->inet_hash_connect->__inet_check_established,可以看到当调用connect时,对应的套接字就被加入了全局的tcp已连接的表中,即tcp_hashinfo.ehash中。对应的匹配TCP套接字过程,如下__inet_lookup_skb->__inet_lookup

内核是先在已经连接的表中查找,再进行listen表的查找。对于客户端来说,syn+ack报文必然可以在已连接表中匹配上对应的套接字。那么,对于本题目来说,要想两端都可以找到套接字,就要求在报文到达前,两端都调用了connect。也就是说,当两端同时调用connect时,两端的syn包就都可以匹配上本地的套接字。

接下来只需要确定对于客户端套接字来说,收到syn报文,是否可以正常处理。tcp_rcv_synsent_state_process函数是TCP套接字处于synsent状态(即调用了connect)的处理函数。下面是其一部分实现代码:

从上,可以得出,对于处于synsent状态的套接字来说,如果收到了syn报文,则会正常回复synack,完成TCP三次握手。

对于腾讯的这道面试题目来说,其答案就是当两端同时发起connect调用时,即使没有listen调用,也可以成功创建TCP连接。

如果去掉“两端”的限制,还有一个答案就是,TCP套接字可以connect它本身bind的地址和端口,也可以达成要求。下面的链接是测试代码,实现了一个TCP套接字成功连接自己,并发送消息。

https://github.com/gfreewind/LinuxDetails/blob/master/networks/5.no_listen_tcp_conn/no_listen_tcp_conn.c

其连接截图如下:

从截图中,可以看到TCP套接字成功的“连接”了自己,并发送和接收了数据包围。netstat的输出更证明了TCP的两端地址和端口是完全相同的。

PS: 本文中涉及的内核代码,均为最新的Linux内核源码。

本文分享自微信公众号 - LinuxerPub(LinuxerPub),作者:glinuxer

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-06-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • TCP的MTU Probe和MSS(2)

    在上一篇《TCP的MTU Probe和MSS(1)》介绍了TCP使用MTU Probe来避免PMTU变小而导致发送失败的方法。其主要思想是在TC...

    glinuxer
  • 应用层如何强制发送RST即相关内核实现

    前几天群里有个同学问,“如何让应用层强制发送RST中止连接”,而不是通过FIN包的四次交互来关闭连接。当时,我只是凭借以往的经验,猜测使用lin...

    glinuxer
  • 以讹传讹的“tcp_tw_reuse”

    因为Linux是一个通用的操作系统,所以其运行环境也是为了通用环境设计的,不会太好,也不会太坏,因为其要默认兼容大部分环境。因此在做服务端部署的...

    glinuxer
  • Python灰帽编程 3.3 MAC洪水

    Python灰帽编程 3.3 MAC洪水 传统的交换机(我只对我目前使用的交互机做过测试,按照常识只能这样表述)在数据转发过程中依靠对CAM表的查询来确定正确的...

    用户1631416
  • 程序员七夕情人节如何过?

    七夕就已经是我国传统习俗,乞巧;又是传统节日,情人节。在七夕即将到来之际,程序员们当然不会坐等干吃狗粮。毕竟这共享时代,除了自行车、汽车、充电宝、雨伞等可以共享...

    kubernetes中文社区
  • sysctl.conf学习和调优

    ? 前言 记得第一次接触/etc/security/limits.conf和/etc/sysctl.conf时 是因为部署Oracle时要按需修改内核参数。l...

    小小科
  • Docker学习——Dockerfile 指令详解(五) 顶

    我们已经介绍了 FROM (指定基础镜像) , RUN(执行命令) ,还提及了 COPY , ADD ,其实 Dockerfile 功能很强大,它提供了十多个指...

    wuweixiang
  • 让Jexus支持高并发请求的优化技巧

    Jexus web server 5.1 每个工作进程的最大并发数固定为1万,最多可以同时开启4个工作进程,因此,每台Jexus V5.1服务器最多可以到支持4...

    张善友
  • linux性能调优(整理)

    为什么要性能调优? 大部分的linux发行版是为了完全兼容市场中大部分计算机而设计的。这是一个相当混杂的硬件集合(硬盘,显卡,网卡,等等)。所以Red Hat,...

    小小科
  • Skype for Business Web 应用

     微软统一通信,我最看好的一点就是跨界无缝沟通,其中比较欣赏的是网页加入会议,支持IE\Chrome\Firfox,只要有internet、有pc、有浏览器,就...

    杨强生

扫码关注云+社区

领取腾讯云代金券