服务与用户界面之间的通信设计
服务与用户界面之间的通信分为两个部分,一是用户界面对服务的操作;二是服务不定时的向界面发送状态或者动作通知。
通常服务与界面之间的通信可以采用的方案有socket(tcp/udp)和命名管道两种方式,socket方案的优势是用户界面可以很容易的在另外一台计算机上操作远程计算机上的服务;而命名管道通常只能管理本机上的服务(最多可以在局域网上管理其它计算机而且需要配置)。
我们的选择是使用socket方案,同时采用TCP方式,因为相对UDP来说,TCP更加给人以可靠的感觉,除了需要处理“流”的分包,TCP相对UDP更加可靠一点。
然后为了处理命令和状态两种不同性质的通信,我们设计了两种socket,分别为CmdSocket和MsgSocket。
用户界面初始化时,创建MsgSocket,并长期与服务保持连接,之后服务的状态变化,都通过这个MsgSocket发送给用户界面,界面再将这些数据显示出来。这里MsgSocket会需要一个额外的线程。
用户的任何操作,需要向服务发送命令并等待回应,这时使用CmdSocket。CmdSocket使用短连接,发送数据并等待回应,然后返回。
采用这样的方式的好处是系统整体比较清晰。以下是我们在产品中使用的代码(通信的内容,是用的mini-xml):
View Code
1 // 创建网络连接 2 SOCKET CmdSocket_CreateAndConnect(const CHAR *ip, WORD port); 3 // 网络发送 4 BOOL CmdSocket_Send(SOCKET cmdSock, const CHAR *data, int dataLen); 5 // 网络接收 6 BOOL CmdSocket_Receive(SOCKET cmdSock, CHAR **recvBuf, int *recvLen); 7 // 关闭连接 8 void CmdSocket_Close(SOCKET cmdSock); 9 10 11 // ----------------------------------------------------------------------------------------- 12 // 异步消息接收线程 13 // 服务器会将一些通知信息通过此接口发送给客户端,格式为XML 14 // ----------------------------------------------------------------------------------------- 15 16 // 回调函数 17 typedef void (CALLBACK MSGSOCKET_CB)(CHAR *msgData, int msgLen); 18 19 // 开始线程 20 BOOL BeginMsgSocketThread(MSGSOCKET_CB *msgSockCB, 21 const CHAR *svrIp, WORD svrPort, 22 const CHAR *svrPwd); 23 // 结束线程 24 void StopMsgSocketThread(); 25 26 27 static MSGSOCKET_CB *g_msgSockCB = NULL; 28 static SOCKET g_msgSock = INVALID_SOCKET; 29 static WSAEVENT g_msgSockEvent = NULL; 30 static WSAEVENT g_msgSockEventStop = NULL; 31 static WSAEVENT g_msgSockEventStopped = NULL; 32 static time_t g_msgSockNextConnectTime = 0; 33 static time_t g_msgSockLastReceiveTime = 0; 34 static CHAR *g_msgSockRecvBuf = NULL; 35 static int g_msgSockRecvSize = 0; 36 static int g_msgSockRecvLen = 0; 37 static CHAR g_msgSvrIp[32] = { 0 }; 38 static WORD g_msgSvrPort = 0; 39 static CHAR g_msgSvrPwd[32] = { 0 }; 40 41 __inline static BOOL Socket_Send(SOCKET cmdSock, const CHAR *data, int dataLen) 42 { 43 int sent, totalSent = 0; 44 45 while (totalSent < dataLen) 46 { 47 sent = send(cmdSock, data+totalSent, dataLen-totalSent, 0); 48 if (sent == SOCKET_ERROR) return FALSE; 49 totalSent += sent; 50 } 51 return TRUE; 52 } 53 54 static void MsgSocket_Close() 55 { 56 if (g_msgSock != INVALID_SOCKET) 57 { 58 closesocket(g_msgSock); 59 g_msgSock = INVALID_SOCKET; 60 } 61 } 62 63 static void MsgSocket_Connect() 64 { 65 struct sockaddr_in addr; 66 time_t currTime; 67 68 time(&currTime); 69 70 if (currTime < g_msgSockNextConnectTime) return; 71 g_msgSockNextConnectTime = currTime + 5; 72 73 g_msgSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 74 75 if (g_msgSock == INVALID_SOCKET) return; 76 77 ResetEvent(g_msgSockEvent); 78 WSAEventSelect(g_msgSock, g_msgSockEvent, FD_CLOSE|FD_CONNECT|FD_READ); 79 80 memset(&addr, 0, sizeof(addr)); 81 addr.sin_family = AF_INET; 82 addr.sin_addr.s_addr = inet_addr(g_msgSvrIp); 83 addr.sin_port = htons(g_msgSvrPort); 84 connect(g_msgSock, (struct sockaddr *)&addr, sizeof(addr)); 85 } 86 87 static void MsgSocket_ProcessReceived() 88 { 89 int msgLen; 90 91 while (1) 92 { 93 msgLen = 4 + ntohl(*((UINT32 *)g_msgSockRecvBuf)); 94 if (msgLen > g_msgSockRecvLen) break; 95 if (*(g_msgSockRecvBuf+4) != 30) 96 { g_msgSockRecvLen = 0; break; } 97 98 g_msgSockCB((CHAR *)g_msgSockRecvBuf, msgLen); 99 100 if (msgLen < g_msgSockRecvLen) 101 { 102 memmove(g_msgSockRecvBuf, g_msgSockRecvBuf+msgLen, g_msgSockRecvLen-msgLen); 103 g_msgSockRecvLen -= msgLen; 104 } 105 else 106 { 107 g_msgSockRecvLen = 0; 108 break; 109 } 110 } 111 } 112 113 static void MsgSocket_Receive() 114 { 115 CHAR buf[8192], *pTmp; 116 int received; 117 118 while (1) 119 { 120 received = recv(g_msgSock, buf, 8192, 0); 121 if (received == SOCKET_ERROR) break; 122 if (received > 0) 123 { 124 if (!g_msgSockRecvBuf) 125 { 126 g_msgSockRecvBuf = malloc(16384); 127 g_msgSockRecvSize = 16384; 128 g_msgSockRecvLen = received; 129 memcpy(g_msgSockRecvBuf, buf, received); 130 } 131 else 132 { 133 if (g_msgSockRecvSize < g_msgSockRecvLen+received) 134 { 135 pTmp = (CHAR *)realloc(g_msgSockRecvBuf, g_msgSockRecvSize + 16384); 136 if (!pTmp) return; 137 g_msgSockRecvBuf = pTmp; 138 g_msgSockRecvSize += 16384; 139 } 140 memcpy(g_msgSockRecvBuf+g_msgSockRecvLen, buf, received); 141 g_msgSockRecvLen += received; 142 } 143 time(&g_msgSockLastReceiveTime); 144 } 145 } 146 } 147 148 static void MsgSocket_ProcessEvent() 149 { 150 WSANETWORKEVENTS ne; 151 152 if (SOCKET_ERROR == WSAEnumNetworkEvents(g_msgSock, g_msgSockEvent, &ne)) 153 return; 154 155 if (ne.lNetworkEvents & FD_READ) 156 { 157 if (ne.iErrorCode[FD_READ_BIT]) 158 { 159 closesocket(g_msgSock); 160 g_msgSock = INVALID_SOCKET; 161 time(&g_msgSockNextConnectTime); 162 g_msgSockNextConnectTime += 5; 163 g_msgSockCB(NULL, -1); 164 return; 165 } 166 MsgSocket_Receive(); 167 MsgSocket_ProcessReceived(); 168 } 169 else if (ne.lNetworkEvents & FD_CONNECT) 170 { 171 if (ne.iErrorCode[FD_CONNECT_BIT]) 172 { 173 closesocket(g_msgSock); 174 g_msgSock = INVALID_SOCKET; 175 time(&g_msgSockNextConnectTime); 176 g_msgSockNextConnectTime += 5; 177 g_msgSockCB(NULL, -1); 178 return; 179 } 180 //MsgSocket_SendRegister(); 181 } 182 else if (ne.lNetworkEvents & FD_CLOSE) 183 { 184 closesocket(g_msgSock); 185 g_msgSock = INVALID_SOCKET; 186 time(&g_msgSockNextConnectTime); 187 g_msgSockNextConnectTime += 5; 188 g_msgSockCB(NULL, -1); 189 } 190 } 191 192 static unsigned __stdcall MsgSocketThreadProc(LPVOID param) 193 { 194 WSAEVENT eh[2]; 195 DWORD dwWait; 196 197 eh[0] = g_msgSockEvent; 198 eh[1] = g_msgSockEventStop; 199 200 while (1) 201 { 202 dwWait = WSAWaitForMultipleEvents(2, eh, FALSE, 1000, FALSE); 203 if (dwWait == WSA_WAIT_EVENT_0) 204 MsgSocket_ProcessEvent(); 205 else if (dwWait == WSA_WAIT_TIMEOUT) 206 MsgSocket_Connect(); 207 else 208 break; 209 } 210 211 WSASetEvent(g_msgSockEventStopped); 212 return 0; 213 } 214 215 BOOL BeginMsgSocketThread(MSGSOCKET_CB *msgSockCB, 216 const CHAR *svrIp, WORD svrPort, 217 const CHAR *svrPwd) 218 { 219 g_msgSockCB = msgSockCB; 220 strcpy_s(g_msgSvrIp, 32, svrIp); 221 g_msgSvrPort = svrPort; 222 strcpy_s(g_msgSvrPwd, 32, svrPwd); 223 g_msgSockEvent = WSACreateEvent(); 224 g_msgSockEventStop = WSACreateEvent(); 225 g_msgSockEventStopped = WSACreateEvent(); 226 CloseHandle((HANDLE)_beginthreadex(NULL, 0, MsgSocketThreadProc, NULL, 0, NULL)); 227 228 return TRUE; 229 } 230 231 void StopMsgSocketThread() 232 { 233 if (!g_msgSockEventStop) return; 234 235 WSASetEvent(g_msgSockEventStop); 236 237 while(TRUE) 238 { 239 if (WAIT_OBJECT_0==MsgWaitForMultipleObjects(1, &g_msgSockEventStopped, FALSE, INFINITE, QS_ALLINPUT)) 240 break; 241 else 242 { 243 MSG msg; 244 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); 245 DispatchMessage(&msg); 246 } 247 } 248 249 WSACloseEvent(g_msgSockEvent); 250 WSACloseEvent(g_msgSockEventStop); 251 WSACloseEvent(g_msgSockEventStopped); 252 g_msgSockEvent = NULL; 253 g_msgSockEventStop = NULL; 254 g_msgSockEventStopped = NULL; 255 if (g_msgSockRecvBuf) free(g_msgSockRecvBuf); 256 257 MsgSocket_Close(); 258 } 259 260 261 // ------------------------------------------------------------------------------------------ 262 // 263 SOCKET CmdSocket_CreateAndConnect(const CHAR *ip, WORD port) 264 { 265 SOCKET cmdSock; 266 struct sockaddr_in addr; 267 268 cmdSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 269 270 if (cmdSock == INVALID_SOCKET) return INVALID_SOCKET; 271 272 memset(&addr, 0, sizeof(addr)); 273 addr.sin_family = AF_INET; 274 addr.sin_addr.s_addr = inet_addr(ip); 275 addr.sin_port = htons(port); 276 if (SOCKET_ERROR == connect(cmdSock, (struct sockaddr *)&addr, sizeof(addr))) 277 { 278 closesocket(cmdSock); 279 return INVALID_SOCKET; 280 } 281 282 return cmdSock; 283 } 284 285 BOOL CmdSocket_Send(SOCKET cmdSock, const CHAR *data, int dataLen) 286 { 287 int sent, totalSent = 0; 288 289 while (totalSent < dataLen) 290 { 291 sent = send(cmdSock, data+totalSent, dataLen-totalSent, 0); 292 if (sent == SOCKET_ERROR) return FALSE; 293 totalSent += sent; 294 } 295 return TRUE; 296 } 297 298 BOOL CmdSocket_Receive(SOCKET cmdSock, CHAR **recvBuf, int *recvLen) 299 { 300 int bufLen, cmdLen, recvLen1, recvTotal = 0; 301 CHAR *buf, *bufNew; 302 303 bufLen = 8192; 304 buf = malloc(bufLen); 305 if (!buf) return FALSE; 306 307 recvTotal = 0; 308 while (1) 309 { 310 recvLen1 = recv(cmdSock, buf+recvTotal, bufLen-recvTotal, 0); 311 if (recvLen1 == SOCKET_ERROR) return FALSE; 312 313 recvTotal += recvLen1; 314 if (recvTotal < 5) return FALSE; 315 cmdLen = 4 + ntohl(*((UINT32 *)buf)); 316 if (*(buf+4) != 30) return FALSE; 317 if (bufLen < cmdLen) 318 { 319 bufLen = ((cmdLen + 1) / 2048 + 1) * 2048; 320 bufNew = (CHAR *)realloc(buf, bufLen); 321 if (!bufNew) 322 { 323 free(buf); 324 return FALSE; 325 } 326 buf = bufNew; 327 } 328 if (recvTotal > cmdLen) 329 { 330 free(buf); 331 return FALSE; 332 } 333 if (recvTotal == cmdLen) break; 334 } 335 *recvBuf = buf; 336 *recvLen = recvTotal; 337 338 return TRUE; 339 } 340 341 void CmdSocket_Close(SOCKET cmdSock) 342 { 343 closesocket(cmdSock); 344 }
这些代码摘自“网游更新平台”
posted on 2012-12-06 12:32 zhaozongzhe 阅读(425) 评论(0) 编辑 收藏 举报
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步