此文件内的函数基本上分成两类。一类的输入参数中包含TChannel结构体变量,另一类的输入参数中包含另一个结构体变量TChanSwitch。初看后觉得二者的差别很小,细看后发现差异在vtbl属性。vtbl这个结构体属性有很多函数指针属性。类似于监听函数(listen)、接受函数(accept)、读函数(read)和写函数(write)等等。TChannel包含读写等函数指针。TChanSwitch包含监听和接受函数指针。因此可以断定TChannel是用来代表一个连接上服务端的socket连接,TChanSwitch用来代表一个服务端socket。即,本文档内的函数分成两类:操作连接上服务端socket的函数,和操作服务端socket的函数。
- struct TChannelVtbl {
- ChannelDestroyImpl * destroy;
- ChannelWriteImpl * write;
- ChannelReadImpl * read;
- ChannelWaitImpl * wait;
- ChannelInterruptImpl * interrupt;
- ChannelFormatPeerInfoImpl * formatPeerInfo;
- };
- struct _TChannel {
- uint signature;
- /* With both background and foreground use of sockets,and
- background being both fork and pthread,it is very easy to
- screw up socket lifetime and try to destroy twice. We use
- this signature to help catch such bugs.
- */
- void * implP;
- struct TChannelVtbl vtbl;
- };
- struct TChanSwitchVtbl {
- SwitchDestroyImpl * destroy;
- SwitchListenImpl * listen;
- SwitchAcceptImpl * accept;
- SwitchInterruptImpl * interrupt;
- };
- struct _TChanSwitch {
- uint signature;
- /* With both background and foreground use of switches,it is very easy to
- screw up switch lifetime and try to destroy twice. We use
- this signature to help catch such bugs.
- */
- void * implP;
- struct TChanSwitchVtbl vtbl;
- };
@H_502_3@TChannel
channelVtbl是个静态变量,保存了本文档内所有操作TChannel结构体的函数指针。
- static struct TChannelVtbl const channelVtbl = {
- &channelDestroy,&channelWrite,&channelRead,&channelWait,&channelInterrupt,&channelFormatPeerInfo,};
TChannel结构体内的implP属性是个无类型指针。意味着它可以指向任何类型。操作TChannel结构体的函数内都可以看到如下这句,将implP强制转换成socketWin结构体变量指针。
- struct socketWin * const socketWinP = channelP->implP;
- static void
- channelDestroy(TChannel * const channelP) {
- struct socketWin * const socketWinP = channelP->implP;
- if (!socketWinP->userSuppliedWinsock)
- closesocket(socketWinP->winsock);
- CloseHandle(socketWinP->interruptEvent);
- free(socketWinP);
- }
注意closesocket前的if判断语句。是否调用closesocket将依据userSuppliedWinsock这个布尔变量。变量名直译过来就是“用户提供Winsock”,再加入这个判断语句就是:如果不是用户提供Winsock,则调用closesocket。这样一来也好理解,如果socket句柄是由abyss库外部提供,即这里提到的“用户”,那么关闭socket句柄理应由库本身来做。
这个函数将依据传入的SOCKET值创建一个TChannel变量。此文档名为socket_win.c,即它是abyss库在windows平台下的特殊实现。之前也提到过TChannel还有个指向无类型的指针。在windows平台下,这个无类型指针将指向socketWin结构体。因此,这个函数还会为TChannel创建创建一个socketWin变量并赋给TChannel。在此函数内又看到了userSuppliedWinsock,在这里可以看到此属性恒为TRUE。即,SOCKET值都是由外部创建提供的。
- static void
- makeChannelFromWinsock(SOCKET const winsock,TChannel ** const channelPP,const char ** const errorP) {
- struct socketWin * socketWinP;
- MALLOCVAR(socketWinP);
- if (socketWinP == NULL)
- xmlrpc_asprintf(errorP,"Unable to allocate memory for Windows "
- "socket descriptor");
- else {
- TChannel * channelP;
- socketWinP->winsock = winsock;
- socketWinP->userSuppliedWinsock = TRUE;
- socketWinP->interruptEvent = CreateEvent(NULL,FALSE,NULL);
- ChannelCreate(&channelVtbl,socketWinP,&channelP);
- if (channelP == NULL)
- xmlrpc_asprintf(errorP,"Unable to allocate memory for "
- "channel descriptor.");
- else {
- *channelPP = channelP;
- *errorP = NULL;
- }
- if (*errorP) {
- CloseHandle(socketWinP->interruptEvent);
- free(socketWinP);
- }
- }
- }
函数内还调用了另一个文件内的函数ChannelCreate,此函数在channel.c文件内。这个函数很简单,就是申请内容创建一个TChannel变量,然后将静态变量channelVtbl和socketWinP变量赋给TChannel变量。如果一切正常,创建好后的TChannel内将包含一个socketWin变量,vtbl结构体属性内的函数指针将指向本文档内的各个静态函数。
makeChannelInfo函数只是创建了一个abyss_win_chaninfo变量。
- static void
- makeChannelInfo(struct abyss_win_chaninfo ** const channelInfoPP,struct sockaddr const peerAddr,socklen_t const peerAddrLen,const char ** const errorP) {
- struct abyss_win_chaninfo * channelInfoP;
- MALLOCVAR(channelInfoP);
- if (channelInfoP == NULL)
- xmlrpc_asprintf(errorP,"Unable to allocate memory");
- else {
- channelInfoP->peerAddrLen = peerAddrLen;
- channelInfoP->peerAddr = peerAddr;
- *channelInfoPP = channelInfoP;
- *errorP = NULL;
- }
- }
ChannelWinCreateWinsock函数包含了上述两个函数。此函数的输入参数其实就是一个SOCKET值。可以理解为外部生成了一个SOCKET句柄后,此函数依据生成好的SOCKET句柄,再创建两个结构体变量。一个是TChannel,另一个是abyss_win_chaninfo。这个函数将依据SOCKET句柄得到的sockaddr变量放置在abyss_win_chaninfo结构体变量内。SOCKET句柄保存在TChannel结构体变量内,且还保存了被封装后的windows平台下的socket函数指针。
- void
- ChannelWinCreateWinsock(SOCKET const fd,TChannel ** const channelPP,struct abyss_win_chaninfo ** const channelInfoPP,const char ** const errorP) {
- struct sockaddr peerAddr;
- socklen_t peerAddrLen;
- int rc;
- peerAddrLen = sizeof(peerAddr);
- rc = getpeername(fd,&peerAddr,&peerAddrLen);
- if (rc != 0) {
- int const lastError = WSAGetLastError();
- if (lastError == WSAENOTCONN) {
- /* NOTE: This specific string 'not in connected' is
- required by one of the rpctest suite items,in abyss.c
- (line 186),hence the separation of the error messages
- in this case ...
- */
- xmlrpc_asprintf(errorP,"Socket on file descriptor %d "
- "is not in connected state. WSAERROR = %d (%s)",fd,lastError,getWSAError(lastError));
- } else
- xmlrpc_asprintf(errorP,"getpeername() Failed. WSAERROR = %d (%s)",getWSAError(lastError));
- } else {
- makeChannelInfo(channelInfoPP,peerAddr,peerAddrLen,errorP);
- if (!*errorP) {
- makeChannelFromWinsock(fd,channelPP,errorP);
- if (*errorP)
- free(*channelInfoPP);
- }
- }
- }
createChannelForAccept函数的作用感觉和ChannelWinCreateWinsock函数一样。只不过函数签名不太一样,增加了一个输入参数。
- static void
- createChannelForAccept(int const acceptedWinsock,struct sockaddr const peerAddr,TChannel ** const channelPP,void ** const channelInfoPP,const char ** const errorP) {
- struct abyss_win_chaninfo * channelInfoP;
- makeChannelInfo(&channelInfoP,sizeof(peerAddr),errorP);
- if (!*errorP) {
- struct socketWin * acceptedSocketP;
- MALLOCVAR(acceptedSocketP);
- if (!acceptedSocketP)
- xmlrpc_asprintf(errorP,"Unable to allocate memory");
- else {
- TChannel * channelP;
- acceptedSocketP->winsock = acceptedWinsock;
- acceptedSocketP->userSuppliedWinsock = FALSE;
- acceptedSocketP->interruptEvent =
- CreateEvent(NULL,NULL);
- ChannelCreate(&channelVtbl,acceptedSocketP,&channelP);
- if (!channelP)
- xmlrpc_asprintf(errorP,"Failed to create TChannel object.");
- else {
- *errorP = NULL;
- *channelPP = channelP;
- *channelInfoPP = channelInfoP;
- }
- if (*errorP) {
- CloseHandle(acceptedSocketP->interruptEvent);
- free(acceptedSocketP);
- }
- }
- }
- }
ChanSwitchWinCreate和ChanSwitchWinCreateWinsock两个函数的作用相同,都是为了创建一个TChanSwitch变量。只是二者的函数签名不一样,前一个的输入参数是端口号,后一个的输入参数是SOCKET句柄。在这两个函数内也能看到,chanSwitchVtbl全局静态变量赋给了TChanSwitch变量的vtbl属性。这个手法和TChannel结构体一致。
之前一直提到的socketWin结构体是在文件的最开始部分声明的。此结构体保存SOCKET句柄、一个布尔型变量(指示SOCKET句柄由谁创建)以及一个windows的句柄。windows句柄在创建每个socketWin结构体变量时生成,现在还不清楚它的用途。
SocketWinInit和SocketWinTerm分别是winsock初始化和卸载函数。connected函数用来判断一个SOCKET句柄是否处于连接状态。getWSAError函数返回错误代码对应的错误字串。
- struct socketWin {
- /*----------------------------------------------------------------------------
- The properties/state of a TSocket unique to a Unix TSocket.
- -----------------------------------------------------------------------------*/
- SOCKET winsock;
- bool userSuppliedWinsock;
- /* 'socket' was supplied by the user; it belongs to him */
- HANDLE interruptEvent;
- };