首页 > 代码库 > lwip socket探秘之bind

lwip socket探秘之bind

一个基本的socket建立顺序是
Server端:
  • socket()
  • bind()
  • listen()
  • accept()
  • recv()
Client端:
  • socket()
  • connect()
  • send()
 
本文着重介绍Server端的bind()过程。
 
用户调用bind()时,实际调用的是lwip_bind(),我们从这个函数看起:
 1 int 2 lwip_bind(int s, const struct sockaddr *name, socklen_t namelen) 3 { 4 .................. 5   sock = get_socket(s); // 根据socket()函数返回的socket号取得socket在lwip中的完整结构体 6   if (!sock) 7     return -1; 8 ................. 9   local_addr.addr = ((const struct sockaddr_in *)name)->sin_addr.s_addr;10   local_port = ((const struct sockaddr_in *)name)->sin_port;11 .................12   err = netconn_bind(sock->conn, &local_addr, ntohs(local_port));  // 主要工作在这个函数里13 .................14   return 0;15 }
lwip_bind()需要由用户传入待绑定的ip地址和端口号。
 
接下来看函数netconn_bind()
 1 err_t 2 netconn_bind(struct netconn *conn, struct ip_addr *addr, u16_t port) 3 { 4   struct api_msg msg; 5  6   LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); 7  8   msg.function = do_bind; // api_msg 的用法在socket创建那一篇文章中已经介绍过,我们已经这道实际上之后会调用到do_bind函数 9   msg.msg.conn = conn;10   msg.msg.msg.bc.ipaddr = addr;11   msg.msg.msg.bc.port = port;12   TCPIP_APIMSG(&msg);13   return conn->err;14 }
do_bind中根据用户传入的type分别调用raw_bind()、tcp_bind()或raw_bind()。
以tcp_bind()为例:
 1 err_t 2 tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) 3 { 4 ............. 5   if (!ip_addr_isany(ipaddr)) {  // 如果用户传入的ip地址不是0,就把用户传入的ip地址记到pcb里 6     pcb->local_ip = *ipaddr; 7   } 8   pcb->local_port = port; // 把用户传入的端口号记到pcb里 9 ..............10 }
至此,bind()函数把用户传入的ip地址和port号记到了pcb里。这也是bind()的含义。
 
但是这里我们看到,如果用户传入的ip地址不是0,才把ip地址记到pcb里,那么不放过任何一点疑问的我们就要问了,如果用户传入的是0呢,那么pcb->local_ip是多少呢?
答案是,如果用户传个0的ip地址进来,这里就先不设置pcb->local_ip了。当TCP层发包时,会调用到tcp_output_segment()函数,而这个函数里有:
 1 static void 2 tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) 3 { 4 .......... 5   /* If we don‘t have a local IP address, we get one by 6      calling ip_route(). */ 7   if (ip_addr_isany(&(pcb->local_ip))) { 8     netif = ip_route(&(pcb->remote_ip)); 9     if (netif == NULL) {10       return;11     }12     ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); // 设置pcb的ip地址13   }14 ..........15 }
注意上面红色的注释。这段代码说明,当TCP层发包时,如果发现pcb->local_ip是0,就会从ip层获得一个本机地址,把这个本机地址填入pcb->local_ip。
 
因此我们又可以知道,如果用户在调用bind()时,想直接使用本机地址作为绑定的ip地址,可以直接在这个参数传一个0。这样在bind()时不设置pcb里的ip地址,而在TCP层发包时才通过调用ip层函数来设置pcb的ip地址。

lwip socket探秘之bind