网络协议栈4:bind()函数

正常的情况下,socket函数的调用,只要有足够的内存用于分配socket结构体,sock结构体,以及空闲的I节点和当前进程有空闲的文件表项,就会返回分配给当前进程分配此I节点的文件表项的序号,即文件句柄,通过这个文件句柄,可以找到对应的文件表项,通过文件表项可以找到对应的I节点,通过I节点可以找到socket套接字,通过套接字可以找到对以的sock结构体,而这个结构体就是我们数据的信使了,注意,只是信使哦。

 

当应用程序调用socket创建完套接字后,一般情况下,接下来就会调用bind()函数来给这个socket绑定一个本地地址,即绑定一个IP地址,也即绑定一块网卡,即这个套接字的数据,就是从指定的网卡发送出去了(如果有多张网卡的话);而如果不调用bind()函数指定一个本地地址的话,在后面的connect函数会先判断是否已经绑定了一个本地地址,如果没有绑定,connect函数会自动绑定一个本地地址的。

 

一般的,我们应用层调用bind()函数如下:

       bzero(&cliaddr,sizeof(cliaddr));

       cliaddr.sin_family = AF_INET;

       cliaddr.sin_port = htons(CLIENT_PORT);

       cliaddr.sin_addr.s_addr = inet_addr("192.168.0.234");

 

       if (bind(clifd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<0)

       {

              printf("bind to port %d failure!/n",CLIENT_PORT);

              exit(1);

       }

首先把本地的IP地址,端口号和协议类型填充好,再调用bind()函数对刚从socket函数返回的文件句柄指定的sock结构体的相关字段进行赋值。

 

1                   static int sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)

2                   {

3                          struct socket *sock;

4                          int i;

5                          char address[MAX_SOCK_ADDR];

6                          int err;

7                  

8                          if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)

9                                 return(-EBADF);

10                        

11                         if (!(sock = sockfd_lookup(fd, NULL)))

12                                return(-ENOTSOCK);

13                   

14                         if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)

15                               return err;

16                   

17                         if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0)

18                         {

19                                return(i);

20                         }

21                         return(0);

22                  }

 

第13行,通过文件句柄找到对应的socket,之后第14行把应用层的数据复制到内核,虽然数据都是一样的,但是因为空间不同,需要转移才能使用,第17行就是调用socket所属的协议的操作集中的bind()函数来真正的绑定本地地址,

即static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)。而绑定是分两部分即端口号和本地地址进行的。

这个函数最主要的工作是如果应用层没有指定端口号,则需要查找空闲的端口号,通过到的空闲的端口号,来决定sock结构体要怎么管理,而如果指定了端口号和IP地址了,就可以直接通过端口号和IP地址得到要插入的是哪个队列,哪个位置。

对于sock的管理,系统在sock结构体中的

struct proto                   *prot;

变量中的

struct sock *          sock_array[SOCK_ARRAY_SIZE];

指针数组来管理的,即通过得到的空闲端口号,跟(SOCK_ARRAY_SIZE -1)取模,得到应该插入哪个队列中,确定了要插入的队列后,再确认该队列的最早的空闲位置,即队列的尾部,之后,把这个即将插入这个队列的sock先从其他的链表中删除,再插入到当前确定的位置,在插入之前,系统已经把本地地址和端口号都已经给sock结构体赋值OK了。

到此,绑定端口号和IP地址的工作就做完了

posted on 2012-01-05 20:11  image eye  阅读(1401)  评论(0编辑  收藏  举报