现在的位置: 首页 > 综合 > 正文

套接字基础

2014年01月23日 ⁄ 综合 ⁄ 共 4773字 ⁄ 字号 评论关闭

套接字基础

一、套接字与套接字接口

套接字是应用程序访问系统网络服务的接口。端到端的通信通过一对套接字来实现,一个套接字对应一个通信端点。

从实现来看,套接字是端端通信的抽象描述。在应用程序里,套接字对应一个整数值(套接字描述符);在内核里,套接字对应一个管理通信过程的对象(struct socket结构)。该结构与前面所说的整数值一一对应。

在Linux系统内核中,struct socket结构对象不仅封装了管理通信过程的数据信息,还封装了负责网络通信的功能函数(其具体的实现方式有些类似MFC中的回调函数)。为了便于访问这些功能函数,Linux提供了一系列编程接口函数,即套接字接口。通过这些接口,应用程序触发系统调用来调用上述功能函数,从而访问网络服务。

根据底层网络机制的差异,套接字可以定义为不同协议族的套接字。比如INET协议族套接字,UNIX域套接字等。在内核源文件include/linux/socket.h中,每个协议族标识符被定义为一个整数:

   1: #define PF_UNSPEC    AF_UNSPEC
   2: #define PF_UNIX      AF_UNIX
   3: #define PF_LOCAL     AF_LOCAL
   4: #define PF_INET      AF_INET
   5: #define PF_AX25      AF_AX25
   6: ...

创建套接字时,可以通过参数选择协议族。如果指定为PF_INET协议族,则称套接字为INET套接字。INET套接字的接口函数提供了TCP/IP网络服务功能。

套接字接口会实现一系列功能以便应用程序使用,包括:套接字创建、地址绑定、连接请求、端口监听、连接请求允许、数据包发送和接收等。

二、套接字创建流程

应用程序采用socket函数创建套接字。socket会触发内核调用sys_socket函数,随后sys_socket又调用sock_create函数。根据socket函数在参数中指定的协议族类型sock_create函数有选择地调用不同的套接字创建函数,例如,当指定PF_INET协议族时,sock_create函数调用inet_create创建INET套接字。这些套接字函数首先创建套接字的内核表示结构,再返回一个套接字描述符来标识生成的套接字对象。socket函数返回时,应用程序获得这个套接字描述符。

三、一些重要的套接字数据结构

Linux内核提供了一系列管理套接字的数据类型,此处只介绍几个比较重要的。其中,struct net_proto_family是协议族管理类型,负责不同协议族套接字的创建;struct socket是套接字结构类型,每个套接字在内核中都对应唯一的struct sockt结构;struct proto_ops是协议族套接字的操作集,统一管理套接字操作函数;struct sock是套接字在传输层的表示类型,为套接字指定传输层协议后,其struct socket的sk指针将指向一个与传输协议关联的struct
sock结构;struct proto是传输层协议操作集,统一管理传输层协议的操作函数。

1、struct net_proto_family

   1: struct net_proto_family
   2: {
   3:     int family;                                          //协议族标志
   4:     int (*create)(strcut socket *sock, int protocol);    //套接字创建方法
   5:     short authentication;                                //认证管理字段
   6:     short encryption;                                    //加密管理字段
   7:     short encrypt_net;
   8:     struct module * owner;
   9: };

struct net_proto_family管理不同协议族套接字的创建方法,其中create指针指向具体协议族套接字的创建函数。在include/linux/socket.h文件中,内核用整数定义这些协议族。在初始化时,Linux系统支持的协议族被注册到数组static struct net_proto_family *net_families中。以下为一些常见协议族

   1: #define PF_UNIX            1        //UNIX域协议族
   2: #define PF_INET            2        //TCP/IP协议族
   3: #define PF_IPX             4        //Novell网的IPX协议族
   4: #define PF_APPLETALK       5
   5: #define PF_ATMPVC          8
   6: #define PF_X25            9
   7: #define PF_INET6          10
   8: #define PF_NETLINK        16

Linux通过net_families表来管理协议族,该表是struct net_proto_family类型的指针数组,定义为:

   1: static struct net_proto_family * net_families[NPROTO];

协议族初始化时,套接字创建方法被sock_register函数(位于net\socket.c中)注册到net_families中:

   1: int sock_register(const struct net_proto_family *ops)
   2: {
   3:     ...
   4:     if (rcu_dereference_protected(net_families[ops->family],
   5:                       lockdep_is_held(&net_family_lock)))
   6:         err = -EEXIST;
   7:     else {
   8:         rcu_assign_pointer(net_families[ops->family], ops);
   9:         err = 0;
  10:     }
  11:     ...
  12: }

比如INET协议族初始化时,函数inet_init调用sock_register来注册INET套接字的创建方法(struct net_proto_family类型变量inet_family_ops管理INET套接字的创建方法)。

sock_register函数定义如下:

   1: int sock_register(const struct net_proto_family *ops)
   2: {
   3:     int err;
   4:  
   5:     if (ops->family >= NPROTO) {
   6:         printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n", ops->family,
   7:                NPROTO);
   8:         return -ENOBUFS;
   9:     }
  10:  
  11:     spin_lock(&net_family_lock);
  12:     if (rcu_dereference_protected(net_families[ops->family],
  13:                       lockdep_is_held(&net_family_lock)))
  14:         err = -EEXIST;
  15:     else {
  16:         rcu_assign_pointer(net_families[ops->family], ops);
  17:         err = 0;
  18:     }
  19:     spin_unlock(&net_family_lock);
  20:  
  21:     printk(KERN_INFO "NET: Registered protocol family %d\n", ops->family);
  22:     return err;
  23: }

例如,inet_family_ops变量定义为:

   1: struct net_proto_family inet_family_ops =
   2: {
   3:     .family = PF_INET,
   4:     .create = inet_create,
   5:     .owner = THIS_MODULE
   6: };

变量inet_family_ops管理INET协议族套接字的创建方法,其create指针指向inet_create函数。

上文提到的inet_init函数代码如下:

   1: static int __init inet_init(void)
   2: {
   3:     (void)sock_register(&inet_family_ops);
   4: }

struct net_proto_family的定义代码位于文件include/linux/net.h

2、struct proto_ops

   1: struct proto_ops
   2: {
   3:     int family;//协议族
   4:     struct module *owner;//所属模块
   5:     //以下均为以函数指针形式指定的套接字操作
   6:     int (*release)(struct socket *sock);
   7:     int (*bind)(struct socket *sock, struct sockaddr *myaddr,
   8:                 int sockaddr_len);
   9:     ...
  10: };

struct proto_ops类型是协议族操作集。不同协议族套接字的操作函数可能不同,但Linux通过struct proto_ops中那些函数指针统一了接口,用这些函数指针来操作套接字。对于INET协议族的TCP和UDP协议,Linux分别提供了inet_stream_ops和inet_dgram_ops两个struct proto_ops类型的变量,以inet_stream_ops为例:

   1: struct proto_ops inet_stream_ops = 
   2: {
   3:     .family = PF_INET,
   4:     .owner = THIS_MODULE,
   5:     .release = inet_release,
   6:     .bind = inet_bind,
   7:     ...
   8: }

struct proto_ops的声明位于include/linux/net.h

3、struct socket类型

   1: struct socket
   2: {
   3:     socket_state    state;//状态值
   4:     unsigned long flags;//标识
   5:     struct proto_ops *ops;//指向一个struct proto_ops结构,为套接字提供协议族操作集
   6:     struct fasync_struct *fasync_list;//异步唤醒链表
   7:     struct file *file;//文件指针
   8:     struct sock *sk;//指向struct sock结构体,该结构体是套接字在传输层的表示结构
   9:     wait_queue_head_t wait;//等待队列
  10:     short type;//传输层数据类型
  11:     unsigned char passcred;//授权描述
  12: };

struct socket类型统一表示不同协议族的套接字,它与应用程序引用的套接字描述符一一对应。成员ops指向struct proto_ops结构体,代表具体协议族套接字的操作集。比如若INET协议族采用TCP传输协议,那么ops指向的结构体为inet_stream_ops;若INET协议族套接字采用UDP传输协议,那么ops指向的结构体等同于inet_dgram_ops。sk指向的struct sock结构体代表了传输层的套接字结构,包含了与具体传输层协议相关的信息,如其中的sk_prot指针提供了传输层的操作集。

struct socket的定义代码位于文件include/linux/net.h

4、struct proto类型

   1: struct proto
   2: {
   3:     void (*close)(struct sock *sk, long timeout);//关闭套接字
   4:     ...//一系列函数指针
   5:     atomic_t *memory_allocated;//分配的内存数
   6:     atomic_t *sockets_allocated;//分配的套接字数
   7:     int *memory_pressure;//与memory_allocated有关的控制
   8:     int *sysctl_mem;//内存访问限制指针
   9:     int *sysctl_wmem;
  10:     int *sysctl_rmem;

抱歉!评论已关闭.