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

时间: 2024-10-25 17:06:35

lwip socket探秘之bind的相关文章

lwip socket探秘之listen

一个基本的socket建立顺序是 Server端: socket() bind() listen() accept() recv() Client端: socket() connect() send() 本文着重介绍Server端的listen()过程. 用户使用socket,调用listen()时,实际调用的是lwip里的lwip_listen().代码如下 1 /** 2 * Set a socket into listen mode. 3 * The socket may not have

lwip socket探秘之socket创建

一个基本的socket建立顺序是 Server端: socket() bind() listen() accept() recv() Client端: socket() connect() send() 本文着重介绍Server端的socket()过程. 用户使用socket时,首先会调用socket()函数创建一个socket.在lwip中实际调用的就是lwip_socket()函数. 代码如下: 1 int 2 lwip_socket(int domain, int type, int pr

lwip socket探秘之accept

一个基本的socket建立顺序是 Server端: socket() bind() listen() accept() recv() Client端: socket() connect() send() 本文着重介绍Server端的accept()过程. 上一篇我们已经分析了listen()过程,listen()过程新建了pcb并把它放到了tcp_listen_pcbs这个链表里. 接下来,Client端通过Server绑定的地址和端口号(通过bind绑定),给Server发包.Server收到

lwip socket探秘之recv

一个基本的socket建立顺序是 Server端: socket() bind() listen() accept() recv() Client端: socket() connect() send() 本文着重介绍Server端的recv()过程. 前一篇文章中,accept()生成了一个新的socket,作为server端socket,用于某一个特定client和server通信.本文中我们把它叫做专属socket,和用作listen的socket以示区别. 下面用户就要使用这个新socke

客户端的socket是否需要bind?

bind() 函数的定义与作用-- 将一本地地址与一套接口捆绑.本函数适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用. 当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名.bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号). 服务端-- 服务端进程bind端口:基本是必须要做的事情,比如一个服务器启动时(比如freebsd),它会一个一个的捆绑众所周知的端口来提供服务,同样,如果bin

linux socket TCP UDP bind 同义IP和port

//TCP and UDP can bind to the same IP & port. #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include &

TIME_WAIT状态下,修改socket选项后,bind端口会失败

TIME_WAIT状态下,修改socket选项后,bind端口会失败,即使使用的是SO_REUSEADDR或者SO_REUSEPORT模式 ********************************* tcp        0      0 127.0.0.1:81                127.0.0.1:56850             TIME_WAIT ********************************* tcp        0      0 127.0.

socket编程bind()函数

#include <sys/socket.h> int bind(int socket, const struct sockaddr *address, socklen_t address_len); bind()函数功能 将address指向的sockaddr结构体中描述的一些属性(IP地址.端口号.地址簇)与socket套接字绑定,也叫给套接字命名. 调用bind()后,就为socket套接字关联了一个相应的地址与端口号,即发送到地址值该端口的数据可通过socket读取和使用.当然也可通过

Linux Socket编程-(转自吴秦(Tyler))

"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的.本文的主要内容如下: 1.网络中进程之间如何通信?