LWIP之三TCP层发送相关.docx_第1页
LWIP之三TCP层发送相关.docx_第2页
LWIP之三TCP层发送相关.docx_第3页
LWIP之三TCP层发送相关.docx_第4页
LWIP之三TCP层发送相关.docx_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

LWIP之TCP层发送相关 2009-05-16 00:42:09标签: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。/214870/158415 2009-5-11 LWIP之TCP层发送相关现在我们正式开始进入对TCP的研究,它属于传输层协议,它为应用程序提供了可靠的字节流服务。在LWIP中基本的TCP处理过程被分割为六个功能函数的实现:tcp_input(), tcp_process(), tcp_receive()【与TCP输入有关】, tcp_write(), tcp_enqueue(), tcp_output()【用于TCP输出】。这些是从大的方面来划分的。现在先从小部tcp.c文件来分析一下:我们知道这里的函数都是被socket那一层的最终调用的。为了利于分析,我选择lwip_send函数来分析,具体不多说,最终调用到了static err_t do_writemore(struct netconn *conn)这个函数,当然这期间也做了不少工作,最主要的就是把发送数据的指针放到了msg的指定变量中msg.msg.msg.w.dataptr = dataptr;/指针msg.msg.msg.w.len = size; /长度这些又经过转化放到了netconn的write_msg中最后就是对do_writemore的调用了,下面详细分析这个函数。这个函数的最直接调用有以下几个:available = tcp_sndbuf(conn-pcb.tcp);err = tcp_write(conn-pcb.tcp, dataptr, len, conn-write_msg-msg.w.apiflags);err = tcp_output_nagle(conn-pcb.tcp);err = tcp_output(conn-pcb.tcp);好,先看tcp_sndbuf这个。从其参数,我们应该想像pcb的重要性。#define tcp_sndbuf(pcb) (pcb)-snd_buf)/由下面分析得这里直接返回buf大小那就继续跟踪这个pcb好了。是的,这还得从lwip_socket开始,在创建连接netconn的时候调用了do_newconn,它接着调用了pcb_new,在这个函数中如果是tcp的话有以下代码msg-conn-pcb.tcp = tcp_new();哈哈,还记得吧,在前面我讨论到了这里,就没有再讨论了。嗯,现在开始吧。* Creates a new TCP protocol control block but doesnt place it on* any of the TCP PCB lists.* The pcb is not put on any list until binding using tcp_bind().struct tcp_pcb * tcp_new(void) return tcp_alloc(TCP_PRIO_NORMAL);也许注释的意义更大一些吧,哈哈,至此,我们从注释可以知道,tcp_bind的作用是把tcp的pcb放入list中,至少是某一个list既然都提到了,似乎不说说有点过意不去啊。Lwid_bind函数最终会调用到tcp_bind【当然是tcp为例进行分析】。这个函数也比较有意思,在进入正题之前先来了下面这么个调用if (port = 0) port = tcp_new_port();意思很明显,就是分配个新的端口号,有意思的就是这个端口号的分配函数tcp_new_portstatic u16_ttcp_new_port(void) struct tcp_pcb *pcb;#ifndef TCP_LOCAL_PORT_RANGE_START#define TCP_LOCAL_PORT_RANGE_START 4096#define TCP_LOCAL_PORT_RANGE_END 0x7fff#endif static u16_t port = TCP_LOCAL_PORT_RANGE_START; again: if (+port TCP_LOCAL_PORT_RANGE_END) port = TCP_LOCAL_PORT_RANGE_START; for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb-next) if (pcb-local_port = port) goto again; for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb-next) if (pcb-local_port = port) goto again; for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb-next) if (pcb-local_port = port) goto again; return port;分别检查了3个pcb链表,本来我不想把这个函数的实现列在这里的,但是这里告诉了我们一些东西,至少我们知道有3个pcb的链表,分别是tcp_active_pcbs【处于接受发送数据状态的pcbs】、tcp_tw_pcbs【处于时间等待状态的pcbs】、tcp_listen_pcbs【处于监听状态的pcbs】。似乎tcp_bind函数更有意思,它分别检查了4个pcbs链表,除了上面的三个还有一个tcp_bound_pcbs【处于已经绑定但还没有连接或者监听pcbs】。作用是是否有与目前pcb的ipaddr相同的pcb存在。不过这些都不是最重要的,TCP_REG(&tcp_bound_pcbs, pcb);才是我们的目的,果然如此,直接加入到了tcp_bound_pcbs链表中了。struct tcp_pcb * tcp_alloc(u8_t prio)/是的,你没看错,这个参数是学名是叫优先级pcb = memp_malloc(MEMP_TCP_PCB);if (pcb != NULL) memset(pcb, 0, sizeof(struct tcp_pcb); pcb-prio = TCP_PRIO_NORMAL; pcb-snd_buf = TCP_SND_BUF;/对的,别的可以先不管,这就是我们要找的东西 pcb-snd_queuelen = 0; pcb-rcv_wnd = TCP_WND; pcb-rcv_ann_wnd = TCP_WND; pcb-tos = 0; pcb-ttl = TCP_TTL; /* The send MSS is updated when an MSS option is received. */ pcb-mss = (TCP_MSS 536) ? 536 : TCP_MSS; pcb-rto = 3000 / TCP_SLOW_INTERVAL; pcb-sa = 0; pcb-sv = 3000 / TCP_SLOW_INTERVAL; pcb-rtime = -1; pcb-cwnd = 1; iss = tcp_next_iss(); pcb-snd_wl2 = iss; pcb-snd_nxt = iss; pcb-snd_max = iss; pcb-lastack = iss; pcb-snd_lbb = iss; pcb-tmr = tcp_ticks; pcb-polltmr = 0;#if LWIP_CALLBACK_API pcb-recv = tcp_recv_null;#endif /* LWIP_CALLBACK_API */ /* Init KEEPALIVE timer */ pcb-keep_idle = TCP_KEEPIDLE_DEFAULT; #if LWIP_TCP_KEEPALIVE pcb-keep_intvl = TCP_KEEPINTVL_DEFAULT; pcb-keep_cnt = TCP_KEEPCNT_DEFAULT;#endif /* LWIP_TCP_KEEPALIVE */ pcb-keep_cnt_sent = 0; return pcb;就是一个tcp_pcb的结构体初始化过程,使用默认值填充该结构好了,下面接着走,该轮到tcp_write了吧* Write data for sending (but does not send it immediately). * It waits in the expectation of more data being sent soon (as * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write().【srccoretcp_out.c】err_t tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) /* connection is in valid state for data transmission? */ if (pcb-state = ESTABLISHED | pcb-state = CLOSE_WAIT | pcb-state = SYN_SENT | pcb-state = SYN_RCVD) if (len 0) return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, NULL, 0); return ERR_OK; else return ERR_CONN; 这个函数确实够简单的了,检查pcb状态,直接入队列。嗯,还是觉得看注释比看函数实现过瘾。下面的这个tcp_enqueue才是个大头函数,lwip协议栈的设计与实现文档中是这么介绍的:应用层调用tcp_write()函数以实现发送数据,接着tcp_write()函数再将控制权交给tcp_enqueue(),这个函数会在必要时将数据分割为适当大小的TCP段,然后再把这些TCP段放到所属连接的传输队列中【pcb-unsent】。函数的注释是这样写的:* Enqueue either data or TCP options (but not both) for tranmission * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write().咱们接着上面往下看,tcp_output_nagle这个函数实际上是个宏,通过预判断以决定是否调用tcp_output。也就是说最后的执行是tcp_output来实现的。这个函数或许比tcp_enqueue还要恐怖,从体积上来看。这里也只做一个简单介绍,具体请参阅tcp_out.c文档。该函数的注释是这么说

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论