网络协议栈2:socket函数

 

首先,这里会用到一个结构体,先把这个结构体整理出来

 

struct socket {

{

short             type;

/*套接字所用的流类型,可取值SOCK_RAW,SOCK_DGRAM,SOCK_STREAM,SOCK_SEQPACKET,SOCK_PACKET,其中SOCK_STREAM 就是通常所说的TCP协议所用*/

socket_state      state;

/*套接字状态,可取值SS_FREE ,SS_UNCONNECTED ,SS_CONNECTING ,SS_CONNECTED ,SS_DISCONNECTING */

long               flags;/*一些标志信息*/

struct proto_ops     *ops;

/*操作函数集指针,对应初始化pops [ ]数组时的各个协议指针*/

void               *data;

/*私有数据,对于INET域,其指向sock数据结构(跟socket数据结构不同)*/

  struct socket        *conn;            /* server socket connected to */

  struct socket        *iconn;          /* incomplete client conn.s     */

  struct socket        *next;      

/*对于INET域,conn  icon 没有用,next是把所有socket组成链表 */

struct wait_queue   **wait;

/*当进程对套接字的操作无法得到满足时,就在这里等待*/

struct inode       *inode;

/*套接字关联的I节点*/

struct fasync_struct  *fasync_list;

/*该结构用于同步文件的读写*/

}

 

在client.c中,我们是通过

if ((clifd = socket(AF_INET,SOCK_STREAM,0)) < 0)

语句来来创建套接字,socket函数就是创建套接字的函数,我们结合实例跟内核函数来看看套接字时如何被创建的。

在内核中,socket()函数被定义如下

1                  static int sock_socket(int family, int type, int protocol)

2                  {

3                       int i, fd;

4                       struct socket *sock;

5                       struct proto_ops *ops;

6                 

7                       for (i = 0; i < NPROTO; ++i)

8                       {

9                             if (pops[i] == NULL) continue;

10                           if (pops[i]->family == family)

11                                break;

12                     }

13                    

14                     if (i == NPROTO)

15                     {

16                           return -EINVAL;

17                     }

18                    

19                     ops = pops[i];

20                    

21                     if ((type != SOCK_STREAM && type != SOCK_DGRAM &&

22                           type != SOCK_SEQPACKET && type != SOCK_RAW &&

23                           type != SOCK_PACKET) || protocol < 0)

24                                return(-EINVAL);

25                    

26                     if (!(sock = sock_alloc()))

27                     {

28                           printk("NET: sock_socket: no more sockets\n");

29                           return(-ENOSR);

30                           }

31                    

32                     sock->type = type;

33                     sock->ops = ops;

34                     if ((i = sock->ops->create(sock, protocol)) < 0)

35                     {

36                           sock_release(sock);

37                           return(i);

38                     }

39                    

40                     if ((fd = get_fd(SOCK_INODE(sock))) < 0)

41                     {

42                           sock_release(sock);

43                           return(-EINVAL);

44                     }

45                    

46                     return(fd);

47                }

 

其中,第8行就是扫描pops [ ]全局数据,找寻有没有匹配我们应用程序调用socket( )函数时指定的协议,通过上节的socket初始化,我们知道pops [ ] 数组中的某项被

static struct proto_ops inet_proto_ops = {
 AF_INET,

 inet_create,

。。。。。。

}

的地址所填充的,即有pops [ ]->family= AF_INET,结合应用程序的socket调用socket(AF_INET,SOCK_STREAM,0)所指定的family为AF_INET,第10行是得到满足,因此跳出循环。

第26行调用sock = sock_alloc()在内存的I节点表(不是磁盘I节点)上找到一个空闲的I节点,并把这个I节点设置成网络I节点,填充I节点的相关属性后,把I节点中的socket地址返回给sock变量,即sock套接字跟I节点在此时关联起来了,能找到sock,就能找到sock对应的I节点,而知道I节点也能找到对应的套接字。

32,33行设置了套接字的类型,和它所属的协议。34行就是利用套接字所属的协议的create函数来创建套接字的。

40行则是通过fd = get_fd(SOCK_INODE(sock)调用get_fd()来找到当前进程中一个空闲的文件句柄,并把这个文件句柄跟sock中的I节点关联起来,之后把文件句柄返回给调用函数。因为I节点跟sock是关联起来,而I节点是跟文件句柄关联的,因此sock跟文件句柄也是关联在一起的,只要找到文件句柄,就能找到I节点,也就能找到sock了。

34行中的create是如何被赋值并被调用的呢。由19,33行我们看到,ops实质就是初始化socket时pops [ ]数组所被指向的数组的地址,也就是数组

static struct proto_ops inet_proto_ops = {
 AF_INET,

 inet_create,

。。。。。。

}

的地址,因此,34行的create函数,就是inet_create函数,即

static int inet_create(struct socket *sock, int protocol)

由于函数比较长,就不贴出来了,其大致的功能是

创建套接字对应的sock结构体(注意,是sock,而不是socket,这两个结构体是有区别的,而且核心的结构体是sock),并初始化sock结构体,并把sock结构体跟socket结构体关联起来,在结构体socket中有一个

void               *data;

成员,就是用来记录现在创建的sock结构体的首地址的,而sock结构体中也将会有一个成员,用于记录sock结构体对应的socket套接字,这样,sock结构体和socket套接字又被关联起来了。

到此,socket套接字的建立就算完成了,总结起来,所谓的建立套接字,实际上就是调用socket系统调用,创建了socket结构体,sock结构体,同时两个结构体相互关联,并把对应的协议的操作集关联到socket结构体上,再把socket结构体跟I节点关联,I节点又跟文件句柄关联,这样的层层关联,应该层就可以通过常用的文件句柄来得到一个套接字了。

 

posted on 2012-01-05 12:21  image eye  阅读(1772)  评论(0编辑  收藏  举报