LINUX网络编程:Tcpsocket封装
目录
1.使用Tcp套接字的流程
2.模板方法设计模式
3.socket.hpp
1.搭建抽象类的算法框架
2.具体类算法实现
2.1创建套接字
2.2绑定
2.3套接字设为监听
2.4接收链接
2.5客户端连接服务器
在使用Tcp套接字时,很多的流程都是固定的,将这些固定的流程封装,更方便后续的使用。
1.使用Tcp套接字的流程
1.创建套接字
2.与网络的信息进行绑定,如ip,端口号
3.将套接字的状态设为监听
4.接收客户端链接
5.进行业务处理
将以上流程都使用面向对象的方式封装,只对外提供接口。
2.模板方法设计模式
在父类(抽象类)中定义出各个算法的框架。
通过子类(具体类)继承父类的框架,将虚方法进行重写。
3.socket.hpp
1.搭建抽象类的算法框架
// inet就是这样的一个类存储这网络的信息 //class inetinfo //{ // std::string _ip;//ip // uint16_t _port;//端口 // struct sockaddr_in _addr; //}; class Socket { public: //纯虚函数 子类继承之后必须重写 virtual void createSokcet() = 0;//创建套接字 virtual void bindSocket(inetinfo &info) = 0;//绑定套接字 virtual void listen() = 0;//设为监听 virtual sockptr accepter(inetinfo &info) = 0;//接收链接 virtual bool connecter(inetinfo &info) = 0;//客户端连接服务器 public: void constructListenSocket(inetinfo &info)//创建服务端的套接字 { createSokcet(); bindSocket(info); listen(); } void construcutClintSocket(inetinfo &info)//创建客户端的套接字 { createSokcet(); connecter(info); } };
2.具体类算法实现
2.1创建套接字
void createSokcet() override { _listenfd = ::socket(AF_INET, SOCK_STREAM, 0);//创建出一个用来监听的fd,不提供服务 if (_listenfd < 0) { exit(ERROR::SOCKERROR); } }
2.2绑定
void bindSocket(inetinfo &info) override { sockaddr_in addr;//一个结构体类型 bzero(&addr, sizeof(addr));//将这个结构体全部置为0 addr.sin_addr.s_addr = inet_addr(info.Ip().c_str());//将字符串类型的ip转化为网络格式ip addr.sin_port = htons(info._port);//将uint16_t类型的端口号 转化为网络格式的端口 addr.sin_family = AF_INET;// 协议族 为AF_INET ipv4的网络协议 socklen_t len = sizeof(addr); int n = bind(_listenfd, (sockaddr *)&addr, len);//将创建出来的套接字与网络信息绑定 if (n < 0) { exit(BINDERROT); } }
2.3套接字设为监听
因为tcp时面向连接的所以必须先获取连接
void listenOrDie() override { //将套接字的状态设为监听 int n = listen(_listenfd, default_num);//从_listenfd中获取连接,第二参数是链接队列的长度 if (n < 0) { exit(LISTENERROR); } }
2.4接收链接
accept接口需要返回一个sockfd用来提供服务,还需要返回一个sockaddr_in标识这个链接是来自哪个ip和端口。
using sockptr = unique_ptr<Socket>; sockptr accepter(inetinfo &info) { sockaddr_in addr; bzero(&addr, sizeof(addr)); socklen_t len = sizeof(addr); int sockfd = accept(_listenfd, (sockaddr *)&addr, &len); if (sockfd < 0) { Log(Info, "accept error"); return nullptr; } info = addr; return std::make_unique<Socket>(sockfd); // 返回一个基类的指针 上层就可以多态调用 }
2.5客户端连接服务器
connecter返回直接设置bool类型即可,不需要返回sockptr,因为客户端创建出的socket直接可以用来连接和业务的io。
参数info需要与那个ip和端口进行连接。
bool connecter(inetinfo &info) { sockaddr_in addr; bzero(&addr, sizeof(addr)); socklen_t len = sizeof(addr); addr.sin_addr.s_addr = inet_addr(info.Ip().c_str()); addr.sin_family = AF_INET; addr.sin_port = htons(info._port); int n = connect(_listenfd, (sockaddr *)&addr, len); if (n < 0) { cout << strerror(errno) << endl; return false; } return true; }