boost asio实现的TCP客户端(同时支持ssl)

因为要开发TCP客户端用于网络通信,用c++来写,要实现跨平台在window和linux都能用的话,自己去封装原始的套接字操作,要兼容linux和window两个平台,有点困难,工作量也不小,于是用了现有的别人封装好的库,用boost的asio来封装一个TCP客户端。 boost 的asio是一个优秀的全异步跨平台的网络通信库,不过源码都是用模板来写的,看起来比较吃力。因为通信也有需要用到SSL的情况,所以设计的TcpClient兼容普通的套接字通信和ssl通信,可以作为初学者的借鉴,有不完善的地方希望大家多多指点。分为4个文件,代码如下:

TcpClient.h文件:

// boost的asio库实现的Tcp客户端类
#ifndef LY_BOOST_TCP_CLIENT
#define LY_BOOST_TCP_CLIENT
#include <string>
#include "boost/asio.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"
#include "boost/bind.hpp"
#include "boost/asio/placeholders.hpp"
#include <boost/thread.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/function.hpp>
#include <vector>
#include <queue>
#include "Logdef.h"

using namespace std;
namespace TC
{
    enum TCP_EVENT
    {
        TCP_EVENT_CONNECTED = 1,
        TCP_EVENT_CONNECT_FAILED,
        TCP_EVENT_WRITE_FAILED,
        TCP_EVENT_READ_FAILED
    };

    // Tcp发送缓冲区
    class TcpSendBuff
    {
        typedef boost::shared_lock<boost::shared_mutex> ReadLock;
        typedef boost::unique_lock<boost::shared_mutex> WriteLock;
        typedef boost::shared_ptr<string> StringPtr;

    public:
        TcpSendBuff();
        ~TcpSendBuff();

        // 添加发送数据,如果未发送数据的总大小超过最大限制,会返回失败
        bool Append(const char *buff, int len);
        // 获取下一次发送的缓冲区数据及长度,如果已经发送完毕,返回空指针NULL, 并且len为0
        const char *GetNextSendBuff(int &len);
        // 每次发送完成后,需要调用这个函数来递减发送的数据,理论上,这个值不会超过发送队列头部的缓冲区大小
        void SetCompleteSendBytes(int len);
        void SetMaxBuffSize(int size) { m_maxSize = size; }

    private:
        std::queue<StringPtr> m_sendQueue; // 发送缓存区,使用队列来存放
        boost::shared_mutex m_rwMutext;    // 发送缓冲区队列要加锁
        int m_sendBuffTotalSize;           // 发送缓冲区的总大小(队列中所有string.size()的和)
        int m_maxSize;
        int m_topEleSendSize; // 队列第一个缓冲区已经发送的字节数
    };

    // Tcp接收缓冲区
    class TcpRecvBuff
    {
        typedef boost::shared_lock<boost::shared_mutex> ReadLock;
        typedef boost::unique_lock<boost::shared_mutex> WriteLock;
        typedef boost::shared_ptr<string> StringPtr;

    public:
        TcpRecvBuff();
        ~TcpRecvBuff();
        // 获取缓冲区首地址,获取失败返回NULL
        // willRecvLen下次准备接收的数据长度,缓冲区内部会决定是否扩容
        char *GetRecvBuff(int willRecvLen);
        // 获取接收的所有数据,如果没有数据,返回NULL并且len为0
        const char *GetRecvData(int &len);
        void SetRecvDataSize(int len) { m_hasBeenRecvSize += len; }
        // 清空接收到的所有数据
        void ClearRecvData() { m_hasBeenRecvSize = 0; };
        void SetMaxBuffSize(int size) { m_maxBuffSize = size; }

    private:
        vector<char> m_recvBuff;        // 接收缓冲区使用vector
        boost::shared_mutex m_rwMutext; // 接收缓冲区队列锁
        int m_hasBeenRecvSize;          // 当前缓冲区已经接收的数据长度
        int m_maxBuffSize;              // 缓冲区最大值
    };

    typedef boost::function<void(const char *, int)> TcpClientReadCallBack;
    // 事件回调
    typedef boost::function<void(TCP_EVENT, string)> TcpClientEventCallBack;
    // 证书校验回调
    typedef boost::function<bool(bool, boost::asio::ssl::verify_context &)> TcpClientVerifyCertificateCallBack;

    class TcpClient : public boost::enable_shared_from_this<TcpClient>
    {
        typedef boost::asio::ip::tcp::endpoint endpoint_type;
        typedef boost::asio::ip::address address_type;
        typedef boost::asio::ip::tcp::socket socket_type;
        typedef boost::shared_ptr<socket_type> sock_ptr;
        typedef boost::asio::io_service io_service_type;
        typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream_sock_type;
        typedef boost::shared_ptr<stream_sock_type> stream_sock_ptr;
        typedef boost::asio::ssl::context ssl_context_type;
        typedef boost::shared_ptr<ssl_context_type> ssl_context_ptr;

    public:
        TcpClient(io_service_type *pIoService);
        ~TcpClient();

        // 本次将要接收的数据长度,直到接收到指定长度的数据后才会调用TcpClientReadCallBack函数回调
        // 连接成功后必须调用此函数才会接收数据,切记
        // bDelPreData 是否删除前面接收的数据,如果不删除,下次回调的时候继续保留
        bool ContinueRecvData(int nRecvLen, bool bDelPreData = true);
        // 设置读数据回调函数
        void SetReadDataCallBack(TcpClientReadCallBack cb) { m_readCb = cb; }
        // 设置事件回调函数,事件类型见TCP_EVENT
        void SetEventCallBack(TcpClientEventCallBack cb) { m_eventCb = cb; }
        // 设置ssl相关证书,bVerifyPeer为true表示需要校验对方,如果cb不为空,则会进行校验回调
        void SetSslContext(string strCapem, string strClientPem, string strPrivateKey, bool bVerifyPeer = true, TcpClientVerifyCertificateCallBack cb = NULL);
        // 异步发起连接,连接结果在事件回调时通知 成功时:TCP_EVENT_CONNECTED 失败时:TCP_EVENT_CONNECT_FAILED
        bool AsyncConnect(string ip, unsigned short port);
        // 异步写数据,如果出错会在事件回调时通知
        bool AsyncWrite(const char *pBuff, int nLen);
        // 关闭连接
        bool Close();

    private:
        // 连接成功回调
        void OnAsyncConnect(const boost::system::error_code &ec);
        // 写数据回调
        void OnAsyncWrite(const boost::system::error_code &ec, size_t bytes_transferred);
        void DoWriteDataToTcp();

        // 收数据回调
        void OnAsyncRead(const boost::system::error_code &ec, size_t recvSize);

        // // 校验对端证书回调函数
        // bool OnVerifyCertificate(bool preverified, boost::asio::ssl::verify_context& ctx);
        // SSL握手回调
        void OnHandleHandshake(const boost::system::error_code &error);

    private:
        endpoint_type m_endPoint;
        sock_ptr m_socketPtr;            // 用于普通的TCP通信
        stream_sock_ptr m_streamSockPtr; // 用于SSL通信
        ssl_context_ptr m_sslContextPtr;
        io_service_type *m_pIoService;

        bool m_bUseSsl;
        bool m_bConnected; // 是否成功建立连接

        // 回调函数
        TcpClientReadCallBack m_readCb;   // 接收数据回调
        TcpClientEventCallBack m_eventCb; // 事件回调
        TcpRecvBuff m_recvBuff;
        TcpSendBuff m_sendBuff;
    };

    typedef boost::shared_ptr<TcpClient> TcpClientPtr;
}

#endif

TcpClient.cpp

#include "WinDef.h"
#include "TcpClient.h"
#include <iostream>
#include <boost/lexical_cast.hpp>

using namespace TC;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;

#define TCP_CLINET_SEND_BUFF_MAX_SIZE 1024 * 1024 * 4

TcpSendBuff::TcpSendBuff() : m_sendBuffTotalSize(0),
                             m_maxSize(TCP_CLINET_SEND_BUFF_MAX_SIZE),
                             m_topEleSendSize(0)
{
}

TcpSendBuff::~TcpSendBuff()
{
}

bool TcpSendBuff::Append(const char *buff, int len)
{
    StringPtr ptr(new string(buff, len));
    // 加锁
    {
        WriteLock writeLock(m_rwMutext);
        if (m_sendBuffTotalSize + len > m_maxSize)
        {
            X_PRINT("Buffer size out of limit, currsize:%d maxsize:%d", m_sendBuffTotalSize, m_maxSize);
            return false;
        }
        m_sendQueue.push(ptr);
        m_sendBuffTotalSize += len;
    }

    return true;
}

const char *TcpSendBuff::GetNextSendBuff(int &len)
{
    len = 0;
    {
        ReadLock writeLock(m_rwMutext);
        if (m_sendQueue.empty())
        {
            return NULL;
        }

        StringPtr topBuffPtr = m_sendQueue.front();
        len = topBuffPtr->size() - m_topEleSendSize;
        const char *retPointer = topBuffPtr->c_str() + m_topEleSendSize;
        return retPointer;
    }
}

void TcpSendBuff::SetCompleteSendBytes(int len)
{
    {
        WriteLock writeLock(m_rwMutext);
        if (m_sendQueue.empty())
        {
            X_PRINT("error: send queue is empty");
            return;
        }

        StringPtr topBuffPtr = m_sendQueue.front();
        if (m_topEleSendSize + len > topBuffPtr->size())
        {
            // 缓冲区错误
            X_PRINT("error: send buff size incorrect");
            return;
        }
        else if (m_topEleSendSize + len < topBuffPtr->size())
        {
            // 第一个缓冲区的数据还没发送完,下次要把剩下的数据发完
            m_topEleSendSize += len;
            return;
        }
        else // (m_topEleSendSize + len) == topBuffPtr->size()
        {
            // 第一个缓冲区的数据已经发完,删除
            m_topEleSendSize = 0;
            m_sendBuffTotalSize -= topBuffPtr->size();
            m_sendQueue.pop();
            X_PRINT("top buff has been send complete, current queue_size:%d", m_sendQueue.size());
            return;
        }
    }
}

TcpRecvBuff::TcpRecvBuff() : m_recvBuff(1024, 0),
                             m_hasBeenRecvSize(0)
{
}
TcpRecvBuff::~TcpRecvBuff()
{
}
char *TcpRecvBuff::GetRecvBuff(int willRecvLen)
{
    {
        WriteLock writeLock(m_rwMutext);
        if (willRecvLen + m_hasBeenRecvSize <= m_recvBuff.size())
        {
            return &m_recvBuff[0] + m_hasBeenRecvSize;
        }
        else if (willRecvLen + m_hasBeenRecvSize > m_maxBuffSize)
        {
            X_PRINT("Buffer size out of limit, currRecvSize:%d willRecvSize:%d maxsize:%d",m_hasBeenRecvSize, willRecvLen, m_maxBuffSize);
            return NULL;
        }
        else
        {
            // 扩容
            m_recvBuff.resize(willRecvLen + m_hasBeenRecvSize);
            return &m_recvBuff[0] + m_hasBeenRecvSize;
        }
    }
}

const char *TcpRecvBuff::GetRecvData(int &len)
{
    len = 0;
    ReadLock readLock(m_rwMutext);
    if (0 == m_hasBeenRecvSize)
    {
        return NULL;
    }
    else
    {
        len = m_hasBeenRecvSize;
        return &m_recvBuff[0];
    }
}

TcpClient::TcpClient(io_service_type *pIoService) : m_pIoService(pIoService),
m_bUseSsl(false),
m_bConnected(false)
{
}

TcpClient::~TcpClient()
{
}

void TcpClient::SetSslContext(string strCapem, string strClientPem, string strPrivateKey, bool bVerifyPeer, TcpClientVerifyCertificateCallBack cb)
{
    m_bUseSsl = true;

    m_sslContextPtr.reset(new ssl_context_type(boost::asio::ssl::context::sslv23)); // 初始化ssl context对象,也可以使用其他ssl版本
    // 设置不压缩
    m_sslContextPtr->set_options(boost::asio::ssl::context::no_compression);
    // 加载ca证书
    m_sslContextPtr->load_verify_file(strCapem);
    // 加载客户端证书
    m_sslContextPtr->use_certificate_file(strClientPem, boost::asio::ssl::context_base::file_format::pem);
    // 加载客户端私钥
    m_sslContextPtr->use_private_key_file(strPrivateKey, boost::asio::ssl::context_base::file_format::pem);
    if (bVerifyPeer)
    {
        m_sslContextPtr->set_verify_mode(boost::asio::ssl::verify_peer);
    }

    m_streamSockPtr.reset(new stream_sock_type(*m_pIoService, *m_sslContextPtr)); // 初始化stream sock
    if (bVerifyPeer)
    {
        // 设置校验对方
        m_streamSockPtr->set_verify_mode(boost::asio::ssl::verify_peer);
    }
    if (!cb.empty())
    {
        // 设置校验回调函数
        //m_streamSockPtr->set_verify_callback(boost::bind(&TcpClient::OnVerifyCertificate, this, _1, _2));
        m_streamSockPtr->set_verify_callback(cb);
    }
}

// bool TcpClient::OnVerifyCertificate(bool preverified, boost::asio::ssl::verify_context &ctx)
// {
//     // The verify callback can be used to check whether the certificate that is
//     // being presented is valid for the peer. For example, RFC 2818 describes
//     // the steps involved in doing this for HTTPS. Consult the OpenSSL
//     // documentation for more details. Note that the callback is called once
//     // for each certificate in the certificate chain, starting from the root
//     // certificate authority.

//     // In this example we will simply print the certificate's subject name.
//     char subject_name[256];
//     X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
//     X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
//     std::cout << "Verifying " << subject_name << "\n";

//     return preverified;
// }

bool TcpClient::AsyncConnect(string ip, unsigned short port)
{
    if (m_bConnected)
    {
        X_PRINT("socket has established");
        return false;
    }

    if (m_bUseSsl)
    {
        string strPort = boost::lexical_cast<string>(port);
        boost::asio::ip::tcp::resolver resolver(*m_pIoService);
        boost::asio::ip::tcp::resolver::query query(ip.c_str(), strPort.c_str());
        boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
        boost::asio::async_connect(m_streamSockPtr->lowest_layer(), endpoint_iterator,
                                   boost::bind(&TcpClient::OnAsyncConnect, shared_from_this(),
                                               boost::asio::placeholders::error));
    }
    else
    {
        boost::asio::ip::tcp::endpoint peer_ep(boost::asio::ip::address::from_string(ip), short(port));
        m_endPoint = peer_ep;
        m_socketPtr.reset(new socket_type(*m_pIoService));
        m_socketPtr->async_connect(m_endPoint, boost::bind(&TcpClient::OnAsyncConnect, shared_from_this(), boost::asio::placeholders::error));
    }
    return true;
}

void TcpClient::OnAsyncConnect(const boost::system::error_code &ec)
{
    if (ec)
    {
        // 连接出现错误
        X_PRINT("connect error:%s", ec.message().c_str());
        if (!m_eventCb.empty())
        {
            m_eventCb(TCP_EVENT_CONNECT_FAILED, ec.message());
        }
        m_bConnected = false;
        if (m_bUseSsl)
        { 
			m_streamSockPtr->lowest_layer().cancel();
			boost::system::error_code call_ec;
			m_streamSockPtr->shutdown(call_ec);
        }
        else
        {
            boost::system::error_code call_ec;
            if (m_socketPtr->is_open())
            {
                m_socketPtr->shutdown(boost::asio::ip::tcp::socket::shutdown_both, call_ec);
                m_socketPtr->close(call_ec);
            }
        }
        return;
    }
    

    if (m_bUseSsl)
    {
        // ssl握手
        m_streamSockPtr->async_handshake(boost::asio::ssl::stream_base::client,
                                         boost::bind(&TcpClient::OnHandleHandshake, shared_from_this(),
                                                     boost::asio::placeholders::error));
        return;
    }

    m_bConnected = true;
    if (!m_eventCb.empty())
    {
        m_eventCb(TCP_EVENT_CONNECTED, "");
    }
}

void TcpClient::OnHandleHandshake(const boost::system::error_code &error)
{
    if (!error)
    {
        X_PRINT("Handshake success");
        m_bConnected = true;
        if (!m_eventCb.empty())
        {
            m_eventCb(TCP_EVENT_CONNECTED, "");
        }
        return;
    }

    X_PRINT("Handshake failed: %s", error.message().c_str());
    if (!m_eventCb.empty())
    {
        m_eventCb(TCP_EVENT_CONNECT_FAILED, error.message());
    }
}

void TcpClient::OnAsyncWrite(const boost::system::error_code &ec, size_t bytes_transferred)
{
    if (ec)
    {
        X_PRINT("write error:%s", ec.message().c_str());
        if (!m_eventCb.empty())
        {
            m_eventCb(TCP_EVENT_WRITE_FAILED, ec.message());
        }

		if (!m_bConnected)
		{
			// 避免多次执行关闭操作而导致崩溃
			return;
		}
        m_bConnected = false;
        if (m_bUseSsl)
        { 
			m_streamSockPtr->lowest_layer().cancel();
			boost::system::error_code call_ec;
			m_streamSockPtr->shutdown(call_ec);
        }
        else
        {
            boost::system::error_code call_ec;
            if (m_socketPtr->is_open())
            {
                m_socketPtr->shutdown(boost::asio::ip::tcp::socket::shutdown_both, call_ec);
                m_socketPtr->close(call_ec);
            }
        }
        return;
    }

    X_PRINT("write success, len:%d", bytes_transferred);

    // 通知缓冲区发送成功
    m_sendBuff.SetCompleteSendBytes(bytes_transferred);
    // 如果没有发送完成,继续发送
    DoWriteDataToTcp();
}

void TcpClient::OnAsyncRead(const boost::system::error_code &ec, size_t recvSize)
{
    if (ec)
    {
        X_PRINT("read error:%s", ec.message().c_str());
        if (!m_eventCb.empty())
        {
            m_eventCb(TCP_EVENT_READ_FAILED, ec.message());
        }

		if (!m_bConnected)
		{
			// 避免多次执行关闭操作而导致崩溃
			return;
		}
        m_bConnected = false;
        if (m_bUseSsl)
        { 
			m_streamSockPtr->lowest_layer().cancel();
			boost::system::error_code call_ec;
			m_streamSockPtr->shutdown(call_ec);
        }
        else
        {
            boost::system::error_code call_ec;
            if (m_socketPtr->is_open())
            {
                m_socketPtr->shutdown(boost::asio::ip::tcp::socket::shutdown_both, call_ec);
                m_socketPtr->close(call_ec);
            }
        }
        return;
    }
    m_recvBuff.SetRecvDataSize(recvSize);

    // 数据接收回调
    if (!m_readCb.empty())
    {
        int dataLen;
        const char *pBuff = m_recvBuff.GetRecvData(dataLen);
        if (pBuff == NULL)
        {
            if (!m_eventCb.empty())
            {
                m_eventCb(TCP_EVENT_READ_FAILED, "RecvBuff error");
            }
            X_PRINT("RecvBuff error");
            return;
        }
        m_readCb(pBuff, dataLen);
    }
}

bool TcpClient::ContinueRecvData(int nRecvLen, bool bDelPreData)
{
    if (!m_bConnected)
    {
        X_PRINT("not connected");
        return false;
    }
    if (bDelPreData)
    {
        m_recvBuff.ClearRecvData();
    }
    char *buff = m_recvBuff.GetRecvBuff(nRecvLen);
    if (buff == NULL)
    {
        X_PRINT("ContinueRecvData error");
        return false;
    }

    if (m_bUseSsl)
    {
        boost::asio::async_read(
            *m_streamSockPtr, boost::asio::buffer(buff, nRecvLen), boost::bind(&TcpClient::OnAsyncRead, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        boost::asio::async_read(
            *m_socketPtr, boost::asio::buffer(buff, nRecvLen), boost::bind(&TcpClient::OnAsyncRead, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
    }
    return true;
}

bool TcpClient::AsyncWrite(const char *pBuff, int nLen)
{
    if (!m_bConnected)
    {
        return false;
    }
    X_PRINT("add bytes to send buff, len:%d", nLen);

    // 放入缓冲区
    if (!m_sendBuff.Append(pBuff, nLen))
    {
        return false;
    }

    DoWriteDataToTcp();
    return true;
}

void TcpClient::DoWriteDataToTcp()
{
    int willSendLen;
    const char *willSendBuff = m_sendBuff.GetNextSendBuff(willSendLen);
    if (willSendBuff == NULL)
    {
        return;
    }

    if (m_bUseSsl)
    {
        boost::asio::async_write(*m_streamSockPtr,
                                 boost::asio::buffer(willSendBuff, willSendLen),
                                 boost::asio::transfer_at_least(willSendLen),
                                 boost::bind(&TcpClient::OnAsyncWrite, shared_from_this(),
                                             boost::asio::placeholders::error,
                                             boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        boost::asio::async_write(*m_socketPtr,
                                 boost::asio::buffer(willSendBuff, willSendLen),
                                 boost::asio::transfer_at_least(willSendLen),
                                 boost::bind(&TcpClient::OnAsyncWrite, shared_from_this(),
                                             boost::asio::placeholders::error,
                                             boost::asio::placeholders::bytes_transferred));
    }
}
bool TcpClient::Close()
{
	if (!m_bConnected)
	{
		// 如果还没连接上,就不要走关闭流程,直接返回
		return true;
	}
	m_bConnected = false;

    if (m_bUseSsl)
    {
        if (m_streamSockPtr != NULL)
        {
			m_streamSockPtr->lowest_layer().cancel();
			boost::system::error_code call_ec;
            m_streamSockPtr->shutdown(call_ec);
        }
    }
    else
    {
        if (m_socketPtr != NULL)
        {
            boost::system::error_code call_ec;
            m_socketPtr->shutdown(boost::asio::ip::tcp::socket::shutdown_both, call_ec);
            m_socketPtr->close(call_ec);
        }
    }
    return true;
}

WinDef.h

#ifdef _MSC_VER
#define _WIN32_WINNT 0x0501
#endif

输出日志相关头文件 Logdef.h

#ifndef TCP_CLIENT_LOG_DEF
#define TCP_CLIENT_LOG_DEF
#include <time.h>
#include <stdio.h>
#include <string>

static std::string GetCurrentDateTime()
{
    struct tm *stTm;
    time_t tm = time(NULL);
    stTm =  localtime(&tm);
    char buffer[50] = {0};
    sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d", stTm->tm_year + 1900, stTm->tm_mon + 1, stTm->tm_mday, stTm->tm_hour, stTm->tm_min, stTm->tm_sec);
    return std::string(buffer);
}

#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__)

#define X_PRINT(...) do {\
	fprintf(stdout, "%s[%s %s:%d]", GetCurrentDateTime().c_str(), __FILENAME__, __FUNCTION__, __LINE__);\
	fprintf(stdout, __VA_ARGS__); \
	fprintf(stdout, "\n"); \
	fflush(stdout); \
}while(0);

#endif

下面是使用示例:

#include "WinDef.h"
#include "TcpClient.h"
#include "boost/thread.hpp"
#include "boost/date_time/posix_time/posix_time.hpp" //定时器


void OnRead(TcpClientPtr clientPtr, const char* pBuff, int len, bool &bClear)
{
    cout << "recv data:" << string(pBuff, len) << endl;
    clientPtr->ContinueRecvData(5);
}

void OnEvent(TcpClientPtr clientPtr, TCP_EVENT ev, string msg)
{
    if (ev == TCP_EVENT_CONNECTED)
    {
        X_PRINT("connect success");
        clientPtr->ContinueRecvData(5);
    }
    else
    {
        cout << "Event callback:" << msg << endl;
    }
}

bool OnVerifyCertificate(bool preverified, boost::asio::ssl::verify_context &ctx)
{
    // The verify callback can be used to check whether the certificate that is
    // being presented is valid for the peer. For example, RFC 2818 describes
    // the steps involved in doing this for HTTPS. Consult the OpenSSL
    // documentation for more details. Note that the callback is called once
    // for each certificate in the certificate chain, starting from the root
    // certificate authority.

    // In this example we will simply print the certificate's subject name.
    char subject_name[256];
    X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
    std::cout << "Verifying " << subject_name << "\n";

    return preverified;
}

boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);   // io_service如果没有用这个work托管,直接run(),在没有任务的时候会自动退出
void ThreadFun(boost::asio::io_service* pIoService)
{
    cout << "run io_service..." << endl;
    try
    {
        io_service.run();
    }
    catch (std::exception &e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    cout << "io_service run end" << endl;
}

int main(int argc, char** argv)
{
    boost::thread thread(boost::bind(ThreadFun, &io_service));
    TcpClientPtr clientPtr(new TcpClient(&io_service));
    clientPtr->SetEventCallBack(boost::bind(OnEvent, clientPtr, _1, _2));
    clientPtr->SetReadDataCallBack(boost::bind(OnRead, clientPtr, _1, _2, _3));
    clientPtr->SetSslContext("ca.crt", "client.crt", "client.key", true, boost::bind(OnVerifyCertificate, _1, _2));
    clientPtr->AsyncConnect("192.168.56.101", 6532);

    string str;
    while (cin >> str)
    {
        clientPtr->AsyncWrite(str.c_str(), str.size());
    }
    return 0;
}

关于编译

因为boost是支持ssl的,但是要包含openssl的头文件以及相应的库,windows下要包含动态库的导入库(libeay32.lib,ssleay32.lib)和运行时动态库(ssleay32.dll,libeay32.dll),linux下需要包含openssl相关的so库(libssl.so)。因此要先编译好openssl库才能使用

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ASIO 是一个通用的异步网络库,可以帮助您编写 HTTP 客户端。下面是一个简单的代码示例,演示了如何使用 ASIO 发送一个 HTTPS 请求: ``` #include <iostream> #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> int main() { boost::asio::io_context ioc; boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client); ctx.set_verify_mode(boost::asio::ssl::verify_peer); boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(ioc, ctx); boost::asio::connect(socket.lowest_layer(), boost::asio::ip::tcp::resolver(ioc).resolve("www.example.com", "https")); socket.handshake(boost::asio::ssl::stream_base::handshake_type::client); boost::asio::streambuf request; std::ostream request_stream(&request); request_stream << "GET / HTTP/1.0\r\n"; request_stream << "Host: www.example.com\r\n"; request_stream << "Accept: */*\r\n"; request_stream << "Connection: close\r\n\r\n"; boost::asio::write(socket, request); boost::asio::streambuf response; boost::asio::read(socket, response); std::cout << &response; return 0; } ``` 这是一个非常基础的代码示例,可以帮助您快速了解如何使用 ASIO 编写 HTTPS 客户端。如果您想要更多信息,可以参考 ASIO 官方文档。 ### 回答2: asio是一个C++网络编程库,易于使用、高效、跨平台,可以用来编写各种类型的网络应用程序,包括HTTPS客户端。 在使用asio编写HTTPS客户端时,需要经历以下几个步骤: 1. 创建一个asio的io_service对象,用于驱动其它的asio组件。 2. 创建一个ssl::context对象,用于配置SSL/TLS连接的参数,比如证书验证、安全选项等。 3. 创建一个ssl::stream对象,用于建立与服务器的SSL/TLS连接。 4. 使用asio提供的ip::tcp::resolver对象来解析主机名和服务,并得到服务器的IP地址和端口号。 5. 使用connect()函数连接服务器。 6. 使用async_handshake()函数进行SSL/TLS握手。 7. 发送HTTPS请求,可以使用asio提供的io_service对象或者ssl::stream对象进行网络通信。 8. 接收服务器的响应,可以使用asio提供的流读取操作,比如async_read()。 9. 处理服务器的响应数据,根据需要进行解析、处理或展示。 10. 使用shutdown()函数结束SSL/TLS连接。 11. 关闭asio的io_service对象,释放资源。 总结来说,使用asio编写HTTPS客户端需要配置SSL/TLS连接的参数、建立连接、进行SSL/TLS握手、发送请求、接收响应以及结束连接。通过使用asio提供的各种功能函数,能够方便地实现一个功能齐全的HTTPS客户端

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值