温馨提示×

温馨提示×

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

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

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

发布时间:2021-11-23 22:08:15 阅读:211 作者:柒染 栏目:大数据
开发者测试专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

这篇文章给大家介绍如何深入理解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 typeint 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实现就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

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

原文链接:https://my.oschina.net/u/4217331/blog/4379134

AI

开发者交流群×