如何深入理解TCP/IP协议的socket实现

发布时间:2021-11-23 22:08:15 作者:柒染
来源:亿速云 阅读:193

这篇文章给大家介绍如何深入理解TCP/IP协议的socket实现,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

socket大家都知道是用于网络通信的,也知道他是ip和端口的组合。但是很多同学可能不是很清楚socket的原理和实现。下面我们深入理解一下socket到底是什么。
    我们回忆一下socket编程的步骤,不管是客户端还是服务端,第一个调的函数都是socket。我们就从这个函数的实现开始,看看一个socket到底是什么。

// 新建一个socket结构体,并且创建一个下层的sock结构体,互相关联
static int sock_socket(int family, int type, int protocol)
{
    int i, fd;
    struct socket *sock;
    struct proto_ops *ops;

    // 找到对应的协议族,比如unix域、ipv4
    for (i = 0; i < NPROTO; ++i) 
    {   // 从props数组中找到family协议对应的操作函数集,props由系统初始化时sock_register进行操作
        if (pops[i] == NULL) continue;
        if (pops[i]->family == family) 
            break;
    }

    if (i == NPROTO) 
    {
          return -EINVAL;
    }
    // 函数集
    ops = pops[i];

    // 检查一下类型
    if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
        type != SOCK_SEQPACKET && type != SOCK_RAW &&
        type != SOCK_PACKET) || protocol < 0)
            return(-EINVAL);

    // 分配一个新的socket结构体
    if (!(sock = sock_alloc())) 
    {
        ...
    }
    // 设置类型和操作函数集
    sock->type = type;
    sock->ops = ops;
    if ((i = sock->ops->create(sock, protocol)) < 0) 
    {
        sock_release(sock);
        return(i);
    }
    // 返回一个新的文件描述符
    if ((fd = get_fd(SOCK_INODE(sock))) < 0) 
    {
        sock_release(sock);
        return(-EINVAL);
    }

    return(fd);
}
 

我们从上到下,逐步分析这个过程。
1 根据传的协议类型,找到对应的函数集,因为不同的协议族他的底层操作是不一样的。
2 分配一个socket结构体。定义如下。我们大概了解一下字段就行。

struct socket {
  short            type;       /* SOCK_STREAM, ...     */
  socket_state        state;
  long            flags;
  struct proto_ops    *ops;   
  // 这个字段要记一下    
  void            *data;      
  struct socket        *conn;      
  struct socket        *iconn;     
  struct socket        *next;
  struct wait_queue    **wait;     
  struct inode        *inode;
  struct fasync_struct  *fasync_list;    
};

struct socket *sock_alloc(void)
{
    struct inode * inode;
    struct socket * sock;
    // 获取一个可用的inode节点
    inode = get_empty_inode();
    if (!inode)
        return NULL;
    // 初始化某些字段
    inode->i_mode = S_IFSOCK;
    inode->i_sock = 1;// socket文件
    inode->i_uid = current->uid;
    inode->i_gid = current->gid;
    // 指向inode的socket结构体,初始化inode结构体的socket结构体
    sock = &inode->u.socket_i;
    sock->state = SS_UNCONNECTED;
    sock->flags = 0;
    sock->ops = NULL;
    sock->data = NULL;
    sock->conn = NULL;
    sock->iconn = NULL;
    sock->next = NULL;
    sock->wait = &inode->i_wait;
    // 互相引用
    sock->inode = inode;        /* "backlink": we could use pointer arithmetic instead */
    sock->fasync_list = NULL;
    // socket数加一
    sockets_in_use++;
    // 返回新的socket结构体,他挂载在inode中
    return sock;
}
 

sock_alloc首先分配了一个inode,inode节点里有一个socket结构体,然后初始化socket结构体的一些字段,并把他的地址返回。

3 这时候我们拿到一个socket结构体。接着调create函数(省略了部分代码)。

// 创建一个sock结构体,和socket结构体互相关联
static int inet_create(struct socket *sock, int protocol)
{
    struct sock *sk;
    struct proto *prot;
    int err;
    // 分配一个sock结构体
    sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
    switch(sock->type) 
    {
        case SOCK_STREAM:
            protocol = IPPROTO_TCP;
            // 函数集
            prot = &tcp_prot;
            break;

        case SOCK_DGRAM:
            protocol = IPPROTO_UDP;
            prot=&udp_prot;
            break;

    }
    // sock结构体的socket字段指向上层的socket结构体
    sk->socket = sock;
    // 省略一堆对sock结构体的初始化代码
}
 

我们发现创建一个socket的时候,申请了一个socket结构体,同时也申请了一个sock结构体。为什么需要两个结构体,并且这两个结构体关联在一起呢?这要说到网络协议的复杂性,而这个设计就是linux对这个复杂性的解决方案。我们回头看看socket函数的参数。

socket(int family, int type, int protocol)
 

family是协议簇,比如unix域、ipv4、ipv6,type是在第一个参数的基础上的子分类。比如ipv4下有tcp、udp、raw、packet。protocol对tcp、udp没用,对raw、packet的话是标记上层协议类型。这好比一棵树一样,从根节点开始,有很多分支。socket结构体是整个网络协议实现的最上层结构,是第一层抽象。根据协议簇的不同,有不同的实现函数,在同一协议簇下,也有不同的子分类,比如ipv4下有tcp、udp等。不同子类具体的逻辑也不一样。即数据结构和算法都不一样。所以socket结构体有一个data字段,他是自定义的,对于ipv4的实现,他是指向一个sock结构体,对于unix域的实现,unix_proto_data结构体。这就解决了不同协议簇(family)不同实现的问题。那对于同一协议簇下的不同子类型,又如何实现呢?比如ipv4下的tcp、udp。linux给出的方案是在sock结构体中定义一个字段,根据子类型type的值,指向不同的底层协议函数集。

如何深入理解TCP/IP协议的socket实现  
在这里插入图片描述

在申请完sock结构体并且和socket结构体互相关联后。这时候我们拿到了一个inode,一个socket结构体,一个sock结构体。然后根据inode拿到一个file和fd文件描述符。最后返回fd给用户。内容结构图如下。  
 
如何深入理解TCP/IP协议的socket实现  
在这里插入图片描述

这就是socket函数返回后的内存结构体。后续我们调用bind,listen等等函数,传入fd,系统就会根据上面图的指向,一直找到tcp函数集,执行对应的函数,对于udp也是一样,不同是tcp函数集变成udp函数集。

关于如何深入理解TCP/IP协议的socket实现就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

推荐阅读:
  1. 【bug-004】【tcp】网络断开重连后,客户端recv函数一直阻塞,不会返回
  2. nginx的TCPwait太多;

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

tcp ip socket

上一篇:如何深入理解TCP/IP协议的bind实现

下一篇:c语言怎么实现含递归清场版扫雷游戏

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》