freecplus框架-tcp網絡通訊

1、源代碼說明

freecplus是一個Linux系統下的C/C++開源框架,源代碼請前往C語言技術網(www.freecplus.net)下載。網絡

本文介紹的是freecplus框架的TCP/IP協議網絡通訊的函數和類。框架

函數和類的聲明文件是freecplus/_freecplus.h。異步

函數和類的定義文件是freecplus/_freecplus.cpp。socket

示例程序位於freecplus/demo目錄中。ide

編譯規則文件是freecplus/demo/makefile。函數

2、概述

freecplus框架對socket通訊封裝以下:操作系統

CTcpClient類:socket通訊的客戶端類。.net

CTcpServer類:socket通訊的服務端類。計算機網絡

TcpRead函數:接收socket的對端發送過來的數據。3d

TcpWrite函數:向socket的對端發送數據。

Readn函數:從已經準備好的socket中讀取數據。

Writen函數:向已經準備好的socket中寫入數據。

在閱讀本文章以前,您必須熟悉TCP/IP協議和socket通訊,本文是介紹的是freecplus框架中網絡通訊的類和函數的用法,不會介紹網絡通訊的基礎知識。

3、通訊的報文格式

freecplus框架的socket通訊報文格式以下:

報文長度+報文內容

報文長度爲4字節的整數,表示的是報文內容的長度,而不是整個TCP報文的長度,整個TCP報文的長度是報文內容的長度+4

報文長度是4字節的整數,即int,是以二進制流的方式寫入socket,不是ascii碼。

採用CTcpClient類、CTcpServer類、TcpRead函數和TcpWrite函數進行socket通訊,能夠避免TCP報文粘包的問題。

4、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,釋放資源。
};

5、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,釋放資源。
};

6、示例程序

一、客戶端

示例(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);
  }

  // 程序直接退出,析構函數會釋放資源。
}

二、服務端

示例(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");    // 程序直接退出,析構函數會釋放資源。
}

三、運行程序前的準備端

我但願您已經學過計算機網絡的基礎知識,在運行示例程序以前,請確保您的Linux操做系統已開通防火牆。

在demo47.cpp和demo48.cpp程序中,服務端的ip地址和通訊端口是寫死在程序中的,請根據您的實際狀況修改它們,而後從新編譯。

四、運行程序

先啓動demo48,而後啓動demo47。

demo47的運行效果以下:

在這裏插入圖片描述

demo48的運行效果以下:

在這裏插入圖片描述

7、socket通訊的函數

採用CTcpClient和CTcpServer類實現socket通訊功能很是方便,可是在實際開發中,某些場景中不能只依賴這兩個類,例如多程線和異步通訊等場景,還必須結合如下將要介紹的幾個函數一塊兒使用。

一、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函數。

二、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函數。

三、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函數。

四、Writen函數

向已經準備好的socket中寫入數據。

函數的聲明:

bool Writen(const int sockfd,const char *buffer,const size_t n);

sockfd:已經準備好的socket鏈接。

buffer:待發送數據緩衝區的地址。

n:待發送數據的字節數。

返回值:成功發送完n字節的數據後返回true,socket鏈接不可用返回false。

在TcpWrite函數中,調用了Writen函數。

8、版權聲明

C語言技術網原創文章,轉載請說明文章的來源、做者和原文的連接。
來源:C語言技術網(www.freecplus.net)
做者:碼農有道

若是這篇文章對您有幫助,請點贊支持,或在您的博客中轉發個人文章,謝謝!!!若是文章有錯別字,或者內容有錯誤,或其餘的建議和意見,請您留言指正,很是感謝!!!

相關文章
相關標籤/搜索