最近項目中有這樣的需求,需要在游戲中內嵌手機驗證模塊,而手機綁定信息保存在運營後台,游戲服務器無法直接訪問,所以就需要游戲服務器向運營後台發送一些 HTTP 請求來獲取這類的信息。 因為這部分功能寫在服務端,為了盡量避免服務器阻塞,在 recv 前加了 select 操作,並且為每個 HTTP 請求分配了一個線程。這裡是針對此需求做的一些練習。 [cpp] #include <stdio.h> #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") // 連接服務器發送消息並將接收數據保存到 pReceiveBuf 中,此過程會 select 並 recv 消息,直到沒有數據到達或者接收緩存已滿 int connectAndSendData(const char *szHost, unsigned short nPort, const char *pDataToSend, unsigned int nDataSize, char *pReceiveBuf = NULL, unsigned int *pnReceiveSize = NULL) { WORD wVersionRequested = MAKEWORD(1, 1); WSADATA wsaData; int err = ::WSAStartup(wVersionRequested, &wsaData); if ( 0 != err ) { printf("[connectAndSendData]: WSAStartup failed. return %d. \r\n", err); return -1; } if ( wsaData.wVersion != wVersionRequested ) { printf("[connectAndSendData]: wsaData.wVersion %d is not equal to wVersionRequested %d.\r\n", wsaData.wVersion, wVersionRequested); ::WSACleanup(); return -2; } SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if ( INVALID_SOCKET == sock ) { printf("[connectAndSendData]: socket error %d. \r\n", WSAGetLastError()); return -3; } struct hostent *p_hostent = gethostbyname(szHost); if( NULL == p_hostent ) { printf("[gethostbyname]: socket error %d. \r\n", WSAGetLastError()); ::closesocket(sock); ::WSACleanup(); return -4; } SOCKADDR_IN addr_server; addr_server.sin_family = AF_INET; addr_server.sin_addr = *((struct in_addr*)p_hostent->h_addr); memset(addr_server.sin_zero, 0, 8); addr_server.sin_port = htons(nPort); err = ::connect(sock, (SOCKADDR*)&addr_server, sizeof(addr_server)); if ( SOCKET_ERROR == err ) { printf("[connectAndSendData]: connect %s:%d error %d. \r\n", szHost, nPort, WSAGetLastError()); ::closesocket(sock); ::WSACleanup(); return -5; } err = ::send(sock, pDataToSend, nDataSize, 0); if ( SOCKET_ERROR == err ) { printf("[connectAndSendData]: send error %d. \r\n", WSAGetLastError()); } if ( NULL != pReceiveBuf && NULL != pnReceiveSize ) { char *p_receive = pReceiveBuf; char *p_buf = p_receive; int n_buf_len = *pnReceiveSize; int n_len = n_buf_len - 1; int n_read = 0; char temp[256]; int n_head_len = -1; int n_content_len = -1; const char *content = NULL; while (1) { fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); struct timeval timeo; timeo.tv_sec = 10; timeo.tv_usec = 1000; int ret = select(sock, &fds, NULL, NULL, &timeo); if (ret <= 0) break; if (FD_ISSET(sock, &fds)) { n_read = ::recv(sock, p_buf, n_len, 0); if (n_read <= 0) break; p_buf += n_read; n_len -= n_read; if ( n_len == 0 ) break; const char *rnrn = strstr(p_receive, "\r\n\r\n"); if (NULL != rnrn && rnrn < p_buf) { rnrn += 4; content = rnrn; if (-1 == n_content_len) { const char *content_length = strstr(p_receive, "Content-Length: "); if (NULL != content_length && content_length < p_buf) { content_length += 16; const char *rn = strstr(content_length, "\r\n"); if (NULL != rn && rn < p_buf) { int count = rn - content_length; strncpy(temp, content_length, count); temp[count] = '\0'; n_content_len = atoi(temp); } } if (-1 == n_content_len) { const char *rn = strstr(rnrn, "\r\n"); if (NULL != rn && rn < p_buf) { int count = rn - rnrn; strncpy(temp, rnrn, count); temp[count] = '\0'; if (1 == sscanf(temp, "%x", &n_content_len) ) { n_content_len += 7; // 0D 0A 30 0D 0A 0D 0A content = rn + 2; } } } if (-1 == n_content_len) { const char *connection = strstr(p_receive, "Connection: "); if (NULL != connection && connection < p_buf) { connection += 12; const char *rn = strstr(connection, "\r\n"); if (NULL != rn && rn < p_buf) { int count = rn - connection; strncpy(temp, connection, count); temp[count] = '\0'; connection = _strupr(temp); if (0 == strcmp(connection, "CLOSE")) n_content_len = 0; } } } } } if (NULL != content && n_content_len > 0) { n_head_len = content - p_receive; int n_cur_len = p_buf - p_receive; if ( n_cur_len >= n_head_len + n_content_len ) break; } } } n_len = n_buf_len - 1 - n_len; n_buf_len = n_len; p_receive[n_len] = '\0'; } err = ::closesocket(sock); if ( SOCKET_ERROR == err ) { printf("[connectAndSendData]: closesocket error %d. \r\n", WSAGetLastError()); } ::WSACleanup(); return 0; } int main() { char request_buffer[1024]; request_buffer[0] = '\0'; // 寫 HTTP 頭信息 // HTPP 頭可以有多行,每行都以 \r\n 結尾,最後再以 \r\n 結束 strcat(request_buffer, "GET /index.php?id=1 HTTP/1.1\r\n"); strcat(request_buffer, "Host: 127.0.0.1:80\r\n"); strcat(request_buffer, "Connection: keep-alive\r\n"); strcat(request_buffer, "Accept: */*\r\n"); strcat(request_buffer, "\r\n"); char receive_buff[102400]; unsigned n_receive_len = 102400; receive_buff[0] = '\0'; connectAndSendData("127.0.0.1", 80, request_buffer, strlen(request_buffer), receive_buff, &n_receive_len); printf(receive_buff); getchar(); return 0; }