C++ implements http timeout client FD_SET model

C++ implements http timeout client FD_SET model

foreword

In the project, I found that the http client implemented by libcurl has a problem of slow speed, and it may be that I have not used it well. On a whim, write an http communication class by yourself to achieve controllability. Because it is only used for research and has not been officially launched, it can only be guaranteed to be basically usable, and there is no guarantee that there will be no bugs.

head File


#pragma once
#include<sstream>
#include <string>
#include <iostream>
using namespace std;

#ifdef WIN32
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#endif

class HttpConnect
{
public:
	HttpConnect(void);
	~HttpConnect(void);

public:
    //通信接口
	void SocketComminucation(string host,int port, std::string request,int mSeptime);
protect:    
    //连接超时
	bool MyConnect(char *host,int port, int timeout);
    //post接口
	void postData(std::string host, std::string path, std::string post_content);
	//get接口
    void getData(std::string host, std::string path, std::string get_content);

	//发送超时接口
	int SendData(SOCKET m_sock, const char* msg, int len, int* pBytesSend);
	//接收超时接口
	int RecvData(SOCKET m_sock, char* pData, int* len);
	//错误信息打印
    void printSocketErrorInfo();

private:
	int m_nSecTime;
};

cpp implementation

#include "StdAfx.h"
#include "HttpConnect.h"



#define SEND_TRY_TIMES   0



HttpConnect::HttpConnect():
	m_nSecTime(0)
{
#ifdef WIN32
	//此处一定要初始化一下,否则gethostbyname返回一直为空
	WSADATA wsa = { 0 };
	WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
}

HttpConnect::~HttpConnect()
{

}

//只有全部发送完时才返回大于0(同时也等于*pBytesSend的值)
//否则返回值同select或send函数,pBytesSend保存实际发送字节数,内部会先select
int HttpConnect::SendData(SOCKET m_sock, const char* msg, int len, int* pBytesSend)
{
	fd_set fdWrite;
	timeval TimeOut;
	int ret, nLeft(len), idx(0), i(0);
	TimeOut.tv_sec = m_nSecTime;
	TimeOut.tv_usec = 0;
	do
	{
		FD_ZERO(&fdWrite);
		FD_SET(m_sock, &fdWrite);
		ret = select(m_sock+1, NULL, &fdWrite, NULL, &TimeOut);
		if (ret > 0)
		{
			ret = send(m_sock, msg+idx, nLeft, 0);
			if (ret == 0) //对方连接中断
			{
				*pBytesSend = idx;
				return ret;
			}
			if (ret == SOCKET_ERROR)
			{
				*pBytesSend = idx;
				return ret;
			}
			idx += ret;
			nLeft -= ret;
		}
		else if(SOCKET_ERROR==ret)
		{
			*pBytesSend = idx;
			return ret;
		}
		else//select超时
		{
			if(++i>SEND_TRY_TIMES)//重试SEND_RECV_TRY_TIMES次失败则返回失败
			{
				*pBytesSend = idx;
				return ret;
			}
		}
	}
	while (nLeft > 0);
	*pBytesSend = idx;
	return idx;
}


int HttpConnect::RecvData(SOCKET m_sock, char* pData, int* len)
{
	fd_set fdRead;
	timeval TimeOut;
	int ret, rc(0), offset(0);
	TimeOut.tv_sec = m_nSecTime;
	TimeOut.tv_usec = 0;
	do
	{
		FD_ZERO(&fdRead);
		FD_SET(m_sock, &fdRead);
		ret = select(m_sock+1, &fdRead, NULL, NULL, &TimeOut);
		if (ret > 0)
		{
#ifdef WIN32
			while(rc = recv(m_sock,(char*) pData+offset, 1024,0))
#else
			while(rc = read(hSocket, pData+offset, 1024))
#endif
			{
				if (rc >0)
				{
					offset += rc;
					std::cout  <<"size:"<< rc << std::endl;
				}
			}

			*len = offset;
		}
		else if(SOCKET_ERROR==ret)
		{
			printSocketErrorInfo();
			return ret;
		}
		else//select超时
		{
			std::cout<<"超时"<<endl;
		}
	}
	while (0);

	return *len;
}


void HttpConnect::SocketComminucation(string host,int port, std::string request,int mSeptime)
{
	//if (!SocketConnect(host,port))
	//{
	//	std::cout  << "connect failed" << std::endl;
	//	return;
	//}

	m_nSecTime = mSeptime;

	struct sockaddr_in serverAddress;
	SOCKET hSocket = NULL;
	hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//hSocket = WSASocket(AF_INET, SOCK_STREAM, TPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if( hSocket==INVALID_SOCKET)
	{
		return;
	}

	memset(&serverAddress, 0, sizeof(serverAddress));     
	serverAddress.sin_family      = AF_INET;
	serverAddress.sin_addr.s_addr = inet_addr(host.c_str());   
	serverAddress.sin_port        = htons((short)port);

	int iMode = 0;//block
	ioctlsocket(hSocket, FIONBIO, (u_long FAR*) &iMode);

	DWORD TimeOut=mSeptime;

	if( SOCKET_ERROR==connect(hSocket, (sockaddr*)&serverAddress, sizeof(serverAddress)) )
	{        
		closesocket(hSocket);
		DWORD gle = WSAGetLastError();
		return;
	}

	int nRealSend,nRealRecv;
	if (SendData(hSocket,request.c_str(),request.length(),&nRealSend) > 0)
	{
		char buf[1024*1024] = {0};
		RecvData(hSocket,buf,&nRealRecv);
		std::cout  << buf << std::endl;
	}



#ifdef WIN32
	closesocket(hSocket);
#else
	close(hSocket);
#endif

}

//变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:
//	1.建立socket
//	2.将该socket设置为非阻塞模式
//	3.调用connect()
//	4.使用select()检查该socket描述符是否可写(注意,是可写)
//	5.根据select()返回的结果判断connect()结果
//	6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
bool HttpConnect::MyConnect(char *host,int port, int timeout)
{
	bool bRet = false;
	SOCKET sock = INVALID_SOCKET;
	do 
	{
		TIMEVAL Timeout;
		Timeout.tv_sec = timeout;
		Timeout.tv_usec = 0;
		struct sockaddr_in address;  /* the libc network address data structure */   

		SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (sock == INVALID_SOCKET)
		{
			printSocketErrorInfo();
			break;
		}

		address.sin_addr.s_addr = inet_addr(host); /* assign the address */
		address.sin_port = htons(port);            /* translate int2port num */
		address.sin_family = AF_INET;

		//set the socket in non-blocking
		unsigned long iMode = 1;
		int iResult = ioctlsocket(sock, FIONBIO, &iMode);
		if (iResult != NO_ERROR)
		{
			printf("ioctlsocket failed with error: %ld\n", iResult);
			printSocketErrorInfo();
			break;
		}

		if( SOCKET_ERROR==connect(sock, (sockaddr*)&address, sizeof(address)) )
		{
			int error = -1;
			timeval tm;
			fd_set set_w;
			fd_set set_r;
			int len = sizeof(int);
			tm.tv_sec  = timeout;
			tm.tv_usec = 0;
			FD_ZERO(&set_w);
			FD_SET(sock, &set_w);

			FD_ZERO(&set_r);
			FD_SET(sock, &set_r);

			if( select(sock+1 ,&set_r ,&set_w, NULL, &tm) > 0)
			{
				getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, /*(socklen_t *)*/&len);
				if(error == 0) 
					bRet = true;
			} 

			if (!bRet)
			{
				printSocketErrorInfo();
				break;
			}
		}else
		{
			bRet = true;
		}

		// restart the socket mode
		iMode = 0;
		iResult = ioctlsocket(sock, FIONBIO, &iMode);
		if (iResult != NO_ERROR)
		{
			printf("ioctlsocket failed with error: %ld\n", iResult);
			printSocketErrorInfo();
			break;
		}
	} while (0);

#ifdef WIN32
	closesocket(sock);
#else
	close(sock);
#endif

	return bRet;
}


void HttpConnect::postData(std::string host, std::string path, std::string post_content)
{
	//POST请求方式 
	std::stringstream stream; 
	stream << "POST " << path; 
	stream << " HTTP/1.0\r\n"; 
	stream << "Host: "<< host << "\r\n";
	stream << "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3\r\n";
	stream << "Content-Type:application/json\r\n";
	stream << "Content-Length:" << post_content.length()<<"\r\n";
	stream << "Connection:close\r\n\r\n"; 
	stream << post_content.c_str();

	string strInfo = stream.str();
	SocketComminucation(host,5557,strInfo,6);
} 

void HttpConnect::getData(std::string host, std::string path, std::string get_content)
{
	//GET请求方式 
	std::stringstream stream; 
	stream << "GET " << path << "?" << get_content; 
	stream << " HTTP/1.0\r\n"; 
	stream << "Host: " << host << "\r\n"; 
	stream <<"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3\r\n"; 
	stream <<"Connection:close\r\n\r\n"; 

	string strInfo = stream.str();
	SocketComminucation(host,5557,strInfo,1000); 
}



void HttpConnect::printSocketErrorInfo()
{
	int id = WSAGetLastError();
	switch (id)
	{
		case WSANOTINITIALISED: printf("not initialized\n"); break;
		case WSASYSNOTREADY: printf("sub sys not ready\n"); break;
		case WSAHOST_NOT_FOUND: printf("name server not found\n"); break;
		case WSATRY_AGAIN: printf("server fail\n"); break;
		case WSANO_RECOVERY: printf("no recovery\n"); break;
		case WSAEINPROGRESS: printf("socket blocked by other prog\n"); break;
		case WSANO_DATA: printf("no data record\n"); break;
		case WSAEINTR: printf("blocking call canciled\n"); break;
		case WSAEPROCLIM: printf("limit exceeded\n");
		case WSAEFAULT: printf("lpWSAData in startup not valid\n");
		default: printf("unknown error id = %d\n",id); break;
	};
	printf("receive error.\n");
}

Guess you like

Origin blog.csdn.net/u012842273/article/details/119909351