服务与用户界面之间的通信设计
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(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); }
这些代码摘自“网游更新平台”
相关文章推荐
- Silverlight与WCF之间的通信(4)silverlight以net.tcp方式调用console上寄宿的wcf服务
- hadoop学习;安装jdk,workstation虚拟机v2v迁移;虚拟机之间和跨物理机之间ping网络通信;virtualbox的centos中关闭防火墙和检查服务启动
- 自己设计系统之间的通信协议
- Silverlight与WCF之间的通信(1)SL客户端定时请求WCF服务
- 通过内核对象在服务程序和桌面程序之间通信的小问题
- Silverlight与WCF之间的通信(4)silverlight以net.tcp方式调用console上寄宿的wcf服务
- VC 多文档用户界面设计及各个文档之间的切换
- 用Token令牌维护微服务之间的通信安全的实现
- 通过内核对象在服务程序和桌面程序之间通信的小问题
- Unity应用架构设计(2)——使用中介者模式解耦ViewModel之间通信
- 架构设计:系统间通信(17)——服务治理与Dubbo 中篇(分析)
- JS观察者设计模式:实现iframe之间快捷通信
- 通过内核对象在服务程序和桌面程序之间通信的小问题
- GPRS数据传送服务的无线通信控制器设计
- Silverlight与WCF之间的通信(5)silverlight应用和wcf服务的发布方法
- 通过内核对象在服务程序和桌面程序之间通信
- Silverlight与WCF之间的通信(4)silverlight以net.tcp方式调用console上寄宿的wcf服务
- 架构设计:系统间通信(15)——服务治理与Dubbo 上篇
- SpringCloud : 两个微服务进程之间通信(远程调用)
- 架构设计:系统间通信(18)——服务治理与Dubbo 下篇(继续分析)