公司有一个需求,要求客户端能够单线程异步发送请求到服务端。
当在不引入过多第三方库的情况下,可以使用下列的方式发送请求。

当然,C++11有很多异步的库,代码示例用的C++11仅是为了简化线程模型,省去自己封装线程池的麻烦。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <poll.h>
#include <sys/epoll.h>
#include <iostream>
#include <sstream>
#include <thread>
#include <mutex>
#include <list>

using namespace std;

#define IPADDR "192.168.15.135"
#define PORT 19090
#define MAXLINE 1024

typedef unsigned long long UInt64;

enum FD_STEP
{
        STEP_NONE,
        STEP_CONNECT,
        STEP_RW
};

typedef struct SReqNode{
    int fd;
    int key;
    unsigned int seq;
    FD_STEP step;
    std::string host;
    SReqNode()
    {
        fd  = -1;
        seq = 0;
        key = 0;
        host = "";
        step = STEP_NONE;
    }
    SReqNode(int fd,int key,unsigned int seq)
    {
        this->fd  = fd;
        this->key = key;
        this->seq = seq;
    }
} SReqNode;

bool Resolve( const char* szHost, in_addr* phost )
{
    bool bRet = false;
    bool bIsName = false;

    if( NULL == szHost ) return false;

    const char* p = szHost;
    while( *p )
    {
        if( (*p < '0' || *p > '9') && *p != '.' )
        {
            bIsName = true;
            break;
        }
        p++;
    }

    if( bIsName )
    {
        hostent* phent;
        phent = gethostbyname( szHost );
        if( phent )
        {
            memcpy( phost, phent->h_addr, sizeof(in_addr) );
            bRet = true;
        }
    }
    else
    {
        bRet = ( 0 != inet_aton( szHost, phost ) );
    }

    return bRet;
}

class CTCPClient{

public:
    CTCPClient()
    {
        m_ptrThread = NULL;
    }
    ~CTCPClient()
    {
       if (m_ptrThread){
           m_ptrThread->join();
           delete m_ptrThread;
       }
    }
    
    static void _entry(void* ptr)
    {
       ( (CTCPClient*)ptr)->Run();
    }

    void Initialize()
    {
        m_epfd = epoll_create(256);
        m_globalCnt = 1;
        m_ptrThread = new thread(CTCPClient::_entry, this);
    }
    void AddMsg(int key)
    {
        m_msgListMtx.lock();
        m_msgList.push_back(key);
        m_msgListMtx.unlock();
    }
    void dealNewMsg()
    {
        std::list<int>  msgList;
        m_msgListMtx.lock();
        msgList.swap(m_msgList);
        m_msgListMtx.unlock();
        std::list<int>::iterator iter = msgList.begin();
        for (;iter != msgList.end(); iter++)
        {
            dealConnect(*iter);
        }
    }
    void dealConnect(int key)
    {
        struct sockaddr_in servaddr;
        int sock_cli = socket(AF_INET, SOCK_STREAM, 0);
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORT);
        
        if ( Resolve(IPADDR,&(servaddr.sin_addr)) )
        {
            std::string tmpIp = std::string(inet_ntoa(servaddr.sin_addr));
            cout << tmpIp << endl;
        }else{
            cout << "Resolve " << IPADDR << " ERROR." << endl;
        }
        
        //servaddr.sin_addr.s_addr = inet_addr(IPADDR);
        int flags = fcntl(sock_cli, F_GETFL, 0);
        fcntl(sock_cli, F_SETFL, flags|O_NONBLOCK);
        
        connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr));
        SReqNode* ptr = new SReqNode(sock_cli, key, m_globalCnt++);
        ptr->step = STEP_CONNECT;
        ptr->host = std::string(IPADDR) + ":" + std::to_string(PORT);
        struct epoll_event ev;
        ev.data.ptr = (void* )ptr;
        ev.events=EPOLLOUT|EPOLLET;
        epoll_ctl(m_epfd, EPOLL_CTL_ADD, sock_cli, &ev);
    }
    void Run()
    {
        int i , maxi, connfd , sockfd, nfds;
        int n;
        char buf[MAXLINE];
        struct epoll_event ev, events[20];  
        socklen_t clilen;

        for(;;)
        {
            dealNewMsg();
            nfds=epoll_wait(m_epfd,events,20,500); 
            for(i=0; i<nfds; i++)
            {
                SReqNode* ptr = (SReqNode*)events[i].data.ptr;
                if ( events[i].events&EPOLLOUT)
                {
                    if ( ptr->step != STEP_CONNECT )
                    {
                        cout << "EPOLLOUT But not connect will release." << endl;
                        epoll_ctl(m_epfd, EPOLL_CTL_DEL, ptr->fd, &events[i]);
                        close(ptr->fd);
                        delete ptr;
                    }
                    ostringstream rspStr;
                    rspStr << "GET /vinput?key=" << ptr->key << "& HTTP/1.1\r\n";
                    rspStr << "Host: " + ptr->host + "\r\n";
                    rspStr << "Connection: close\r\n";
                    rspStr << "User-Agent: Mozilla/5.0\r\n";
                    rspStr << "Accept-Language: zh-CN,zh;q=0.9\r\n\r\n";
                    
                    ptr->step = STEP_RW;
                    ev.data.ptr = ptr; 
                    ev.events=EPOLLIN|EPOLLET;
                    epoll_ctl(m_epfd, EPOLL_CTL_MOD, ptr->fd, &ev);
                    send(ptr->fd, rspStr.str().c_str(), rspStr.str().length(), 0);
                }
                else if (events[i].events & EPOLLIN)
                {
                    SReqNode* tmpPrt = (SReqNode*)events[i].data.ptr;
                    sockfd = tmpPrt->fd;
                    if ( sockfd < 0)  
                        continue;  
                    n = recv(sockfd,buf,MAXLINE,0);
                    if (n <= 0)   
                    {    
                        close(sockfd);  
                        events[i].data.fd = -1;  
                    }
                    else
                    {
                        buf[n]='\0';
                        printf("Socket %d said : %s\n",sockfd,buf);
                    }
                    epoll_ctl(m_epfd, EPOLL_CTL_DEL, sockfd, &events[i]);
                    close(sockfd);
                    delete tmpPrt;
                }
                else 
                {
                    printf("This is not avaible!");
                }
            }
        }
        close(m_epfd);
    }

private:
    int m_epfd;
    std::mutex m_msgListMtx;
    std::list<int>  m_msgList;
    int m_globalCnt;
    thread* m_ptrThread;
};

int main()
{
    CTCPClient tcpClient;
    tcpClient.Initialize();
    
    int input;
    while(1)
    {
        cout << "input:";
        cin >> input;
        if (input == 3306)
        {
            break;
        }
        tcpClient.AddMsg(input);
    }
    return 0;
}

/***********************
//编译方法:
$ g++ tcpclient.cpp -g -lpthread -std=c++11 -o tcpclient
$ ./tcpclient
input:3
Socket 5 said : HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/plain;charset=UTF-8
Connection: close
Content-Length: 6

Recv:3
************************/
posted on 2021-09-23 14:57  步孤天  阅读(585)  评论(0编辑  收藏  举报