前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >字节三面,答的不满意

字节三面,答的不满意

作者头像
小林coding
发布2022-10-27 12:57:34
4730
发布2022-10-27 12:57:34
举报
文章被收录于专栏:小林coding小林coding

作者:小林coding

八股文网站:xiaolincoding.com

大家好,我是小林。

早上看到一个读者说面字节三面的时候,问了这个问题:

这位读者的角度是以为服务端没有调用 listen,客户端会 ping 不通服务器,很明显,搞错了。

ping 使用的协议是 ICMP,属于网络层的事情,而面试官问的是传输层的问题。

针对这个问题,服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,然后客户端对服务端发起了连接建立,此时那么会发生什么呢?

这个问题,自己做个实验就知道了。

我用下面这个程序作为例子,绑定了 IP 地址 + 端口,而没有调用 listen。

代码语言:javascript
复制
/*******服务器程序  TCPServer.c ************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
    int sockfd, ret;
    struct sockaddr_in server_addr;

    /* 服务器端创建 tcp socket 描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
        exit(1);
    }

    /* 服务器端填充 sockaddr 结构 */
    bzero(&server_addr, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(8888);
  
  /* 绑定 ip + 端口 */
    ret = bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));
    if(ret < 0)
    {
        fprintf(stderr, "Bind error:%s\n\a", strerror(errno));
        exit(1);
    }
  
  //没有调用 listen
    
    sleep(1000);
    close(sockfd);
    return 0;
}

然后,我用浏览器访问这个地址:http://121.43.173.240:8888/

报错连接服务器失败。

同时,我也用抓包工具,抓了这个过程。

可以看到,客户端对服务端发起 SYN 报文后,服务端回了 RST 报文。

所以,这个问题就有了答案,服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,然后客户端对服务端发起了连接建立,服务端会回 RST 报文。

接下来,带大家源码分析一下。

Linux 内核处理收到 TCP 报文的入口函数是 tcp_v4_rcv,在收到 TCP 报文后,会调用 __inet_lookup_skb 函数找到 TCP 报文所属 socket 。

代码语言:javascript
复制
int tcp_v4_rcv(struct sk_buff *skb)
{
 ...
  
 sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
 if (!sk)
  goto no_tcp_socket;
 ...
}

__inet_lookup_skb 函数首先查找连接建立状态的socket(__inet_lookup_established),在没有命中的情况下,才会查找监听套接口(__inet_lookup_listener)。

查找监听套接口(__inet_lookup_listener)这个函数的实现是,根据目的地址和目的端口算出一个哈希值,然后在哈希表找到对应监听该端口的 socket。

本次的案例中,服务端是没有调用 listen 函数的,所以自然也是找不到监听该端口的 socket。

所以,__inet_lookup_skb 函数最终找不到对应的 socket,于是跳转到no_tcp_socket。

在这个错误处理中,只要收到的报文(skb)的「校验和」没问题的话,内核就会调用 tcp_v4_send_reset 发送RST中止这个连接。

至此,整个源码流程就解析完。

其实很多网络的问题,大家都可以自己做实验来找到答案的。

好了,今天周末就水到了,下次见!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小林coding 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档