分类: C/C++
2008-04-23 21:57:52
论软件接口中几种底层通讯的实现
作者:
一、 概述
软件接口是实现一个系统跟另外系统进行信息交互的桥梁,在不同的系统之间,根据系统的关联程度的不同存在紧耦合和松耦合两种:紧耦合要求接口响应反应快,消息不能阻塞;松耦合对响应反应要求比较低。本人主要讨论紧耦合接口通讯实现,在目前应用中,Socket、中间件、SOAP等都用相应的应用,但是应用中发现各通讯方式有自己固有的特征,"适合的才是最好的",这是真理。
在接口和系统信息交互的过程中,两种模式使用得很普遍:同步调用和异步调用,同步调用要求接口发出请求消息后必须等待服务端系统的应答消息,接口阻塞直至超时;异步调用则发出请求消息后,接口可以从事其它处理,定时轮询服务端应答消息和消息或事件通知。同步方式简单,但是很容易造成接口阻塞,造成消息积压超时。
二、 技术实现
1、 Socket通讯
Socket通讯相对来说是很古老的通讯方式,也是最常用的通讯方式。Socket通讯有阻塞和非阻塞两种方式。在同步方式,采用阻塞编程比较简单,但是为了防止接口阻塞,我们需要设置Socket超时,因此可以使用Socket的SELECT模型(参考如下示例代码):
CurReceLen=0;
for(;;)
{
iResult=select(0,&fdread,NULL,NULL,&timeout);
if(iResult==0)
{
AfxMessageBox("接收应答消息超时!!!",MB_OK|MB_ICONERROR);
closesocket(Socket);
return FALSE;
}
CurReceLen = recv(Socket, oBuf ReceLen, len, NO_FLAG_SET);
if((CurReceLen>0) && (CurReceLen != SOCKET_ERROR))
{
oBuf[ReceLen CurReceLen]=''\0'';
memcpy((char *)&MsgLen,oBuf,sizeof(WORD32));
MsgLen=ntohl(MsgLen);
if(ReceLen CurReceLen==MsgLen)
{
ReceLen =CurReceLen;
break;
}
ReceLen =CurReceLen;
}
}
在异步方式下,采用非阻塞方式实现比较方便,在非阻塞方式下可使用WSAAsyncSelect模型和WSAEventSelect模型:WSAAsyncSelect模型基于消息,WSAEventSelect模型基于事件,下面的示例代码设置了Socket进行读写和关闭操作的消息:
if (status == SOCKET_ERROR)
{
WriteLogFile("Set stream socket module fail!!!IP(%s),
Port(%d) and error(%d)",
GetIPAddr((PeerMap node)->IPAddr),
(PeerMap node)->PeerPortNo,
WSAGetLastError());
CloseSocket(TempSocket,__LINE__,__FILE__);
return FALSE;
}
无论使用阻塞方式或非阻塞方式编程,需要重点考虑的一个问题:粘包现象,即应用发送两个或以上的数据包,在Socket通讯层将数据包合并成一个发送出去,因此接收端收到数据包以后需要对数据包根据应用定义的长度进行拆分,否则导致应用层丢包。
int tpcallex(char *svc, char *idata, long ilen, char **odata, long *olen, long flags, long timeout)
{
const int err_invoke_result = -1;
int iHandler=0;
int iResult=0;
int iTimeOut=timeout;
iHandler = tpacall((char *)svc, (char *)idata, ilen, (long)TPNOBLOCK);
if(iHandler == err_invoke_result)
{
return iHandler;
}
while(iTimeOut>0)
{
iResult = tpgetrply(&iHandler, (char **)odata, olen, (long)TPNOBLOCK);
if(iResult == err_invoke_result)
{
Sleep(10);
iTimeOut -= 10;
continue;
}
break;
}
if(iTimeOut<=0)
{
tpcancel(iHandler);
return err_invoke_result;
}
return iHandler;
}
如果要增加接口的处理能力,使用多线程方式会存在隐患,最好的方式是采用多进程,不过存在如何消息均衡的问题。
if(!m_bFlatType)
{
for(i=paramNum,j=0;i>j;i--,j )
{
VARIANTARG argTemp;
VariantInit(&argTemp);
argTemp=va[i-1];
va[i-1]=va[j];
va[j]=argTemp;
}
}
params.cArgs = paramNum;
params.rgvarg = va;
params.cNamedArgs = 0;
params.rgdispidNamedArgs = NULL;
hr = SoapConnect.pSoapClient[index]->Invoke(dispidFn,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
¶ms,
&result, 0, 0);
if(FAILED(hr))
{
HandleHResult(_T("Invoke of " strService " method failed."), hr);
VariantClear(&result);
for(i=0;i
三、总结
在三种通讯方式中,各有优缺点,但是主要还在于服务端采用什么技术方案来实现,接口必须对应采用相应的通讯模式。
除了上面的通讯模式,当然还有很多其它的方式,如管道、消息队列等,目前我在紧耦合的接口中使用得不多。