boost网络库开发
一、前言
網絡庫是從事C++開發最基礎、最核心、最常用的一個庫,所有的協議都是建立在一個穩定高效的網絡庫之上的,所以對于c++程序員來說它是必不可少且非常非常重要的一個核心組件,我們可以使用網絡庫做任何我們想做的事情,比如
- 用于文件數據上傳、下載
- 所有通信協議如http、rtp、rtsp等協議的封裝
- 服務器模塊多客戶端監聽、連接、通信
- 客戶端與服務端通信
- 局域網廣播搜索
- 局域網設備搜索
- 多播組播
一直不停的在C++、Java、Web以及linux系統運維等技術方面不停的來回切換,突然發現很久沒有做c++了,最近一直在做java的要多,關于c++這塊從事有8-9年的開發,這塊相關的文章總結的卻比Java要少很多。
- 一個是c++的技術難度和門檻要高很多,一直都在不斷積累,可概要的技術點不多,抽不出時間進行文章化。
- 一個是Java相關的技術容易的多,可寫的內容也多,研究一門基本上都有對應的doc文檔,所以將文檔在翻譯一遍成網絡文章就相對容易很多
不管如何,因為工作原因,最近又杠上了C++的音視頻這塊相關技術,正好抽空將之前所有的c++資料和庫總結了一下:
不總結還好,一旦總結,還是很多的,很多實際項目中開發的庫和組件還沒提取出來,不過對于一個老C++程序員來說,項目中開發好的穩定的庫和資料是彌足珍貴的,要多善于總結和滾雪球。
在開始講解boost網絡庫之前,我這里先給大家稍微普及一下windows下的網絡知識,我是windows下開發的,關于socket編程在windows下有多重開發模型,可以參考一下《winsock網絡編程》 一書
- 選擇模型(select)
選擇模型是網絡編程中最基礎、最簡單的一種模型,因為其簡單易用性所以備受學生時代的在校生或畢業生使用,但是它也是性能最差的一種編程模型。選擇模型是針對select而言的,它可以阻塞可以是非阻塞的,所以它包括兩種模式:
(1)、 阻塞模式
執行I/O操作完成前會一直進行等待,不會將控制權交給程序
(2)、 非阻塞模式
執行I/O操作時,WinSock函數會返回并交出控制權。因為函數在沒有運行完成就進行返回,并會不斷地返回 WSAEWOULDBLOCK錯誤,但是它功能很強大。 - 異步消息選擇
異步消息模型就是借助windows的消息機制來實現的,熟悉win32應用開發或者mfc應用開發的人員都知道,所有的窗口都有對應的消息處理機制,我們只需要創建一個窗口句柄HWND,然后將HWND與對應的消息進行綁定即可,它是通過WsaAsyncSelect接口函數來實現的,它實現的主要思路是:
(1)、首先我們定義一個消息,告訴系統當有客戶端消息到達的時候,發送該消息通知我們
(2)、然后在消息處理函數里面添加對消息的處理即可 - 事件模型
除此之外,winsock還提供了一個異步I/O模型-事件模型–WsaEventSelect ,和WSAAsyncSelect模型類似的是,它也允許應用程序在一個或多個套接字上,接收以事件為基礎的網絡事件通知。該模型最主要的差別在于網絡事件會投遞至一個事件對象句柄,而非投遞至一個窗口例程。
事件選擇模型不用主動去輪詢所有客戶端套接字是否有數據到來的模型,它也是在客戶端有數據到來時,系統發送通知給我們的程序,但是,它不是發送消息,而是通過事件的方式來通知我們的程序,這就解決了WsaAsyncSelect模型只能用在Win32窗口程序的問題。
- 重疊I/O模型
重疊模型是讓應用程序使用重疊數據結構(WSAOVERLAPPED),一次投遞一個或多個 WinSock I/O請求。針對這些請求,在它們完成后,應用程序會受到通知,于是就可以通過另外的代碼來處理這些數據了。有兩種方法可以用來管理重疊I/O請求完成的情況 – 即接到重疊操作完成的通知時處理
(1) 事件對象通知 (Event Object Notification)
基于事件通知的方法,就是要將WinSock事件對象與WSAOVERLPPED結構關聯在一起,在使用重疊結構的情況下,我們常用的 send,sendto,recv,recvfrom 也要被WSASend,WSASendto,WSARecv,WSARecvfrom 替換掉了。
(2) 完成例程(Completion Routies)
完成例程來實現重疊I/O比用事件通知簡單得多,在這個模型中,主線程只用不停的接受連接即可
- 完成端口
完成端口對象取代了WSAAsyncSelect中的消息驅動和WSAEventSelect中的事件對象,當然完成端口模型的內部機制要比WSAAsyncSelect和WSAEventSelect模型復雜得多,但是是性能最佳的網絡編程模型。從本質上說,完成端口模型要求我們創建一個Win32完成端口對象,通過指定數量的線程,對重疊I/O請求進行管理,以便為已經完成的重疊I/O請求提供服務。
假若一個應用程序同時需要管理為數眾多的套接字,那么采用這種模型,往往可以達到最佳的系統性能!
boost庫的網絡模型實現在windows上底層是通過iocp完成端口模型實現的,所以是性能最佳的一種網絡模型實現。
二、實現
boost庫中有一個專門的用于網絡編程的庫-asio,也就是異步io,它能實現tcp、udp、甚至usb串口數據讀取的功能,它是一個非常強大的、跨平臺的異步網絡通信庫,這就是我為什么選擇它的原因。
在介紹源碼實現的時候,我們先了解一下asio中的幾個常用對象,和socket一樣,它包含如下幾個對象
- io_service
它主要作為一個事件驅動器,在多線程編程里面提供了任務隊列和任務分發功能 - acceptor
它和socket一樣,提供了端點連接監聽相關能力 - endpoint和address
address封裝了設備ipv4和ipv6的地址,endpoint標識address和port的組合,決定一臺機器的某個端口(我們稱之為端點)。 - socket
套接口編程模型的核心類,提供了同步和異步操作接口集合
說明了以上幾個核心概念之后,我需要通過boost網絡庫封裝實現如下幾個接口的能力,包括tcp、udp、廣播、數據收發、同步異步等,各個接口以及說明如下:
// --------------------------------------------------------------------------------------- // Function: // Init network SDK // Parameters: // [in]pConnectCallBack : connect or disconnect callback // [in]pDataCallBack : receive data callback // [in]pErrCallBack : error message callback // [in]pContext : callback context // Remark: // This interface must be invoked at the beginning of application, when connect server // success or disconnect server pConnectCallBack callback will be invoked; when data // arrived, pDataCallBack callback will be invoked ; when error occur at the process // of running, pErrCallBack callback will be invoked; // pContext parameter express the scenes of the callback, if the callback invoked by // SDK, pContext will be passed to you by callback last parameter // Return: // if error occur, the result value as the top description will be return by SDK // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Init();// --------------------------------------------------------------------------------------- // Function: // Clean and release SDK resource // Parameters: // NULL // Remark: // This interface must be invoked at the end of application // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Cleanup();// tcp server // --------------------------------------------------------------------------------------- // Function: // Start or stop listen local port // Parameters: // [in]nPort : listen port // [out]pHandle : server handle address // [lHandle] : server handle output by NetSdk_Listen // Remark: // This interface is adapted to server, not for client // Example: // PCONNECTCALLBACK // NetSdk_Listen--> PRECVDATACALLBACK -->NetSdk_UnListen // PNETSDKERRCALLBACK // || // \/ // NetSdk_Send // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Listen(int nPort, PCONNECTCALLBACK pConnectCallBack,PRECVDATACALLBACK pDataCallBack, PNETSDKERRCALLBACK pErrorBack,void* pContext, long* pHandle); NETSDK_API NETSDK_RETURN NetSdk_ListenEx(const char* ipV4OrV6, int nPort, PCONNECTCALLBACK pConnectCallBack,PRECVDATACALLBACK pDataCallBack,PNETSDKERRCALLBACK pErrorBack,void* pContext, long* pHandle); NETSDK_API NETSDK_RETURN NetSdk_UnListen(long lHandle);// tcp client // --------------------------------------------------------------------------------------- // Function: // Connect or disconnect server // Parameters: // [in]pServerIp : server ip address // [in]nPort : server port // [out]pHandle : client handle address // [in]lHandle : client handle // [in]nTimeoutSec : timeout // [in]bindLocalPort : tcp client bind local port // Remark: // This interface is adapted to tcp client, not for server // if set bindLocalPort to none zero, then client bind local port by manual // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Connect(const char* pServerIp, int nPort, int nTimeoutSec,PCONNECTCALLBACK pConnectCallBack, PRECVDATACALLBACK pDataCallBack, void* pContext, long* pHandle, int bindLocalPort = 0);// tcp client // --------------------------------------------------------------------------------------- // Function: // Send message to server or reply message to client // Parameters: // [in]nClientId : client handle // [in]pBytes : send data address // [in]nLen : send data length // Remark: // This interface is adapted to tcp client, not for server // Example: // NetSdk_Connect-->NetSdk_Send-->NetSdk_DisConnect // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Send(long nClientId, unsigned char* pBytes, int nLen); // return byte have send, if error occur then return 0 or less than 0 NETSDK_API int NetSdk_SendSync(long nClientId, unsigned char* pBytes, int nLen);// tcp client // --------------------------------------------------------------------------------------- // Function: // Get TCP Client local address and peer address // Parameters: // [in]nClientId : client handle // [in]pBytes : send data address // [in]nLen : send data length // Remark: // This interface is adapted to tcp client // Example: // NetSdk_Connect-->NetSdk_GetConnectAddr // NetSdk_Listen-->PCONNECTCALLBACK-->NetSdk_GetConnectAddr // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_GetConnectAddr(long nClientId, sockaddr_in* pLocalAddr, sockaddr_in* pPeerAddr);// udp client // --------------------------------------------------------------------------------------- // Function: // Bind local port and send udp data to another device // Parameters: // NULL // Remark: // This interface is adapted to udp client, not for server // NetSdk_Broadcast_Sync and NetSdk_SendTo_Sync is synchronized interface // the value returned is indicate the size has send // NetSdk_Broadcast and NetSdk_SendTo is asynchronized interface // Example: // NetSdk_Bind-->NetSdk_Broadcast // or // NetSdk_Bind-->NetSdk_SendTo // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Bind(const char* pServerIp, int nPort, PRECVDATACALLBACK pDataCallBack, void* pContext,long* pHandle);// --------------------------------------------------------------------------------------- // Function: // Send Udp broadcast message to local network // Parameters: // [in]nClientId : returned by NetSdk_Bind // [in]nPort : which port to all machine // [in]pBytes : the message body // [in]nLen : the message length // Remark: // this interface is used to send broadcast to all // machine in local network // Example: // NetSdk_Bind-->NetSdk_Broadcast // or // NetSdk_Bind-->NetSdk_SendTo // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_Broadcast(long nClientId,int nPort,unsigned char* pBytes, int nLen); // --------------------------------------------------------------------------------------- // Function: // Send Udp message to specific machine // Parameters: // [in]nClientId : returned by NetSdk_Bind // [in]pAddr : the endpoint to received message // [in]pBytes : the message body // [in]nLen : the message length // Remark: // NULL // Example: // NetSdk_Bind-->NetSdk_SendTo // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_SendTo(long nClientId,sockaddr_in* pAddr,unsigned char* pBytes, int nLen);NETSDK_API int NetSdk_Broadcast_Sync(long nClientId,int nPort,unsigned char* pBytes, int nLen); NETSDK_API int NetSdk_SendTo_Sync(long nClientId,sockaddr_in* pAddr,unsigned char* pBytes, int nLen);// tcp or udp client // --------------------------------------------------------------------------------------- // Function: // Close client handle // Parameters: // NULL // Remark: // // Example: // NetSdk_Bind-->NetSdk_CloseHandle // or // NetSdk_Connect-->NetSdk_CloseHandle // Return: // return value returned by SDK As mentioned above // --------------------------------------------------------------------------------------- NETSDK_API NETSDK_RETURN NetSdk_CloseHandle(long lHandle);實現總的類圖如下所示
各個類的作用如下:
- CClient
提供了作為tcp客戶端和udp客戶端的基礎實現類,包括獲取客戶id、獲取地址、端口、日志等接口封裝 - CUdpClient
實現了udp本地端口綁定、數據同步異步發送、數據接收、廣播等接口 - CTcpClient
實現了tcp客戶端連接、斷開、發送數據、處理數據等接口 - CNetWork
服務端網絡的抽象,服務端可以同時監聽多個網卡的多個網口,它包括啟動、停止等接口 - CTcpServer
是tcp網絡的實現,服務端可以同時監聽多個tcp端點,實現多端口通信。 - CNetWorkMgr
網絡管理器,管理多個網絡對象,包括添加網絡、移除網絡、清理等接口
客戶端實現
作為一個客戶端,我想要的主要功能是:連接服務器(tcp)、綁定本機端口(udp接收數據)、發送數據(同步異步)、接收數據等功能。
udp客戶端綁定本機端口:
bool CUdpClient::Bind(udp::endpoint ep) {try{m_bindPort = ep;boost::system::error_code err;if (!m_socket.is_open()){m_socket.open(/*ip::udp::v4()*/ep.protocol(), err);if (err){std::string strErr = (boost::format("open socket error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;}}m_socket.set_option(asio::socket_base::reuse_address(true),err);if (err){std::string strErr = (boost::format("reuse address error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;} #ifdef _WIN32BOOL bNewBehavior = FALSE;DWORD dwBytesReturned = 0;WSAIoctl(m_socket.native_handle(), SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL); #endifm_socket.bind(ep, err);if (err){std::string strErr = (boost::format("bind socket error[%d]")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_INISOCK_ERR,(unsigned char*)strErr.c_str() , strErr.length());return false;}return true;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return false; }udp數據異步發送
NETSDK_RETURN CUdpClient::AsynSendTo(udp::endpoint ep, unsigned char* pBytes, int nLen) {try{// 獲取空bufferBufferPtr pBuffer = m_write_buffer.GetEmptyBuffer();if (!pBuffer)return NETERR_BUFERR_FULL;// 填充數據pBuffer->FillData(pBytes, nLen);pBuffer->m_ep = ep;m_write_buffer.AddFullBuffer(pBuffer);// 數據是否發送完畢boost::mutex::scoped_lock a_lock(m_send_lock);if (m_send_finish){// 獲取當前發送bufferBufferPtr pBuffer = m_write_buffer.GetFullBuffer();// 無可發送的bufferif (!pBuffer)return NETERR_UNKNOWN;m_send_finish = false;AsyncSend(pBuffer);}return NETERR_SUCCESS;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return NETERR_UNKNOWN; }tcp客戶端連接
bool CTcpClient::Connect(std::string strIp, int nPort, bool bSync, int nTimeout) {try{tcp::endpoint ep(ip::address::from_string(strIp), nPort);if (bSync){m_bconnected = false;boost::system::error_code err_code;if (m_bindLocalPort != 0) {m_socket.open(/*tcp::v4()*/ep.protocol(), err_code);m_socket.set_option(tcp::acceptor::reuse_address(true), err_code);tcp::endpoint bind_ep(ip::address::from_string("0.0.0.0"), m_bindLocalPort);m_socket.bind(bind_ep, err_code);}// 非阻塞模式連接,方式默認等待20秒 // { // m_socket.io_control(boost::asio::ip::tcp::socket::non_blocking_io(true)); // //err_code = m_socket.connect(ep, err_code); // m_socket.connect(ep, err_code); // // fd_set fdWrite; // FD_ZERO(&fdWrite); // FD_SET(m_socket.native(), &fdWrite); // timeval tv = { nTimeout }; // if (select(0, NULL, &fdWrite, NULL, &tv) <= 0 || !FD_ISSET(m_socket.native(), &fdWrite)) // { // m_bconnected = false; // return m_bconnected; // } // // m_socket.io_control(boost::asio::ip::tcp::socket::non_blocking_io(false)); // m_bconnected = true; // StartKeepAlive(); // }// err_code = m_socket.connect(ep, err_code); // if (!err_code) // { // m_bconnected = true; // StartKeepAlive(); // return true; // } // return m_bconnected;boost::recursive_mutex::scoped_lock guard(m_connect_mutext);m_socket.async_connect(ep, boost::bind(&CTcpClient::ConnectHandler, shared_from_this(), boost::asio::placeholders::error));m_connect_cond.wait_for(guard, boost::chrono::seconds(nTimeout));return m_bconnected;}else{m_socket.async_connect(ep, boost::bind(&CTcpClient::ConnectHandler, shared_from_this(), boost::asio::placeholders::error));return true;}}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return false; }tcp數據處理
NETSDK_RETURN CTcpClient::AsynWrite(unsigned char* pBytes, int nLen) {try{if(!m_bconnected)return NETERR_SEND_ERR;// 獲取空bufferBufferPtr pBuffer = m_write_buffer.GetEmptyBuffer(nLen);if (!pBuffer)return NETERR_BUFERR_FULL;// 填充數據pBuffer->FillData(pBytes, nLen);m_write_buffer.AddFullBuffer(pBuffer);// 數據是否發送完畢boost::mutex::scoped_lock a_lock(m_send_lock);if (m_send_finish){// 獲取當前發送bufferBufferPtr pNextBuffer = m_write_buffer.GetFullBuffer();// 無可發送的bufferif (!pNextBuffer)return NETERR_UNKNOWN;m_send_finish = false;AsyncSend(pNextBuffer);}return NETERR_SUCCESS;}catch (std::exception& e){CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_UNKNOWN, (unsigned char*)e.what(), strlen(e.what()));}return NETERR_UNKNOWN; }稍微注意的是,tcp數據發送的時候,我這里用了一個環形緩沖區,數據發送首先進入環形緩沖器,發送完成后繼續從環形緩沖區中獲取數據并發送出去。
tcp數據讀取
void CTcpClient::ReadHandler(const boost::system::error_code err, const size_t nTransferedSize) {sockaddr_in stAddr;try{ stAddr.sin_family = AF_INET;stAddr.sin_addr.s_addr = inet_addr(GetAddr().c_str());stAddr.sin_port = htons(GetPort());if (!err && nTransferedSize > 0){CCallBack::NotifyDataCB(m_eType, GetSId(), GetId(),&stAddr,m_read_buffer->m_pBuffer, nTransferedSize);AsynRead();}else if(err){std::string strErr = (boost::format("read tcp data error[%d]\n")%err.value()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_RECV_ERR, (unsigned char*)strErr.c_str(), strErr.length());// 其他異常不認為是斷開if (WSAECONNRESET == err.value() || WSAECONNABORTED == err.value() ||WSAENETRESET == err.value() || WSAESHUTDOWN == err.value() || WSAENETDOWN == err.value() || WSAEHOSTDOWN == err.value()||ERROR_SEM_TIMEOUT == err.value() || ERROR_FILE_NOT_FOUND == err.value()){m_bconnected = false;if(CClientMgr::get_mutable_instance().GetTcpClient(GetId())){CClientMgr::get_mutable_instance().PopTcpClient(GetId());CCallBack::NotifyConnectCB(m_eType, GetSId(), GetId(), &stAddr, true);}}else{std::string strErr = (boost::format("ReadHandle error[%d-%s]\n")%err.value()%err.message()).str();WriteLog(strErr);}}else{std::string strErr = (boost::format("ReadHandle error[%d-%s]\n")%err.value()%err.message()).str();WriteLog(strErr);}}catch (std::exception& e){std::string strErr = (boost::format("read tcp[%d] data exception[%s]\n")%GetId()%e.what()).str();CCallBack::NotifyErrCB(m_eType, GetSId(), GetId(), NETERR_RECV_ERR, (unsigned char*)strErr.c_str(), strErr.length());WriteLog(strErr);} }當有數據回調的時候,我們首先檢查是否有錯誤,如網絡斷開、對方重置連接、關閉、超時等,如果沒有錯誤則回調出去數據,如果異常則回調斷開異常!
服務端實現
服務端實現,最重要的就是支持多個端點的監聽,支持多個客戶端的連接,以下一tcp服務為例進行說明
tcp服務端連接監聽實現
bool CTcpServer::Listen() {try{boost::system::error_code err;if (!m_acceptor.is_open()){m_acceptor.open(m_endPoint.protocol(),err);if (err){Stop();return false;}m_acceptor.set_option(tcp::acceptor::reuse_address(true),err);if (err){Stop();return false;}m_acceptor.bind(m_endPoint,err);if (err){Stop();return false;}m_acceptor.listen(socket_base::max_connections,err);if (err){Stop();return false;}}TcpClientPtr client(new CTcpClient(m_io_server,GetId(),true));client->RegisterConnectCallBack(GetConnectCallBack(), GetConnectCallBackContext());client->RegisterReceiveDataCallBack(GetReceiveDataCallBack(), GetReceiveDataCallBackContext());m_acceptor.async_accept(client->GetSocket(),bind(&CTcpServer::AcceptClient,shared_from_this(),boost::asio::placeholders::error,client));}catch (std::exception){return false;}return true; }當客戶端連接到服務端之后,會調用異步回調
void CTcpServer::AcceptClient(boost::system::error_code err, TcpClientPtr client) {if (err){return ;}// client connect{sockaddr_in stAddr;std::memset(&stAddr, 0, sizeof(stAddr));stAddr.sin_family = AF_INET;stAddr.sin_addr.s_addr = inet_addr(client->GetAddr().c_str());stAddr.sin_port = ::ntohs(client->GetPort());CCallBack::NotifyConnectCB(client_type_tcp, GetId(), client->GetId(), &stAddr, false);}CClientMgr::get_mutable_instance().PushTcpClient(client->GetId(), client);client->StartKeepAlive();client->AsynRead();Listen(); }客戶端連接之后將由CClientMgr客戶端管理對象進行管理。
三、測試
核心的代碼我在上面已經列舉出來,其他的枝節大家可以自行補腦或搜索實現,下面我們使用tcp-udp測試工具進行測試或使用我寫的自帶測試工具測試,首先來了解一下netsdk網絡的使用,我的頭文件中有關于該網絡的說明:
// ------------------------------------------------------------------------------------------------ // File: // netsdk.h // Usage: // net library for tcp client or udp client or tcp server // Remark: // usage for this library discribed as follow // Author: // lixiangxiang from founder // History: // 2015/10/1 v1.0 // Contact: // lixiang6153@126.com csdn name:lixiang987654321 // Copyright: // lixiang6153@126.com // ------------------------------------------------------------------------------------------------//------------------------------------------------------------------------------------------------- // >>>>>>>>>>>>>>>>>>>>>>>usage for this library<<<<<<<<<<<<<<<< // ********************************tcp server************************************************* // NetSdk_Init → PNETSDKERRCALLBACK → NetSdk_Cleanup // ↓ // ↓nClientId (client connected,bExit is false,save client id) // ↓ // PRECVDATACALLBACK(data from client) // ↓ // ↓ // NetSdk_Send (response data toclient) // ↓ // ↓maybe occur // PCONNECTCALLBACK(client disconnect, bExit is true) // // ********************************tcp client************************************************* // NetSdk_Init → NetSdk_Connect → NetSdk_CloseHandle → NetSdk_Cleanup // ↓ // ↓ // PRECVDATACALLBACK(data from server) // ↓ // ↓maybe occur // PCONNECTCALLBACK(server disconnect, bExit is true) // // ********************************udp client************************************************* // NetSdk_Init → NetSdk_Bind NetSdk_CloseHandle→ NetSdk_Cleanup // ↓ // ↓ // NetSdk_SendTo(send data to endpoint) // // ********************************udp broadcast********************************************** // NetSdk_Init → NetSdk_Bind NetSdk_CloseHandle→ NetSdk_Cleanup // ↓ ↓ // ↓ ↓ // (send broadcast to everyone) (receive data from endpoint) // NetSdk_Broadcast PRECVDATACALLBACK // //-------------------------------------------------------------------------------------------------服務端netsdk使用流程
我這里稍作說明,如果是作為服務器端我們的使用方式如下:
NetSdk_Init初始化網絡庫=>NetSdk_Listen開始監聽本機端口=> 接收連接=>處理數據=> NetSdk_UnListen停止端口監聽=>NetSdk_Cleanup清理網絡庫資源我們可以同時監聽多個網卡的多個端口,如果需要服務端反饋數據給客戶端,可以通過連接返回的clientId,使用客戶端相關接口進行回復和反饋。
客戶端netsdk使用流程
作為客戶端,如果是tcp客戶端,使用接口流程如下:
NetSdk_Init初始化網絡庫=>NetSdk_Connect連接服務器=>處理數據=>NetSdk_Send發送數據到服務端=> NetSdk_CloseHandle關閉與服務端連接=>NetSdk_Cleanup清理網絡庫資源作為tcp客戶端,數據發送也可以支持同步和異步發送,默認是異步,同步發送接口NetSdk_SendSync
作為客戶端,如果是udp客戶端,使用接口流程如下:
NetSdk_Init初始化網絡庫=>NetSdk_Bind綁定本機端口=>處理數據=>NetSdk_SendTo發送數據報文到對應機器=> NetSdk_CloseHandle關閉與服務端連接=>NetSdk_Cleanup清理網絡庫資源作為udp客戶端,數據發送也支持同步發送,同步發送接口:NetSdk_SendTo_Sync;除此之外udp還支持廣播,廣播接口也支持同步也異步發送,接口如下:
NetSdk_Broadcast NetSdk_Broadcast_Sync我的測試工具如下所示:
包括了服務端和客戶端,可以將該工具放在2臺機器,一臺做服務端,一臺做客戶端,如果沒有多余機器,可以使用一個工具進行測試既作為服務端,也作為客戶端,客戶端使用同一個ip和端口。
作為服務端進行測試
服務端監聽和斷開監聽代碼
void CNetsdkDemonDlg::OnBnClickedButtonListen() {UpdateData(TRUE);if (m_listenHandle < 0) {if (NETERR_SUCCESS != NetSdk_Listen(m_local_port, Connect_Callback,Receive_Data_Callback,Error_Callback,this, &m_listenHandle)) {AfxMessageBox("監聽本機端口失敗!");return;}GetDlgItem(IDC_BUTTON_LISTEN)->SetWindowText("停止監聽");AddLog("服務端:開始監聽本機端口:%d", m_local_port);EnableServerButton(FALSE);}else {CloseServer();GetDlgItem(IDC_BUTTON_LISTEN)->SetWindowText("開始監聽");AddLog("服務端:停止監聽本機端口");EnableServerButton(TRUE);} } void CNetsdkDemonDlg::CloseServer() {if (m_listenHandle > 0){NetSdk_UnListen(m_listenHandle);m_listenHandle = -1;} }如本機默認監聽的端口為1234,啟動demon后點擊監聽(本地ip使用默認的127.0.0.1監聽所有網卡)
監聽成功后效果
監聽成功后可以使用tcp-dup測試工具測試連接本機的1234端口了
可以看到使用tcp-udp測試工具連接上了我的服務端,注意我的本機真實地址192.168.50.7,服務端監聽的是所有網卡的1234端口!
然后我們發送數據試試看!
可以看到服務端收到了客戶端id為3的客戶發送到服務端的數據!
作為客戶端測試
為了避免干擾,我們使用tcp-udp工具作為服務端,我的測試工具作為客戶端連接到服務端(當然客戶端和服務端完全可以用我的工具)測試如下:
可以看到客戶端連接到了服務端,下面我們開始發送數據:
總結
我給的demon僅僅調用了tcp服務端和tcp客戶端相關接口,當然還包括udp相關接口,這里可以自己測試調用,我想說明的是本網絡庫是經過了大量數據測試和多個實戰項目測試的一個穩定高效的網絡庫組件(120路音視頻主碼流同時發送接收),只要有了網絡庫組件,你再也不在為項目之間的通信而擔憂,只需關注業務邏輯處理即可。
當然,網絡庫進行是封裝了tcp和udp等相關通信,協議層(私有協議或http、rtsp等與業務相關的協議)設計還是需要你個人去設計的,這個與項目的具體業務息息相關,與底層的通信無關(不關心傳輸的是什么數據)。
另外,本網絡庫是經過了大量實戰和不斷更新和修改的穩定高效的網絡庫,花費了較多的心思,所以如果需要本項目的源碼,可以與本人直接溝通購買意向,價格在500-1000不等(自己考量一下應該是很值得了,買一個視頻教程都已經到這個數了),該庫是一個boost封裝了跨平臺的庫(linux只需要稍作改動,請自行修改,本人不會幫忙修改),代碼風格絕對是一個c++老手寫的代碼,閱讀性較強,新手上手較快,很容易修改得linux或定制自己接口或添加其他模塊或功能,代碼一瞥:
源碼獲取、合作、技術交流請獲取如下聯系方式:
QQ交流群:961179337
微信賬號:lixiang6153
公眾號:IT技術快餐
電子郵箱:lixx2048@163.com
總結
以上是生活随笔為你收集整理的boost网络库开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 免费搭建属于自己的域名个性邮箱
- 下一篇: android 开源_适用于Androi