freecplus框架-tcp網絡通信

一、源代碼說明

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)
作者:碼農有道

如果文章有錯別字,或者內容有錯誤,或其他的建議和意見,請您留言指正,非常感謝!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章