前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解TCP/IP协议的实现之accept(基于linux1.2.13)

深入理解TCP/IP协议的实现之accept(基于linux1.2.13)

作者头像
theanarkh
发布2020-03-12 00:22:16
7520
发布2020-03-12 00:22:16
举报
文章被收录于专栏:原创分享原创分享

我们解析分析tcp/ip协议的实现,这一篇讲一下accept,accept就是从已完成三次握手的连接队列里,摘下一个节点。我们可以了解到三次握手的实现和过程。很多同学都了解三次握手是什么,但是可能很少同学会深入思考或者看他的实现,众所周知,一个服务器启动的时候,会监听一个端口。其实就是新建了一个socket。那么如果有一个连接到来的时候,我们通过accept就能拿到这个新连接对应的socket。那么这个socket和监听的socket是不是同一个呢?其实socket分为监听型和通信型的。表面上,服务器用一个端口实现了多个连接,但是这个端口是用于监听的,底层用于和客户端通信的其实是另一个socket。所以每一个连接过来,负责监听的socket发现是一个建立连接的包(syn包),他就会生成一个新的socket与之通信(accept的时候返回的那个)。我们将会从代码中看到这个实现。 我们从accept函数开始,详细分析这个过程。

代码语言:javascript
复制
static int sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
    struct file *file;
    struct socket *sock, *newsock;
    int i;
    char address[MAX_SOCK_ADDR];
    int len;

    if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
        return(-EBADF);
    // 根据文件描述符找到对应的file结构体和socket结构
      if (!(sock = sockfd_lookup(fd, &file))) 
        return(-ENOTSOCK);
    if (sock->state != SS_UNCONNECTED) 
    {
        return(-EINVAL);
    }
    // socket没有调用过listen,报错,该标记位在listen中设置
    if (!(sock->flags & SO_ACCEPTCON)) 
    {
        return(-EINVAL);
    }
    // 分配一个新的socket结构体
    if (!(newsock = sock_alloc())) 
    {
        printk("NET: sock_accept: no more sockets\n");
        return(-ENOSR); /* Was: EAGAIN, but we are out of system
                   resources! */
    }
    newsock->type = sock->type;
    newsock->ops = sock->ops;
    // 创建一个底层的sock结构体和新的socket结构体互相关联
    if ((i = sock->ops->dup(newsock, sock)) < 0) 
    {
        sock_release(newsock);
        return(i);
    }
    // accept返回一个新的sock和socket关联
    i = newsock->ops->accept(sock, newsock, file->f_flags);
    if ( i < 0) 
    {
        sock_release(newsock);
        return(i);
    }
    // 返回一个新的文件描述符
    if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
    {
        sock_release(newsock);
        return(-EINVAL);
    }
    // 是否需要获取socket对应的地址
    if (upeer_sockaddr)
    {
        newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
        move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
    }
    return(fd);
}

我们一步步来分析这个函数。 1 通过fd找到对应的socket结构体,然后申请一个新的socket结构体和sock结构体,并且把他们两互相关联。这个在前面的文章分析过。 2 然后把监听的socket和准备用于通信的结构体作为参数,调用accept函数。 3 最后返回通信socket对应的文件描述符。

下面我们开始分析accept函数的真正实现。

代码语言:javascript
复制
static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
    struct sock *sk1, *sk2;
    int err;
    sk1 = (struct sock *) sock->data;
    // 返回一个新的sock结构体
    sk2 = sk1->prot->accept(sk1,flags);
    // 互相关联
    newsock->data = (void *)sk2;
    sk2->socket = newsock;
    newsock->conn = NULL;
    // 设置sock为已经建立连接状态
    newsock->state = SS_CONNECTED;
    return(0);
}

这个函数主要是调底层的accept函数,底层accept函数会返回一个新的sock结构体,socket和sock结构体的区别和背景在之前的文章里已经分析过。总的来说,accept函数就是申请一个新的通信socket,这个socket关联了一个新的sock结构体。下面我们看看tcp层的accept函数。

代码语言:javascript
复制
static struct sock *tcp_accept(struct sock *sk, int flags)
{
    struct sock *newsk;
    struct sk_buff *skb;

    // 是一个listen的套接字
    if (sk->state != TCP_LISTEN) 
    {
        sk->err = EINVAL;
        return(NULL); 
    }

    cli();
    // 从sock的receive_queue队列摘取已建立连接的节点,
    while((skb = tcp_dequeue_established(sk)) == NULL) 
    {   
        // 没有已经建立连接的节点,但是设置了非阻塞模式,直接返回
        if (flags & O_NONBLOCK) 
        {
            sti();
            release_sock(sk);
            sk->err = EAGAIN;
            return(NULL);
        }
        release_sock(sk);
        //阻塞进程,如果后续建立了连接,则进程被唤醒的时候,就会跳出while循环
        interruptible_sleep_on(sk->sleep);
      }
    sti();

    // 拿到一个新的sock结构,由建立连接的时候创建的
    newsk = skb->sk;

    // 返回新的sock结构体
    return(newsk);
}

这个函数主要的逻辑是从监听型socket的已完成三次握手的队列里摘下一个节点。这个节点是一个sk_buff结构体,sk_buff是一个表示网络数据包的数据结构。

accept函数就分析完了。下一篇我们分析三次握手。看看accept函数摘下的这个节点是如果生成的。

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

本文分享自 编程杂技 微信公众号,前往查看

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

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

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