文章目錄
一、源代碼說明
freecplus是一個Linux系統下的C/C++開源框架,源代碼請前往C語言技術網(www.freecplus.net)下載。
本文介紹的是freecplus框架的TCP/IP協議網絡通信的函數和類。
函數和類的聲明文件是freecplus/_freecplus.h。
函數和類的定義文件是freecplus/_freecplus.cpp。
示例程序位於freecplus/demo目錄中。
編譯規則文件是freecplus/demo/makefile。
二、概述
freecplus框架對socket通信封裝如下:
CTcpClient類:socket通信的客戶端類。
CTcpServer類:socket通信的服務端類。
TcpRead函數:接收socket的對端發送過來的數據。
TcpWrite函數:向socket的對端發送數據。
Readn函數:從已經準備好的socket中讀取數據。
Writen函數:向已經準備好的socket中寫入數據。
在閱讀本文章之前,您必須熟悉TCP/IP協議和socket通信,本文是介紹的是freecplus框架中網絡通信的類和函數的用法,不會介紹網絡通信的基礎知識。
三、通信的報文格式
freecplus框架的socket通信報文格式如下:
報文長度+報文內容
報文長度爲4字節的整數,表示的是報文內容的長度,而不是整個TCP報文的長度,整個TCP報文的長度是報文內容的長度+4。
報文長度是4字節的整數,即int,是以二進制流的方式寫入socket,不是ascii碼。
採用CTcpClient類、CTcpServer類、TcpRead函數和TcpWrite函數進行socket通信,可以避免TCP報文粘包的問題。
四、socket通信客戶端
socket通信的客戶端封裝在CTcpClient類中。
類的聲明:
// socket通信的客戶端類
class CTcpClient
{
public:
int m_sockfd; // 客戶端的socket.
char m_ip[21]; // 服務端的ip地址。
int m_port; // 與服務端通信的端口。
bool m_state; // 與服務端的socket連接狀態。
bool m_btimeout; // 調用Read和Write方法時,失敗的原因是否是超時:true-未超時,false-已超時。
int m_buflen; // 調用Read方法後,接收到的報文的大小,單位:字節。
CTcpClient(); // 構造函數。
// 向服務端發起連接請求。
// ip:服務端的ip地址。
// port:服務端監聽的端口。
// 返回值:true-成功;false-失敗。
bool ConnectToServer(const char *ip,const int port);
// 接收服務端發送過來的數據。
// buffer:接收數據緩衝區的地址,數據的長度存放在m_buflen成員變量中。
// itimeout:等待數據的超時時間,單位:秒,缺省值是0-無限等待。
// 返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時,成員變量m_btimeout的值被設置爲true;2)socket連接已不可用。
bool Read(char *buffer,const int itimeout=0);
// 向服務端發送數據。
// buffer:待發送數據緩衝區的地址。
// ibuflen:待發送數據的大小,單位:字節,缺省值爲0,如果發送的是ascii字符串,ibuflen取0,如果是二進制流數據,ibuflen爲二進制數據塊的大小。
// 返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。
bool Write(const char *buffer,const int ibuflen=0);
// 斷開與服務端的連接
void Close();
~CTcpClient(); // 析構函數自動關閉socket,釋放資源。
};
五、socket通信的服務端
socket通信的服務端封裝在CTcpServer類中。
類的聲明:
// socket通信的服務端類
class CTcpServer
{
private:
int m_socklen; // 結構體struct sockaddr_in的大小。
struct sockaddr_in m_clientaddr; // 客戶端的地址信息。
struct sockaddr_in m_servaddr; // 服務端的地址信息。
public:
int m_listenfd; // 服務端用於監聽的socket。
int m_connfd; // 客戶端連接上來的socket。
bool m_btimeout; // 調用Read和Write方法時,失敗的原因是否是超時:true-未超時,false-已超時。
int m_buflen; // 調用Read方法後,接收到的報文的大小,單位:字節。
CTcpServer(); // 構造函數。
// 服務端初始化。
// port:指定服務端用於監聽的端口。
// 返回值:true-成功;false-失敗,一般情況下,只要port設置正確,沒有被佔用,初始化都會成功。
bool InitServer(const unsigned int port);
// 阻塞等待客戶端的連接請求。
// 返回值:true-有新的客戶端已連接上來,false-失敗,Accept被中斷,如果Accept失敗,可以重新Accept。
bool Accept();
// 獲取客戶端的ip地址。
// 返回值:客戶端的ip地址,如"192.168.1.100"。
char *GetIP();
// 接收客戶端發送過來的數據。
// buffer:接收數據緩衝區的地址,數據的長度存放在m_buflen成員變量中。
// itimeout:等待數據的超時時間,單位:秒,缺省值是0-無限等待。
// 返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時,成員變量m_btimeout的值被設置爲true;2)socket連接已不可用。
bool Read(char *buffer,const int itimeout);
// 向客戶端發送數據。
// buffer:待發送數據緩衝區的地址。
// ibuflen:待發送數據的大小,單位:字節,缺省值爲0,如果發送的是ascii字符串,ibuflen取0,如果是二進制流數據,ibuflen爲二進制數據塊的大小。
// 返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。
bool Write(const char *buffer,const int ibuflen=0);
// 關閉監聽的socket,即m_listenfd,常用於多進程服務程序的子進程代碼中。
void CloseListen();
// 關閉客戶端的socket,即m_connfd,常用於多進程服務程序的父進程代碼中。
void CloseClient();
~CTcpServer(); // 析構函數自動關閉socket,釋放資源。
};
六、示例程序
1、客戶端
示例(demo47.cpp)
/*
* 程序名:demo47.cpp,此程序演示採用freecplus框架的CTcpClient類實現socket通信的客戶端。
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include "../_freecplus.h"
int main(int argc,char *argv[])
{
CTcpClient TcpClient; // 創建客戶端的對象。
if (TcpClient.ConnectToServer("172.16.0.15",5858)==false) // 向服務端發起連接請求。
{
printf("TcpClient.ConnectToServer(\"172.16.0.15\",5858) failed.\n"); return -1;
}
char strbuffer[1024]; // 存放數據的緩衝區。
for (int ii=0;ii<5;ii++) // 利用循環,與服務端進行5次交互。
{
memset(strbuffer,0,sizeof(strbuffer));
snprintf(strbuffer,50,"這是第%d個超級女生,編號%03d。",ii+1,ii+1);
printf("發送:%s\n",strbuffer);
if (TcpClient.Write(strbuffer)==false) break; // 向服務端發送請求報文。
memset(strbuffer,0,sizeof(strbuffer));
if (TcpClient.Read(strbuffer,20)==false) break; // 接收服務端的迴應報文。
printf("接收:%s\n",strbuffer);
sleep(1);
}
// 程序直接退出,析構函數會釋放資源。
}
2、服務端
示例(demo48.cpp)
/*
* 程序名:demo48.cpp,此程序演示採用freecplus框架的CTcpServer類實現socket通信的服務端。
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include "../_freecplus.h"
int main(int argc,char *argv[])
{
CTcpServer TcpServer; // 創建服務端對象。
if (TcpServer.InitServer(5858)==false) // 初始化TcpServer的通信端口。
{
printf("TcpServer.InitServer(5858) failed.\n"); return -1;
}
if (TcpServer.Accept()==false) // 等待客戶端連接。
{
printf("TcpServer.Accept() failed.\n"); return -1;
}
printf("客戶端(%s)已連接。\n",TcpServer.GetIP());
char strbuffer[1024]; // 存放數據的緩衝區。
while (true)
{
memset(strbuffer,0,sizeof(strbuffer));
if (TcpServer.Read(strbuffer,300)==false) break; // 接收客戶端發過來的請求報文。
printf("接收:%s\n",strbuffer);
strcat(strbuffer,"ok"); // 在客戶端的報文後加上"ok"。
printf("發送:%s\n",strbuffer);
if (TcpServer.Write(strbuffer)==false) break; // 向客戶端迴應報文。
}
printf("客戶端已斷開。\n"); // 程序直接退出,析構函數會釋放資源。
}
3、運行程序前的準備端
我希望您已經學過計算機網絡的基礎知識,在運行示例程序之前,請確保您的Linux操作系統已開通防火牆。
在demo47.cpp和demo48.cpp程序中,服務端的ip地址和通信端口是寫死在程序中的,請根據您的實際情況修改它們,然後重新編譯。
4、運行程序
先啓動demo48,然後啓動demo47。
demo47的運行效果如下:
demo48的運行效果如下:
七、socket通信的函數
採用CTcpClient和CTcpServer類實現socket通信功能非常方便,但是在實際開發中,某些場景中不能只依賴這兩個類,例如多程線和異步通信等場景,還必須結合以下將要介紹的幾個函數一起使用。
1、TcpRead函數
接收socket的對端發送過來的數據。
函數的聲明:
bool TcpRead(const int sockfd,char *buffer,int *ibuflen,const int itimeout=0);
參數說明:
sockfd:可用的socket連接。
buffer:接收數據緩衝區的地址。
ibuflen:本次成功接收數據的字節數。
itimeout:接收等待超時的時間,單位:秒,缺省值是0-無限等待。
返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時;2)socket連接已不可用。
在CTcpClient和CTcpServer類的Read方法中調用了TcpRead函數。
2、TcpWrite函數
向socket的對端發送數據。
函數的聲明:
bool TcpWrite(const int sockfd,const char *buffer,const int ibuflen=0);
參數說明:
sockfd:可用的socket連接。
buffer:待發送數據緩衝區的地址。
ibuflen:待發送數據的字節數,如果發送的是ascii字符串,ibuflen取0,如果是二進制流數據,ibuflen爲二進制數據塊的大小。
返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。
在CTcpClient和CTcpServer類的Write方法中調用了TcpRead函數。
3、Readn函數
從已經準備好的socket中讀取數據。
函數的聲明:
bool Readn(const int sockfd,char *buffer,const size_t n);
sockfd:已經準備好的socket連接。
buffer:接收數據緩衝區的地址。
n:本次接收數據的字節數。
返回值:成功接收到n字節的數據後返回true,socket連接不可用返回false。
注意:
1)sockfd是已經準備好的socket連接,那什麼是已經準備好的socket?在這個socket上,已經或馬上有n字節的數據一定會到達。
2)成功接收到n字節的數據後返回true,如果沒有n字節的數據怎麼辦?不會,在1)中已經說明了,一定會有n字節的數據會到達。
3)如果數據大於n字節怎麼辦?Readn只讀取n個字節的數據,其它的數據屬於其它的報文。
4)socket的對端是採用Writen方法寫入的數據。
在TcpRead函數中,調用了Readn函數。
4、Writen函數
向已經準備好的socket中寫入數據。
函數的聲明:
bool Writen(const int sockfd,const char *buffer,const size_t n);
sockfd:已經準備好的socket連接。
buffer:待發送數據緩衝區的地址。
n:待發送數據的字節數。
返回值:成功發送完n字節的數據後返回true,socket連接不可用返回false。
在TcpWrite函數中,調用了Writen函數。
八、版權聲明
C語言技術網原創文章,轉載請說明文章的來源、作者和原文的鏈接。
來源:C語言技術網(www.freecplus.net)
作者:碼農有道
如果文章有錯別字,或者內容有錯誤,或其他的建議和意見,請您留言指正,非常感謝!!!