练习使用 socket 发送简单的 HTTP GET 请求

本文介绍了一种在游戏中实现手机验证的方法,通过向运营后台发送HTTP请求获取玩家的手机绑定信息。采用非阻塞方式处理请求,利用select进行数据接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近项目中有这样的需求,需要在游戏中内嵌手机验证模块,而手机绑定信息保存在运营后台,游戏服务器无法直接访问,所以就需要游戏服务器向运营后台发送一些 HTTP 请求来获取这类的信息。
因为这部分功能写在服务端,为了尽量避免服务器阻塞,在 recv 前加了 select 操作,并且为每个 HTTP 请求分配了一个线程。这里是针对此需求做的一些练习。
#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值