LWIP之SOCKET的实现文档格式.docx
《LWIP之SOCKET的实现文档格式.docx》由会员分享,可在线阅读,更多相关《LWIP之SOCKET的实现文档格式.docx(56页珍藏版)》请在冰点文库上搜索。
这些函数实如今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;
testedbyselect*/
u16_tsendevent;
/**socketflags(currently,onlyusedforO_NONBLOCK)*/
u16_tflags;
/**lasterrorthatoccurredonthissocket*/
interr;
};
好,这个构造先不管它,接着看下get_socket函数的实现【也是在文件中】,在这里我们看到这样一条语句sock=&
sockets[s];
很明显,返回值也是这个sock,它是根据传进来的序列号在sockets数组中找到对应的元素并返回该元素的地址。
好了,那么这个sockets数组是在哪里被赋值了这些元素的呢?
进展到这里似乎应该从标准的socket编程的开场,也就是socket函数讲起,那我们就顺便看一下。
它对应的实际实现是下面这个函数
Intlwip_socket(intdomain,inttype,intprotocol)
【】
这个函数根据不同的协议类型,也就是函数中的type参数,创立了一个netconn构造体的指针,接着就是用这个指针作为参数调用了alloc_socket函数,下面详细看下这个函数的实现
staticintalloc_socket(structnetconn*newconn)
{
inti;
/*Protectsocketarray*/
sys_sem_wait(socksem);
/*allocateanewsocketidentifier*/
for(i=0;
i<
NUM_SOCKETS;
++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;
}
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;
thistemporarilystoreshowmuchisalreadysent.*/
intwrite_offset;
#ifLWIP_TCPIP_CORE_LOCKING
/**TCP:
thistemporarilystoreswhethertowakeuptheoriginalapplicationtask
ifdatacouldn'
tbesentinthefirsttry.*/
u8_twrite_delayed;
#endif/*LWIP_TCPIP_CORE_LOCKING*/
/**Acallbackfunctionthatisinformedabouteventsforthisnetconn*/
netconn_callbackcallback;
到此,对这个构造都有些什么,做了一个大概的理解。
下面以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)
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;
*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,&
sys_arch_sem_wait(apimsg->
msg.conn->
op_completed,0);
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->
pcb.tcp=tcp_new();
err=ERR_MEM;
break;
setup_tcp(msg->
conn);
我们知道在这里建立了这个tcp的连接。
至于这个超级牛的函数,以后再做介绍。
嗯,还是回过头来接着看accept函数吧。
Sock获得了,接着就是newconn=netconn_accept(sock->
通过mbox获得新的连接。
粗略的估计了一下,这个新的连接应该和listen有关系。
那就再次打断一下,看看那个listen操作。
lwip_listen--→netconn_listen_with_backlog--→do_listen--→
tcp_arg(msg->
pcb.tcp,msg->
tcp_accept(msg->
pcb.tcp,accept_function);
//注册了一个承受函数
*AcceptcallbackfunctionforTCPnetconns.
*Allocatesanewnetconnandpoststhattoconn->
acceptmbox.
staticerr_taccept_function(void*arg,structtcp_pcb*newpcb,err_terr)
structnetconn*newconn;
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);
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!
pcb.tcp=NULL;
netconn_free(newconn);
对了,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)
msg.function=do_getaddr;
r=addr;
msg.msg.msg.ad.port=port;
msg.msg.msg.ad.local=local;
TCPIP_APIMSG(&
returnconn->
err;
说明一下,api_msg构造几乎都是在netconn_xxx函数中被调用,方式千篇一律,除了的赋值不一样外。
上面的调用很简单,对该构造体变量赋值,接着就是调用TCPIP_APIMSG,这个函数上面讲过,可过去看下。
既然如此,就不得不说mbox及其相关函数了。
staticsys_mbox_tmbox=SYS_MBOX_NULL;
再看sys_mbox_t的定义,在【】中
/*Foratotallyminimalandstandalonesystem,weprovidenull
definitionsofthesys_functions.*/
typedefu8_tsys_sem_t;
typedefu8_tsys_mbox_t;
typedefu8_tsys_prot_t;
可以看到这里只是简单的定义成了u8类型,注意上面的红色字体的说明,很明显这个是可移植的一局部,需要根据不同的平台,不同的操作系统详细定义。
可以借鉴焦海波大侠的关于ucos上对lwip的移植笔记来看。
我们可以看到在api_msg构造的处理过程中,所有的信息都是包含在api_msg_msg构造体中的,api_msg只是将其和function简单的组合了。
下面看下