目录
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));//将这个结构体全部置为0addr.sin_addr.s_addr = inet_addr(info.Ip().c_str());//将字符串类型的ip转化为网络格式ipaddr.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; }