LWIP之SOCKET的实现.docx
- 文档编号:11497984
- 上传时间:2023-06-01
- 格式:DOCX
- 页数:57
- 大小:54.74KB
LWIP之SOCKET的实现.docx
《LWIP之SOCKET的实现.docx》由会员分享,可在线阅读,更多相关《LWIP之SOCKET的实现.docx(57页珍藏版)》请在冰点文库上搜索。
LWIP之SOCKET的实现
LWIP之SOCKET的实现
Lwip协议栈的实现目的,无非是要上层用来实现app的socket编程。
好,我们就从socket开始。
为了兼容性,lwip的socket应该也是提供标准的socket接口函数,恩,没错,在src\include\lwip\socket.h文件中可以看到下面的宏定义:
#ifLWIP_COMPAT_SOCKETS
#defineaccept(a,b,c)lwip_accept(a,b,c)
#definebind(a,b,c)lwip_bind(a,b,c)
#defineshutdown(a,b)lwip_shutdown(a,b)
#defineclosesocket(s)lwip_close(s)
#defineconnect(a,b,c)lwip_connect(a,b,c)
#definegetsockname(a,b,c)lwip_getsockname(a,b,c)
#definegetpeername(a,b,c)lwip_getpeername(a,b,c)
#definesetsockopt(a,b,c,d,e)lwip_setsockopt(a,b,c,d,e)
#definegetsockopt(a,b,c,d,e)lwip_getsockopt(a,b,c,d,e)
#definelisten(a,b)lwip_listen(a,b)
#definerecv(a,b,c,d)lwip_recv(a,b,c,d)
#definerecvfrom(a,b,c,d,e,f)lwip_recvfrom(a,b,c,d,e,f)
#definesend(a,b,c,d)lwip_send(a,b,c,d)
#definesendto(a,b,c,d,e,f)lwip_sendto(a,b,c,d,e,f)
#definesocket(a,b,c)lwip_socket(a,b,c)
#defineselect(a,b,c,d,e)lwip_select(a,b,c,d,e)
#defineioctlsocket(a,b,c)lwip_ioctl(a,b,c)
#ifLWIP_POSIX_SOCKETS_IO_NAMES
#defineread(a,b,c)lwip_read(a,b,c)
#definewrite(a,b,c)lwip_write(a,b,c)
#defineclose(s)lwip_close(s)
先不说实际的实现函数,光看这些定义的宏,就是标准socket所必须有的接口。
接着看这些实际的函数实现。
这些函数实现在src\api\socket.c中。
先看下接受连接的函数,这个是tcp的
原型:
intlwip_accept(ints,structsockaddr*addr,socklen_t*addrlen)
可以看到这里的socket类型参数s,实际上是个int型
在这个函数中的第一个函数调用是sock=get_socket(s);
这里的sock变量类型是lwip_socket,定义如下:
/**Containsallinternalpointersandstatesusedforasocket*/
structlwip_socket{
/**socketscurrentlyarebuiltonnetconns,eachsockethasonenetconn*/
structnetconn*conn;
/**datathatwasleftfromthepreviousread*/
structnetbuf*lastdata;
/**offsetinthedatathatwasleftfromthepreviousread*/
u16_tlastoffset;
/**numberoftimesdatawasreceived,setbyevent_callback(),
testedbythereceiveandselectfunctions*/
u16_trcvevent;
/**numberoftimesdatawasreceived,setbyevent_callback(),
testedbyselect*/
u16_tsendevent;
/**socketflags(currently,onlyusedforO_NONBLOCK)*/
u16_tflags;
/**lasterrorthatoccurredonthissocket*/
interr;
};
好,这个结构先不管它,接着看下get_socket函数的实现【也是在src\api\socket.c文件中】,在这里我们看到这样一条语句sock=&sockets[s];很明显,返回值也是这个sock,它是根据传进来的序列号在sockets数组中找到对应的元素并返回该元素的地址。
好了,那么这个sockets数组是在哪里被赋值了这些元素的呢?
进行到这里似乎应该从标准的socket编程的开始,也就是socket函数讲起,那我们就顺便看一下。
它对应的实际实现是下面这个函数
Intlwip_socket(intdomain,inttype,intprotocol)【src\api\socket.c】
这个函数根据不同的协议类型,也就是函数中的type参数,创建了一个netconn结构体的指针,接着就是用这个指针作为参数调用了alloc_socket函数,下面具体看下这个函数的实现
staticintalloc_socket(structnetconn*newconn)
{
inti;
/*Protectsocketarray*/
sys_sem_wait(socksem);
/*allocateanewsocketidentifier*/
for(i=0;i if(! sockets[i].conn){ sockets[i].conn=newconn; sockets[i].lastdata=NULL; sockets[i].lastoffset=0; sockets[i].rcvevent=0; sockets[i].sendevent=1;/*TCPsendbufisempty*/ sockets[i].flags=0; sockets[i].err=0; sys_sem_signal(socksem); returni; } } sys_sem_signal(socksem); return-1; } 对了,就是这个时候对全局变量sockets数组的元素赋值的。 既然都来到这里了,那就顺便看下netconn结构的情况吧。 它的学名叫netconndescriptor /**Anetconndescriptor*/ structnetconn { /**typeofthenetconn(TCP,UDPorRAW)*/ enumnetconn_typetype; /**currentstateofthenetconn*/ enumnetconn_statestate; /**thelwIPinternalprotocolcontrolblock*/ union{ structip_pcb*ip; structtcp_pcb*tcp; structudp_pcb*udp; structraw_pcb*raw; }pcb; /**thelasterrorthisnetconnhad*/ err_terr; /**semthatisusedtosynchroneouslyexecutefunctionsinthecorecontext*/ sys_sem_top_completed; /**mboxwherereceivedpacketsarestoreduntiltheyarefetched bythenetconnapplicationthread(cangrowquitebig)*/ sys_mbox_trecvmbox; /**mboxwherenewconnectionsarestoreduntilprocessed bytheapplicationthread*/ sys_mbox_tacceptmbox; /**onlyusedforsocketlayer*/ intsocket; #ifLWIP_SO_RCVTIMEO /**timeouttowaitfornewdatatobereceived (orconnectionstoarriveforlisteningnetconns)*/ intrecv_timeout; #endif/*LWIP_SO_RCVTIMEO*/ #ifLWIP_SO_RCVBUF /**maximumamountofbytesqueuedinrecvmbox*/ intrecv_bufsize; #endif/*LWIP_SO_RCVBUF*/ u16_trecv_avail; /**TCP: whendatapassedtonetconn_writedoesn'tfitintothesendbuffer, thistemporarilystoresthemessage.*/ structapi_msg_msg*write_msg; /**TCP: whendatapassedtonetconn_writedoesn'tfitintothesendbuffer, thistemporarilystoreshowmuchisalreadysent.*/ intwrite_offset; #ifLWIP_TCPIP_CORE_LOCKING /**TCP: whendatapassedtonetconn_writedoesn'tfitintothesendbuffer, thistemporarilystoreswhethertowakeuptheoriginalapplicationtask ifdatacouldn'tbesentinthefirsttry.*/ u8_twrite_delayed; #endif/*LWIP_TCPIP_CORE_LOCKING*/ /**Acallbackfunctionthatisinformedabouteventsforthisnetconn*/ netconn_callbackcallback; };【src\include\lwip\api.h】 到此,对这个结构都有些什么,做了一个大概的了解。 下面以SOCK_STREAM类型为例,看下netconn的new过程: 在lwip_socket函数中有 caseSOCK_DGRAM: conn=netconn_new_with_callback((protocol==IPPROTO_UDPLITE)? NETCONN_UDPLITE: NETCONN_UDP,event_callback); #definenetconn_new_with_callback(t,c)netconn_new_with_proto_and_callback(t,0,c) 简略实现如下: structnetconn* netconn_new_with_proto_and_callback(enumnetconn_typet,u8_tproto,netconn_callbackcallback) { structnetconn*conn; structapi_msgmsg; conn=netconn_alloc(t,callback); if(conn! =NULL) { msg.function=do_newconn; msg.msg.msg.n.proto=proto; msg.msg.conn=conn; TCPIP_APIMSG(&msg); } returnconn; } 主要就看TCPIP_APIMSG了,这个宏有两个定义,一个是LWIP_TCPIP_CORE_LOCKING的,一个非locking的。 分别分析这两个不同类型的函数 *Callthelowerpartofanetconn_*function *ThisfunctionhasexclusiveaccesstolwIPcorecodebylockingit *beforethefunctioniscalled. err_ttcpip_apimsg_lock(structapi_msg*apimsg)【这个是可以locking的】 { LOCK_TCPIP_CORE(); apimsg->function(&(apimsg->msg)); UNLOCK_TCPIP_CORE(); returnERR_OK; } *Callthelowerpartofanetconn_*function *Thisfunctionisthenrunninginthethreadcontext *oftcpip_threadandhasexclusiveaccesstolwIPcorecode. err_ttcpip_apimsg(structapi_msg*apimsg)【此为非locking的】 { structtcpip_msgmsg; if(mbox! =SYS_MBOX_NULL){ msg.type=TCPIP_MSG_API; msg.msg.apimsg=apimsg; sys_mbox_post(mbox,&msg); sys_arch_sem_wait(apimsg->msg.conn->op_completed,0); returnERR_OK; } returnERR_VAL; } 其实,功能都是一样的,都是要对apimsg->function函数的调用。 只是途径不一样而已。 看看它们的功能说明就知道了。 这么来说apimsg->function的调用很重要了。 从netconn_new_with_proto_and_callback函数的实现,可以知道这个function就是do_newconn Voiddo_newconn(structapi_msg_msg*msg) { if(msg->conn->pcb.tcp==NULL){ pcb_new(msg); } /*Else? This"new"connectionalreadyhasaPCBallocated.*/ /*Isthisanerrorcondition? Shoulditbedeleted? */ /*Wecurrentlyjustarehappyandreturn.*/ TCPIP_APIMSG_ACK(msg); } 还是看TCP的,在pcb_new函数中有如下代码: caseNETCONN_TCP: msg->conn->pcb.tcp=tcp_new(); if(msg->conn->pcb.tcp==NULL){ msg->conn->err=ERR_MEM; break; } setup_tcp(msg->conn); break; 我们知道在这里建立了这个tcp的连接。 至于这个超级牛的函数,以后再做介绍。 嗯,还是回过头来接着看accept函数吧。 Sock获得了,接着就是newconn=netconn_accept(sock->conn);通过mbox取得新的连接。 粗略的估计了一下,这个新的连接应该和listen有关系。 那就再次打断一下,看看那个listen操作。 lwip_listen--netconn_listen_with_backlog--do_listen-- tcp_arg(msg->conn->pcb.tcp,msg->conn); tcp_accept(msg->conn->pcb.tcp,accept_function);//注册了一个接受函数 *AcceptcallbackfunctionforTCPnetconns. *Allocatesanewnetconnandpoststhattoconn->acceptmbox. staticerr_taccept_function(void*arg,structtcp_pcb*newpcb,err_terr) { structnetconn*newconn; structnetconn*conn; conn=(structnetconn*)arg; /*Wehavetosetthecallbackhereeventhough *thenewsocketisunknown.conn->socketismarkedas-1.*/ newconn=netconn_alloc(conn->type,conn->callback); if(newconn==NULL){ returnERR_MEM; } newconn->pcb.tcp=newpcb; setup_tcp(newconn); newconn->err=err; /*Registereventwithcallback*/ API_EVENT(conn,NETCONN_EVT_RCVPLUS,0); if(sys_mbox_trypost(conn->acceptmbox,newconn)! =ERR_OK) { /*Whenreturning! =ERR_OK,theconnectionisabortedintcp_process(), sodonothinghere! */ newconn->pcb.tcp=NULL; netconn_free(newconn); returnERR_MEM; } returnERR_OK; } 对了,accept函数中从mbox中获取的连接就是这里放进去的。 再回到accept中来,取得了新的连接,接下来就是分配sock了,再然后,再然后? 再然后就等用户来使用接收、发送数据了。 到此整个APP层,也就是传输层以上对socket的封装讲完了。 在最后再总结一些整个路径的调用情况吧 LWIP之API_MSG结构及其实现 从上面一篇的socket实现来看,如果要评起到最关键作用的一个结构体,那么structapi_msg当之无愧。 先看下它的定义: /**Thisstructcontainsafunctiontoexecuteinanotherthreadcontextand astructapi_msg_msgthatservesasanargumentforthisfunction. Thisispassedtotcpip_apimsgtoexecutefunctionsintcpip_threadcontext.*/ structapi_msg { /**functiontoexecuteintcpip_threadcontext*/ void(*function)(structapi_msg_msg*msg); /**argumentsforthisfunction*/ structapi_msg_msgmsg; }; 功能说的很清楚。 但是具体怎么个操作法还是不知道,没关系,接着看它的调用。 举一个例子,刚好是上一篇中调用,但是没有看具体实现的 err_tnetconn_getaddr(structnetconn*conn,structip_addr*addr,u16_t*port,u8_tlocal) { structapi_msgmsg; msg.function=do_getaddr; msg.msg.conn=conn; msg.msg.msg.ad.ipaddr=addr; msg.msg.msg.ad.port=port; msg.msg.msg.ad.local=local; TCPIP_APIMSG(&msg); returnconn->err; } 说明一下,api_msg结构几乎都是在netconn_xxx函数中被调用,方式千篇一律,除了msg.funcion的赋值不一样外。 上面的调用很简单,对该结构体变量赋值,接着就是调用TCPIP_APIMSG,这个函数上面讲过,可过去看下。 既然如此,就不得不说mbox及其相关函数了。 staticsys_mbox_tmbox=SYS_MBOX_NULL;【tcp.c】 再看sys_mbox_t的定义,在【src\include\lwip\sys.h】中 /*Foratotallyminimalandstandalonesystem,weprovidenull definitionsofthesys_functions.*/ typedefu8_tsys_sem_t; typedefu8_tsys_mbox_t; typedef
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- LWIP SOCKET 实现