一个基本的http客户端

高可用 客户端

1. httpClient.h

#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 parseFunc_;
    std::string url_;
    std::string host_;
    unsigned short port_;
};

2.httpClient.cpp

#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(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(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(address->ai_addr);
            freeaddrinfo(reslut);
            return static_cast(addr->sin_addr.s_addr);
        }
        else if (address->ai_family == AF_INET6)
        {
            struct sockaddr_in6 *addr = reinterpret_cast(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(&sendsize), sizeof(sendsize));
    int ret2 = ::setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&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(&timeout), sizeof(timeout));
    int ret2 = ::setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&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;
}

你可能感兴趣的:(http,网络协议,网络)