高可用 客户端
1. httpClient.h
#include <iostream>
#include <string>
#include <functional>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_;
};
2.httpClient.cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <memory>#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/ioctl.h>#include <jsoncpp/json/json.h>#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 ipv6hints.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; // 128kint 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;
}