您的位置:首页 > 理论基础 > 计算机网络

我想写一个Linux下的C++程序库--记我的C++库设计历程:设计一个TCP服务程序

2015-02-17 20:22 399 查看
我想写一个Linux下的C++程序库,实现一些常用的功能。

我首先想到的就是实现一个TCP监听程序。该程序应该具有哪些功能呢?

1: 启动/停止监听

2: 有客户端连接时,通知调用者

3: 与客户端断开时,通知调用者

4: 有消息到达时,通知调用者

5: 尽量避免程序退出时有没有close的socket。

该程序的大体接口及结构主要用一个类表示,内容如下:

#pragma once

#include <functional>

namespace Hi
{
/*
* @ brief TCP监听会发送的通知
*/
class TcpEvent
{
public:
/* brief 客户端连接成功通知,
* 参数分别为socket描述符,远端ip,远端端口,本地ip,本地端口
*/
std::function<void(int,
const char*,
unsigned short,
const char*,
unsigned short)> on_open_;

/* brief 客户端连接断开通知 */
std::function<void(int)> on_close_;

/* brief 接收到客户端消息通知 */
std::function<void(int)> on_receive_;
};
/*
* @ brief TCP监听类
*/
class HiTcpServer
{
public:
/*
* @brief 启动监听
* @param [in] evt 通知对象
* @param [in] port 简体端口
* @param [in] ip 监听IP,如果为空时,表示监听本机所有的IP
* @retval true:成功;false:失败
*/
bool open(const TcpEvent& evt,
unsigned short port,
const char* ip = NULL);

/*
* @brief 停止监听
* @retval true:成功;false:失败
*/
bool close();
};
}


这是我想到的TCP监听程序的最初接口(假定HiTcpServer放在net文件夹下的hiTcpServer.h文件中)。

怎么用呢?测试代码如下:

#pragma once
#include <string>
#include <sys/socket.h>
#include <iostream>
#include "net/hiTcpServer.h"

using namespace std;

// 客户端连接成功
static void on_client_open(int sock,
const char* remote_ip,
unsigned short remote_port,
const char* local_ip,
unsigned short local_port)
{
cout<<"accept a client connect,socket["<<
sock<<"] remote["<<remote_ip<<","<<remote_port<<
"]local["<<local_ip<<","<<local_port<<"]"<<endl;
}

// 接收到客户端消息
static void on_client_recv_data(int sock)
{
char rece_buf[256];
memset(rece_buf, 0, 256);
int n = recv(sock, rece_buf, 256, 0);
cout<<"receive client(socket:"<<sock<<") data,len:"<<n<<endl;
}

// 客户端连接断开
static void on_client_close(int sock)
{
cout<<"client (socket:"<<sock<<") is close"<<endl;
}
void main()
{
Hi::TcpEvent evt;
evt.on_open_ = std::bind(&on_client_open);
evt.on_close_ = std::bind(&on_client_close);
evt.on_receive_ = std::bind(&on_client_recv_data);

Hi::HiTcpServer server;
server.open(evt, 6000);
sleep(60);
}


初步想法就是这样了。

TcpEvent::on_open_函数参数有点多,而且估计每个连接都有远端地址和本地地址,将其抽象成

一个类比较合适,该类可以叫SocketChannel。

具体定义(为了简单,可以将其放入hiTcpServer.h中):

/*
* @brief socket通道信息类
*/
class SocketChannel
{
public:
SocketChannel(): sock_(-1), remote_port_(0), local_port_(0)
{
}
public:
int				sock_;			///< socket文件描述符
std::string 	remote_ip_;		///< 对端IP
unsigned short 	remote_port_;	///< 对端端口
std::string 	local_ip_;		///< 本地IP
unsigned short 	local_port_;	///< 本地端口
};


TcpEvent::on_open_的定义就成了:

std::function<void(bool,int,SocketChannel&)> on_open_;

修正后的HiTcpServer就成了:

#pragma once

#include <functional>

namespace Hi
{
/* * @brief socket通道信息类 */ class SocketChannel { public: SocketChannel(): sock_(-1), remote_port_(0), local_port_(0) { } public: int sock_; ///< socket文件描述符 std::string remote_ip_; ///< 对端IP unsigned short remote_port_; ///< 对端端口 std::string local_ip_; ///< 本地IP unsigned short local_port_; ///< 本地端口 };
/*
* @ brief TCP监听会发送的通知
*/
class TcpEvent
{
public:
/* brief 客户端连接成功通知 */
std::function<void(int, SocketChannel&)> on_open_;

/* brief 客户端连接断开通知 */
std::function<void(int)> on_close_;

/* brief 接收到客户端消息通知 */
std::function<void(int)> on_receive_;
};

/* @ brief 逻辑实现类*/
class TcpServerImpl;

/*
* @ brief TCP监听类
*/
class HiTcpServer
{
public:
HiTcpServer();
~HiTcpServer();
HiTcpServer& operator =(const HiTcpServer&) = delete;
HiTcpServer(const HiTcpServer&) = delete;
public:
/*
* @brief 启动监听
* @param [in] evt 通知对象
* @param [in] port 监听端口
* @param [in] ip 监听IP,如果为空时,表示监听本机所有的IP
* @retval true:成功;false:失败
*/
bool open(const TcpEvent& evt,
unsigned short port,
const char* ip = NULL);

/*
* @brief 停止监听
* @retval true:成功;false:失败
*/
bool close();
private:
TcpServerImpl* impl_; /* brief 实现逻辑的指针 */
};
}


在新的hiTcpServer.h中,除了添加SocketChannel类,修改了TcpEvent::on_open_的参数外,还添加了TcpServerImpl

的指针,并声明了构造函数。

HiTcpServer的impl_变量主要负责实现程序逻辑。

HiTcpServer的拷贝构造函数和赋值指针被delete,以防止创建的HiTcpServer对象被拷贝。

再看一眼hiTcpServer.h感觉有点复杂,SocketChannel,TcpEvent放在这儿有点乱,

而且实现的时候很可能还会用到,所以需要将其从hiTcpServer.h中移除,写入到另一个头文件

中(net下的hiNetCommon.h)。

新的程序:

hiNetCommon.h:

#pragma once
#include <functional>
#include <string>
#include <netinet/in.h>

namespace Hi
{
/* * @brief socket通道信息类 */ class SocketChannel { public: SocketChannel(): sock_(-1), remote_port_(0), local_port_(0) { } public: int sock_; ///< socket文件描述符 std::string remote_ip_; ///< 对端IP unsigned short remote_port_; ///< 对端端口 std::string local_ip_; ///< 本地IP unsigned short local_port_; ///< 本地端口 };
/*
* @ brief TCP监听会发送的通知
*/
class TcpEvent
{
public:
TcpEvent();
public:
/* brief 客户端连接成功通知 */
std::function<void(int, SocketChannel&)> on_open_;

/* brief 客户端连接断开通知 */
std::function<void(int)> on_close_;

/* brief 接收到客户端消息通知 */
std::function<void(int)> on_receive_;
};
}


hiTcpServer.h:

#pragma once

#include "net/hiNetCommon.h"

namespace Hi
{
/* @ brief 逻辑实现类*/
class TcpServerImpl;

/*
* @ brief TCP监听类
*/
class HiTcpServer
{
public:
HiTcpServer();
~HiTcpServer();
HiTcpServer& operator =(const HiTcpServer&) = delete;
HiTcpServer(const HiTcpServer&) = delete;
public:
/*
* @brief 启动监听
* @param [in] evt 通知对象
* @param [in] port 监听端口
* @param [in] ip 监听IP,如果为空时,表示监听本机所有的IP
* @retval true:成功;false:失败
*/
bool open(const TcpEvent& evt,
unsigned short port,
const char* ip = NULL);

/*
* @brief 停止监听
* @retval true:成功;false:失败
*/
bool close();
private:
TcpServerImpl* impl_;	/* brief 实现逻辑的指针 */
};
}


测试程序:

#pragma once
#include <string>
#include <sys/socket.h>
#include <iostream>
#include "net/hiTcpServer.h"

using namespace std;

// 客户端连接成功
static void on_client_open(int sock,
Hi::SocketChannel& channel)
{
cout<<"accept a client connect,socket["<<
sock<<"] remote["<<channel.remote_ip<<
","<<channel.remote_port<<
"]local["<<channel.local_ip<<","<<
channel.local_port<<"]"<<endl;
}

// 接收到客户端消息
static void on_client_recv_data(int sock)
{
char rece_buf[256];
memset(rece_buf, 0, 256);
int n = recv(sock, rece_buf, 256, 0);
cout<<"receive client(socket:"<<sock<<") data,len:"<<n<<endl;
}

// 客户端连接断开
static void on_client_close(int sock)
{
cout<<"client (socket:"<<sock<<") is close"<<endl;
}
void main()
{
Hi::TcpEvent evt;
evt.on_open_ = std::bind(&on_client_open);
evt.on_close_ = std::bind(&on_client_close);
evt.on_receive_ = std::bind(&on_client_recv_data);

Hi::HiTcpServer server;
server.open(evt, 6000);
sleep(60);
}


HiTcpServer类看起来比较简单,但是我不会一上来就实现HiTcpServer,

因为它的实现不像看起来那样简单,涉及到socket的监听和IO事件的分发。所以还需要有其他的程序支持。

我打算添加两个类HiTcpListen和HiTcpEPoll分贝来实现socket的监听和IO事件的分发。

后续1:

1: 该库只有在支持C++11的gcc才能编译

2: 对stl不太熟悉的调用者,需要了解的知识点有:function,bind,delete函数(C++11新特性)

3: 其实我还想为HiTcpServer和TcpEvent添加免派生功能的,以防止类被继承,

但是我没有好的方法实现这样的功能。希望调用者,最好不要从HiTcpServer和TcpEvent派生子类。

4: 在HiTcpServer的最初版本中,有三个重载的open函数,最后我调整了参数的顺序(将可为空的ip

调整到最后),将三个函数合并为一个,总感觉opend函数的参数顺序有点怪异。

5: 在这个接口中,我尽量避免通过类继承来实现功能扩展。

6: 为了保持简单明了,我没有对socket文件描述符做任何处理直接暴露给了调用者,感觉这样对调用者更友好。

7: 远端地址和本地地址其实可以通过socket文件描述符方便的获得,所以感觉TcpEvent::on_open_

的第二个参数有点多余,以后的版本中可能会修改该接口。如果真的这样做,会添加一个根据socket文件描述符获得

相关信息的接口,或者用户直接用getpeername和getsockname也行。

《unix编程艺术》中,强调“薄胶合层”,现在的TcpEvent::on_open_好像是违反了这个原则。

8: 在我最初的想法中,HiTcpServer::open有三个function参数,分别对应于TcpEvent的

三个成员变量,我觉得HiTcpServer::open参数有点多,才抽象出TcpEvent的。

限制:

1: HiTcpServer类限制了用户选择IO复用机制的自由,启动socket监听的自由,甚至网络比较差时收发数据的

处理也没有支持。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: