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)
做者:码农有道

若是这篇文章对您有帮助,请点赞支持,或在您的博客中转发个人文章,谢谢!!!若是文章有错别字,或者内容有错误,或其余的建议和意见,请您留言指正,很是感谢!!!

相关文章
相关标签/搜索