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地址。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM