高可用 客户端
- #include
- #include
- #include
-
- class HttpClient
- {
- public:
- HttpClient(std::string url) : url_(url), port_(0) {}
- int write_http(const std::string &method, const std::string &msg);
-
- private:
- int get_socket();
-
- int set_sock_timeout(int sockfd, int sec, int ms);
-
- int set_buf_size(int sockfd, int sendsize, int recvsize);
-
- int domain_judge(const char *buf);
-
- int split_url(std::string &url, std::string &host, unsigned short &port);
-
- int host_get_by_name(const char *name);
-
- void print_netstat(int err);
-
- int noblock_connect(int sockfd, struct sockaddr *addrs, int addrlen);
-
- int make_http_head(const char *method, std::string &httpmsg, const std::string &purl);
-
- int make_http_msg(const std::string &method, std::string &msg);
-
- int writen(int connfd, const char *vptr, size_t n);
-
- int recvn_timeout(int connfd, char *vptr, int n, int timeout);
-
- std::string readn(int sockfd, size_t n);
-
- int parse_recvmsg(std::string &recvmsg);
-
- int parse_test(std::string &msg);
-
- private:
- std::function<int(std::string &)> parseFunc_;
- std::string url_;
- std::string host_;
- unsigned short port_;
- };
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
-
- #include "httpClient.h"
-
- int HttpClient::make_http_head(const char *method, std::string &httpmsg, const std::string &purl)
- {
- char headbuf[1024] = {0};
- int len = snprintf(headbuf, sizeof(headbuf), "%s %s HTTP/1.1\r\n"
- "Content-Type: application/json\r\n"
- "Accept: application/json\r\n"
- "Host: %s:%d\r\n"
- "Connection: Keep-Alive\r\n"
- "Content-Length: %d\r\n\r\n",
- method,
- purl.c_str(),
- host_.c_str(),
- port_,
- static_cast<int>(httpmsg.size()));
- httpmsg = headbuf + httpmsg;
- return 0;
- }
-
- int HttpClient::make_http_msg(const std::string &method, std::string &msg)
- {
- return make_http_head(method.c_str(), msg, "");
- }
-
- int HttpClient::writen(int connfd, const char *vptr, size_t n)
- {
- int nleft = n, nwrite = 0, retryCont = 0;
- char *ptr = const_cast<char *>(vptr);
-
- while (nleft > 0)
- {
- if ((nwrite = send(connfd, ptr, nleft, MSG_NOSIGNAL)) <= 0)
- {
- if (retryCont < 100 && (nwrite == 0 || errno == EINTR))
- {
- nwrite = 0;
- ++retryCont;
- usleep(10000);
- }
- else
- {
- return -1;
- }
- }
- nleft -= nwrite;
- ptr += nwrite;
- }
-
- return n;
- }
-
- int HttpClient::recvn_timeout(int connfd, char *vptr, int n, int sec)
- {
- int nleft = n;
- int nread = 0, retryCnt = 0;
- char *ptr = vptr;
- fd_set fdset;
- struct timeval timeout;
-
- while (nleft > 0)
- {
- timeout.tv_sec = sec;
- timeout.tv_usec = 0;
- FD_ZERO(&fdset);
- FD_SET(connfd, &fdset);
- if (select(connfd + 1, &fdset, nullptr, nullptr, &timeout) <= 0)
- {
- printf("select fail errno = %d\n", errno);
- break;
- }
- if ((nread = recv(connfd, ptr, nleft, 0)) < 0)
- {
- if (retryCnt < 50 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
- {
- nread = 0;
- ++retryCnt;
- usleep(10000);
- }
- else
- {
- break;
- }
- }
- else if (nread == 0)
- {
- break;
- }
- nleft -= nread;
- ptr += nread;
- }
- return (n - nleft);
- }
-
- std::string HttpClient::readn(int sockfd, size_t n)
- {
- char buf[2048] = {0};
- std::string result;
- int total = 0;
- int byterecv;
-
- do
- {
- byterecv = recvn_timeout(sockfd, buf, 2048, 5);
- if (byterecv <= 0)
- {
- break;
- }
- total += byterecv;
- result.append(buf, byterecv);
- } while (total < n && byterecv == sizeof(buf));
-
- return result;
- }
-
- int HttpClient::parse_recvmsg(std::string &recvmsg)
- {
- std::string ::size_type headend = recvmsg.find("\r\n\r\n");
- if (headend == std::string::npos)
- {
- std::cout << "not found header" << std::endl;
- return false;
- }
- std::string body = recvmsg.substr(headend + 4);
- return parseFunc_(body);
- }
-
- int HttpClient::split_url(std::string &url, std::string &host, unsigned short &port)
- {
- if (!url.compare(0, 7, "http://"))
- {
- url = url.substr(7);
- }
-
- if (!url.compare(0, 8, "https://"))
- {
- url = url.substr(8);
- }
-
- size_t slashPos = url.find("/");
- if (slashPos != std::string::npos)
- {
- url = url.substr(0, slashPos);
- }
-
- size_t colonPos = url.find(":");
- if (colonPos == std::string::npos)
- {
- host = url;
- port = 80;
- }
- else
- {
- host = url.substr(0, colonPos);
- port = atoi(url.substr(colonPos + 1).c_str());
- }
- }
-
- int HttpClient::domain_judge(const char *buf)
- {
- if (nullptr == buf)
- {
- return false;
- }
-
- bool hasChar = false, hasDot = false;
- for (size_t i = 0; i < strlen(buf); ++i)
- {
- if (isalpha(buf[i]))
- {
- hasChar = true;
- continue;
- }
- if ('.' == buf[i])
- {
- hasDot = true;
- continue;
- }
- if (hasChar && hasDot)
- {
- return true;
- }
- }
- return false;
- }
-
- int HttpClient::host_get_by_name(const char *name)
- {
- // char ipaadress[64] = {0};
- struct addrinfo hints, *reslut = nullptr, *address = nullptr;
- memset(&hints, 0, sizeof(addrinfo));
-
- hints.ai_family = AF_UNSPEC; // ipv4 ipv6
- hints.ai_flags = AI_PASSIVE;
- hints.ai_socktype = SOCK_STREAM;
-
- int ret = getaddrinfo(name, nullptr, &hints, &reslut);
- if (ret != 0)
- {
- std::cerr << "getaddrinfo err " << gai_strerror(ret) << std::endl;
- return ret;
- }
-
- for (address = reslut; address != nullptr; address = address->ai_next)
- {
- if (address->ai_family == AF_INET)
- {
- struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(address->ai_addr);
- freeaddrinfo(reslut);
- return static_cast<int>(addr->sin_addr.s_addr);
- }
- else if (address->ai_family == AF_INET6)
- {
- struct sockaddr_in6 *addr = reinterpret_cast<struct sockaddr_in6 *>(address->ai_addr);
- continue; // 暂不使用ipv6
- }
- }
- freeaddrinfo(reslut);
- return false;
- }
-
- int HttpClient::set_buf_size(int sockfd, int sendsize, int recvsize)
- {
- int ret1 = ::setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char *>(&sendsize), sizeof(sendsize));
- int ret2 = ::setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char *>(&recvsize), sizeof(recvsize));
-
- return (ret1 == 0 && ret2 == 0) ? 0 : -1;
- }
-
- int HttpClient::set_sock_timeout(int sockfd, int sec, int ms)
- {
- timeval timeout = {sec, ms * 1000};
- int ret1 = ::setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout));
- int ret2 = ::setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout));
-
- return (ret1 == 0 && ret2 == 0) ? 0 : -1;
- }
-
- int HttpClient::noblock_connect(int sockfd, struct sockaddr *addrs, int addrlen)
- {
- struct timeval timeout = {5, 0};
- int err = -1;
- fd_set fdset;
-
- int flag = 1;
- if (ioctl(sockfd, FIONBIO, &flag) != 0)
- {
- printf("ioctl error\n");
- }
-
- int ret = connect(sockfd, addrs, addrlen);
- if (-1 == ret)
- {
- if (EINPROGRESS == errno)
- {
- FD_ZERO(&fdset);
- FD_SET(sockfd, &fdset);
- if (select(sockfd + 1, nullptr, &fdset, nullptr, &timeout) > 0)
- {
- getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&addrlen);
- if (0 == err)
- {
- ret = 0;
- }
- else
- {
- ret = -1;
- }
- }
- else
- {
- ret = -1;
- }
- }
- }
- flag = 0;
- if (ioctl(sockfd, FIONBIO, &flag) != 0)
- {
- printf("ioctl error\n");
- }
- if (ret < 0)
- {
- print_netstat(err);
- }
- return ret;
- }
-
- void HttpClient::print_netstat(int err)
- {
- switch (err)
- {
- case ENETUNREACH: // 网不通
- case EHOSTUNREACH:
- printf("network is unreachable err[%d]\n", err);
- break;
- case ECONNREFUSED: // 连接端口被拒绝
- printf("no-one listening on the remote address\n");
- break;
- case ETIMEDOUT: // 网络连接失败,服务器未响应
- printf("timeout while attempting connection the server may be to busy\n");
- break;
- default:
- printf("other errno %d\n", err);
- break;
- }
- }
-
- int HttpClient::get_socket()
- {
- int bufsize = 0x20000; // 128k
- int errnoret = -1;
- struct sockaddr_in serveraddr;
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if (sockfd < 0)
- {
- return -1;
- }
- memset(&serveraddr, 0, sizeof(serveraddr));
- serveraddr.sin_family = AF_INET;
- split_url(url_, host_, port_);
-
- if (domain_judge(host_.c_str()))
- {
- serveraddr.sin_addr.s_addr = host_get_by_name(host_.c_str());
- }
- else
- {
- serveraddr.sin_addr.s_addr = inet_addr(host_.c_str());
- }
-
- serveraddr.sin_port = htons(port_);
-
- if (set_buf_size(sockfd, bufsize, bufsize) < 0)
- {
- close(sockfd);
- return -1;
- }
-
- if (set_sock_timeout(sockfd, 5, 0) < 0)
- {
- close(sockfd);
- return -1;
- }
-
- int ret = noblock_connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
- if (-1 == ret)
- {
- close(sockfd);
- return -1;
- }
-
- return sockfd;
- }
-
- int HttpClient::parse_test(std::string &msg)
- {
- std::cout << msg << std::endl;
- return 0;
- }
-
- int HttpClient::write_http(const std::string &method, const std::string &msg)
- {
- std::string httpmsg = msg;
-
- int fd = get_socket();
- if (fd < 0)
- {
- printf("fd is error\n");
- return -1;
- }
- make_http_msg(method, httpmsg);
- writen(fd, httpmsg.c_str(), httpmsg.size());
- std::string recvmsg = readn(fd, 2048);
- std::cout << recvmsg << std::endl;
- if (recvmsg.size())
- {
- parseFunc_ = std::bind(&HttpClient::parse_test, this, std::placeholders::_1);
- parse_recvmsg(recvmsg);
- }
-
- return 0;
- }
-
- void test()
- {
- HttpClient client("192.168.95.1:8080");
- client.write_http("post", "hello\n");
- }
-
- int main()
- {
- test();
- return 0;
- }