您的位置:首页 > 其它

服务与用户界面之间的通信设计

2012-12-06 12:32 190 查看
服务与用户界面之间的通信分为两个部分,一是用户界面对服务的操作;二是服务不定时的向界面发送状态或者动作通知。

通常服务与界面之间的通信可以采用的方案有socket(tcp/udp)和命名管道两种方式,socket方案的优势是用户界面可以很容易的在另外一台计算机上操作远程计算机上的服务;而命名管道通常只能管理本机上的服务(最多可以在局域网上管理其它计算机而且需要配置)。

我们的选择是使用socket方案,同时采用TCP方式,因为相对UDP来说,TCP更加给人以可靠的感觉,除了需要处理“流”的分包,TCP相对UDP更加可靠一点。

然后为了处理命令和状态两种不同性质的通信,我们设计了两种socket,分别为CmdSocket和MsgSocket。

用户界面初始化时,创建MsgSocket,并长期与服务保持连接,之后服务的状态变化,都通过这个MsgSocket发送给用户界面,界面再将这些数据显示出来。这里MsgSocket会需要一个额外的线程。

用户的任何操作,需要向服务发送命令并等待回应,这时使用CmdSocket。CmdSocket使用短连接,发送数据并等待回应,然后返回。

采用这样的方式的好处是系统整体比较清晰。以下是我们在产品中使用的代码(通信的内容,是用的mini-xml):

View Code

// 创建网络连接
SOCKET CmdSocket_CreateAndConnect(const CHAR *ip, WORD port);
// 网络发送
BOOL CmdSocket_Send(SOCKET cmdSock, const CHAR *data, int dataLen);
// 网络接收
BOOL CmdSocket_Receive(SOCKET cmdSock, CHAR **recvBuf, int *recvLen);
// 关闭连接
void CmdSocket_Close(SOCKET cmdSock);

// -----------------------------------------------------------------------------------------
// 异步消息接收线程
// 服务器会将一些通知信息通过此接口发送给客户端,格式为XML
// -----------------------------------------------------------------------------------------

// 回调函数
typedef void (CALLBACK MSGSOCKET_CB)(CHAR *msgData, int msgLen);

// 开始线程
BOOL BeginMsgSocketThread(MSGSOCKET_CB *msgSockCB,
const CHAR *svrIp, WORD svrPort,
const CHAR *svrPwd);
// 结束线程
void StopMsgSocketThread();

static MSGSOCKET_CB *g_msgSockCB = NULL;
static SOCKET g_msgSock = INVALID_SOCKET;
static WSAEVENT g_msgSockEvent = NULL;
static WSAEVENT g_msgSockEventStop = NULL;
static WSAEVENT g_msgSockEventStopped = NULL;
static time_t g_msgSockNextConnectTime = 0;
static time_t g_msgSockLastReceiveTime = 0;
static CHAR *g_msgSockRecvBuf = NULL;
static int g_msgSockRecvSize = 0;
static int g_msgSockRecvLen = 0;
static CHAR g_msgSvrIp[32] = { 0 };
static WORD g_msgSvrPort = 0;
static CHAR g_msgSvrPwd[32] = { 0 };

__inline static BOOL Socket_Send(SOCKET cmdSock, const CHAR *data, int dataLen)
{
int sent, totalSent = 0;

while (totalSent < dataLen)
{
sent = send(cmdSock, data+totalSent, dataLen-totalSent, 0);
if (sent == SOCKET_ERROR) return FALSE;
totalSent += sent;
}
return TRUE;
}

static void MsgSocket_Close()
{
if (g_msgSock != INVALID_SOCKET)
{
closesocket(g_msgSock);
g_msgSock = INVALID_SOCKET;
}
}

static void MsgSocket_Connect()
{
struct sockaddr_in addr;
time_t currTime;

time(&currTime);

if (currTime < g_msgSockNextConnectTime) return;
g_msgSockNextConnectTime = currTime + 5;

g_msgSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (g_msgSock == INVALID_SOCKET) return;

ResetEvent(g_msgSockEvent);
WSAEventSelect(g_msgSock, g_msgSockEvent, FD_CLOSE|FD_CONNECT|FD_READ);

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(g_msgSvrIp);
addr.sin_port = htons(g_msgSvrPort);
connect(g_msgSock, (struct sockaddr *)&addr, sizeof(addr));
}

static void MsgSocket_ProcessReceived()
{
int msgLen;

while (1)
{
msgLen = 4 + ntohl(*((UINT32 *)g_msgSockRecvBuf));
if (msgLen > g_msgSockRecvLen) break;
if (*(g_msgSockRecvBuf+4) != 30)
{ g_msgSockRecvLen = 0; break; }

g_msgSockCB((CHAR *)g_msgSockRecvBuf, msgLen);

if (msgLen < g_msgSockRecvLen)
{
memmove(g_msgSockRecvBuf, g_msgSockRecvBuf+msgLen, g_msgSockRecvLen-msgLen);
g_msgSockRecvLen -= msgLen;
}
else
{
g_msgSockRecvLen = 0;
break;
}
}
}

static void MsgSocket_Receive()
{
CHAR buf[8192], *pTmp;
int received;

while (1)
{
received = recv(g_msgSock, buf, 8192, 0);
if (received == SOCKET_ERROR) break;
if (received > 0)
{
if (!g_msgSockRecvBuf)
{
g_msgSockRecvBuf = malloc(16384);
g_msgSockRecvSize = 16384;
g_msgSockRecvLen = received;
memcpy(g_msgSockRecvBuf, buf, received);
}
else
{
if (g_msgSockRecvSize < g_msgSockRecvLen+received)
{
pTmp = (CHAR *)realloc(g_msgSockRecvBuf, g_msgSockRecvSize + 16384);
if (!pTmp) return;
g_msgSockRecvBuf = pTmp;
g_msgSockRecvSize += 16384;
}
memcpy(g_msgSockRecvBuf+g_msgSockRecvLen, buf, received);
g_msgSockRecvLen += received;
}
time(&g_msgSockLastReceiveTime);
}
}
}

static void MsgSocket_ProcessEvent()
{
WSANETWORKEVENTS ne;

if (SOCKET_ERROR == WSAEnumNetworkEvents(g_msgSock, g_msgSockEvent, &ne))
return;

if (ne.lNetworkEvents & FD_READ)
{
if (ne.iErrorCode[FD_READ_BIT])
{
closesocket(g_msgSock);
g_msgSock = INVALID_SOCKET;
time(&g_msgSockNextConnectTime);
g_msgSockNextConnectTime += 5;
g_msgSockCB(NULL, -1);
return;
}
MsgSocket_Receive();
MsgSocket_ProcessReceived();
}
else if (ne.lNetworkEvents & FD_CONNECT)
{
if (ne.iErrorCode[FD_CONNECT_BIT])
{
closesocket(g_msgSock);
g_msgSock = INVALID_SOCKET;
time(&g_msgSockNextConnectTime);
g_msgSockNextConnectTime += 5;
g_msgSockCB(NULL, -1);
return;
}
//MsgSocket_SendRegister();
}
else if (ne.lNetworkEvents & FD_CLOSE)
{
closesocket(g_msgSock);
g_msgSock = INVALID_SOCKET;
time(&g_msgSockNextConnectTime);
g_msgSockNextConnectTime += 5;
g_msgSockCB(NULL, -1);
}
}

static unsigned __stdcall MsgSocketThreadProc(LPVOID param)
{
WSAEVENT eh[2];
DWORD dwWait;

eh[0] = g_msgSockEvent;
eh[1] = g_msgSockEventStop;

while (1)
{
dwWait = WSAWaitForMultipleEvents(2, eh, FALSE, 1000, FALSE);
if (dwWait == WSA_WAIT_EVENT_0)
MsgSocket_ProcessEvent();
else if (dwWait == WSA_WAIT_TIMEOUT)
MsgSocket_Connect();
else
break;
}

WSASetEvent(g_msgSockEventStopped);
return 0;
}

BOOL BeginMsgSocketThread(MSGSOCKET_CB *msgSockCB,
const CHAR *svrIp, WORD svrPort,
const CHAR *svrPwd)
{
g_msgSockCB = msgSockCB;
strcpy_s(g_msgSvrIp, 32, svrIp);
g_msgSvrPort = svrPort;
strcpy_s(g_msgSvrPwd, 32, svrPwd);
g_msgSockEvent = WSACreateEvent();
g_msgSockEventStop = WSACreateEvent();
g_msgSockEventStopped = WSACreateEvent();
CloseHandle((HANDLE)_beginthreadex(NULL, 0, MsgSocketThreadProc, NULL, 0, NULL));

return TRUE;
}

void StopMsgSocketThread()
{
if (!g_msgSockEventStop) return;

WSASetEvent(g_msgSockEventStop);

while(TRUE)
{
if (WAIT_OBJECT_0==MsgWaitForMultipleObjects(1, &g_msgSockEventStopped, FALSE, INFINITE, QS_ALLINPUT))
break;
else
{
MSG msg;
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
DispatchMessage(&msg);
}
}

WSACloseEvent(g_msgSockEvent);
WSACloseEvent(g_msgSockEventStop);
WSACloseEvent(g_msgSockEventStopped);
g_msgSockEvent = NULL;
g_msgSockEventStop = NULL;
g_msgSockEventStopped = NULL;
if (g_msgSockRecvBuf) free(g_msgSockRecvBuf);

MsgSocket_Close();
}

// ------------------------------------------------------------------------------------------
//
SOCKET CmdSocket_CreateAndConnect(const CHAR *ip, WORD port)
{
SOCKET cmdSock;
struct sockaddr_in addr;

cmdSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (cmdSock == INVALID_SOCKET) return INVALID_SOCKET;

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
if (SOCKET_ERROR == connect(cmdSock, (struct sockaddr *)&addr, sizeof(addr)))
{
closesocket(cmdSock);
return INVALID_SOCKET;
}

return cmdSock;
}

BOOL CmdSocket_Send(SOCKET cmdSock, const CHAR *data, int dataLen)
{
int sent, totalSent = 0;

while (totalSent < dataLen)
{
sent = send(cmdSock, data+totalSent, dataLen-totalSent, 0);
if (sent == SOCKET_ERROR) return FALSE;
totalSent += sent;
}
return TRUE;
}

BOOL CmdSocket_Receive(SOCKET cmdSock, CHAR **recvBuf, int *recvLen)
{
int bufLen, cmdLen, recvLen1, recvTotal = 0;
CHAR *buf, *bufNew;

bufLen = 8192;
buf = malloc(bufLen);
if (!buf) return FALSE;

recvTotal = 0;
while (1)
{
recvLen1 = recv(cmdSock, buf+recvTotal, bufLen-recvTotal, 0);
if (recvLen1 == SOCKET_ERROR) return FALSE;

recvTotal += recvLen1;
if (recvTotal < 5) return FALSE;
cmdLen = 4 + ntohl(*((UINT32 *)buf));
if (*(buf+4) != 30) return FALSE;
if (bufLen < cmdLen)
{
bufLen = ((cmdLen + 1) / 2048 + 1) * 2048;
bufNew = (CHAR *)realloc(buf, bufLen);
if (!bufNew)
{
free(buf);
return FALSE;
}
buf = bufNew;
}
if (recvTotal > cmdLen)
{
free(buf);
return FALSE;
}
if (recvTotal == cmdLen) break;
}
*recvBuf = buf;
*recvLen = recvTotal;

return TRUE;
}

void CmdSocket_Close(SOCKET cmdSock)
{
closesocket(cmdSock);
}


 这些代码摘自“网游更新平台
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐