[TCP][四] lwip_listen(a,b)

说到 Server 端的 listen 操作,就有意思了。☄☺


目录

函数原型

函数解析

总结


listen 是 Server 才有的动作,主要用来监听 Client 发起的连接请求,并为该连接新建 socket。为什么还要为每个连接都新建一个 socket 呢?原来,Server 端使用一个 socket 专门用来监听,每监听到一个 Client 连接,就用新建的 socket 与 Client 通信。监听 socket 只负责监听,一直持续有效。

 

函数原型

int lwip_listen(int s, int backlog)

参数:

s,即用来监听的 socket fd

backlog,表示能监听的连接(SYN)个数上限。在监听过程中收到 Client SYN 后就开始建立连接,该值表示监听阶段缓存的正在建立的连接 和 已经建立但还没有被应用层 accept 的连接 的个数上限,达到上限后新收到的 SYN 便不再处理,默认最大值 255。当已建立的连接被应用层 accept 后,便可以腾出缓存空间接收新的连接请求。

 

函数解析

注意,这里的 s 是 Server 用来监听的 socket fd,为避免混淆,我们暂且写成 Server_L_fd。

这个函数主要干了以下几件事情:

1. 新建一个如下类型的 Server_L_tcp_pcb

struct tcp_pcb_listen {  
    IP_PCB;
    TCP_PCB_COMMON(struct tcp_pcb_listen);

    #if TCP_LISTEN_BACKLOG
    u8_t backlog;
    u8_t accepts_pending;
    #endif
};

这个结构是比 struct tcp_pcb 小得多,为了节省内存只保留监听需要的东西。

还记得内存池 mempool 吗,这里的 “新建” 就是从中拿一个 struct tcp_pcb_listen 出来。

 

2. 原 tcp_pcb

从原 tcp_pcb 中拷贝信息到 Server_L_tcp_pcb,再把原 tcp_pcb 释放。

原有 tcp_pcb 结构包含所有用于 tcp 通信的参数,结构较大而监听时用不到。所以将其中能用到的拷贝到 Server_L_tcp_pcb 中,并将原有 tcp_pcb 从 tcp_bound_pcbs 中拿出来释放掉(还到 mempool 中)。

拷贝的变量主要有:

lpcb->local_port = pcb->local_port;
lpcb->state = LISTEN;
lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
ip_addr_copy(lpcb->local_ip, pcb->local_ip);

看到没,local_ip / local_port 都没变哦。

Server_L_tcp_pcb 的状态是 LISTEN。

 

3. 将 Server_L_tcp_pcb 插到 tcp_listen_pcbs 的头部。

4. 原 netconn

释放掉原 netconn 中的 recvmbox,监听时用不着。

申请 acceptmbox,默认大小 6,表示最多有 6 个已完成握手的连接等待应用层 accept !注意与 backlog 的区别。

原 netconn 状态变成 NETCONN_LISTEN,并将成员 pcb.tcp 指向 Server_L_tcp_pcb(原 tcp_pcb 已释放)。

 

总结

lwip_listen() 把 Server 端用于监听的前期工作完成后,Server 就坐等 Client 发起连接握手。后面我们分析,收到 Client SYN 后,三次握手的过程 Server 内部都发生了什么。

你可能感兴趣的:(Lwip探秘)