Class封装(改进版v2)

本篇博客定义一套用于TCP通信比较实用/好用Socket类库(运用C++封装的思想,将socketAPI尽量封装的好用与实用),从开发出Socket库的第一个版本以来,作者不知道做了多少改进,每次有新的/好的想法尽量实现到该库当中来;而且我还使用该库开发出作者第一个真正意义上的基于Linux的Server程序[MyHttpd,在后续的博客当中,我一定会将MyHttpd的实现原理与实现代码更新到这篇博客所属的专栏中,希望读者朋友不吝赐教];

可能在以后的学习和工作中,作者还可能会加入新的功能和修复发现的BUG,因此,如果有读者喜欢这个Socket库,请持续关注这篇博客,我会把最新的更新信息都发布到这篇博客当中,当然,如果有读者朋友发现了这个Socket库的BUG,还希望读者朋友不吝赐教,谢谢您的关注;

实现中的几个注意点:

1)TCPSocket类几个成员函数的访问权限为protected,使Socket类可以进行继承,但不允许私自使用;

2)TCPClient类的send/receive方法使用了著名的writen/readn(来源UNP)实现,解决了TCP的粘包问题.

3)TCPServer端添加了地址复用,可以方便TCP服务器重启;

4)添加了异常类,让我们在编写易出错的代码时,可以解放思想,不用一直考虑该函数调用出错会发生什么情况!

5)TCPSocket类中添加了getfd接口,如果有这三个类完成不了的功能,则可以将socket获取出来,使用Linux的系统调用完成相应的功能;

6)TCPClient中有好几个发送/接受的接口,其中,使用send发送的数据一定要使用receive来接收,因为作者使用的是自定义应用层协议来解决的TCP粘包问题,请读者朋友注意;

由于实现思想较简单,因此在代码中并未添加大量的注释,请读者耐心读下去,在博文结尾处,会有该库的测试使用示例与Makefile文件,并将整个文件夹(完整的项目实现源代码)放到了CSDN的下载资源(不需要下载分的O(∩_∩)O~)中,下载链接见下:

Socket类

TCPSocket/TCPClient/TCPServer类设计

class TCPSocket{protected:TCPSocket();virtual ~TCPSocket();bool create();bool bind(unsigned short int port, const char *ip = NULL) const;bool listen(int backlog = SOMAXCONN) const;bool accept(TCPSocket &clientSocket) const;bool connect(unsigned short int port, const char *ip) const;/**注意: TCPSocket基类并没有send/receive方法**/bool reuseaddr() const;bool isValid() const{return (m_sockfd != -1);}public:bool close();int getfd() const{return m_sockfd;}//flag: true=SetNonBlock, false=SetBlockbool setNonBlocking(bool flag) const;protected:int m_sockfd;};/** TCP Client **/class TCPClient : public TCPSocket{private:struct Packet{unsigned int msgLen;//数据部分的长度(网络字节序)chartext[1024]; //报文的数据部分};public:TCPClient(unsigned short int port, const char *ip) throw(SocketException);TCPClient();TCPClient(int clientfd);~TCPClient();size_t send(const std::string& message) const throw(SocketException);size_t receive(std::string& message) const throw(SocketException);size_t read(void *buf, size_t count) throw(SocketException);void write(const void *buf, size_t count) throw(SocketException);size_t write(const char *msg) throw(SocketException);};/** TCP Server **/class TCPServer : public TCPSocket{public:TCPServer(unsigned short int port, const char *ip = NULL, int backlog = SOMAXCONN) throw(SocketException);~TCPServer();void accept(TCPClient &client) const throw(SocketException);TCPClient accept() const throw(SocketException);};

TCPSocket/TCPClient/TCPServer类实现

TCPSocket::TCPSocket(): m_sockfd(-1) {}TCPSocket::~TCPSocket(){if (isValid())::close(m_sockfd);}bool TCPSocket::create(){if (isValid())return false;if ((m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)return false;return true;}bool TCPSocket::bind(unsigned short int port, const char *ip) const{if (!isValid())return false;struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);if (ip == NULL)addr.sin_addr.s_addr = htonl(INADDR_ANY);elseaddr.sin_addr.s_addr = inet_addr(ip);if ( ::bind(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1 )return false;return true;}bool TCPSocket::listen(int backlog) const{if (!isValid())return false;if ( ::listen(m_sockfd, backlog) == -1)return false;return true;}bool TCPSocket::accept(TCPSocket &clientSocket) const{if (!isValid())return false;clientSocket.m_sockfd =::accept(this->m_sockfd, NULL, NULL);if (clientSocket.m_sockfd == -1)return false;return true;}bool TCPSocket::connect(unsigned short int port, const char *ip) const{if (!isValid())return false;struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);if ( ::connect(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)return false;return true;}bool TCPSocket::setNonBlocking(bool flag) const{if (!isValid())return false;int opt = fcntl(m_sockfd, F_GETFL, 0);if (opt == -1)return false;if (flag)opt |= O_NONBLOCK;elseopt &= ~O_NONBLOCK;if (fcntl(m_sockfd, F_SETFL, opt) == -1)return false;return true;}bool TCPSocket::reuseaddr() const{if (!isValid())return false;int on = 1;if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)return false;return true;}bool TCPSocket::close(){if (!isValid())return false;::close(m_sockfd);m_sockfd = -1;return true;}/** client TCP Socket **/TCPClient::TCPClient(unsigned short int port, const char *ip)throw(SocketException){if (create() == false)throw SocketException("tcp client create error");if (connect(port, ip) == false)throw SocketException("tcp client connect error");}TCPClient::TCPClient() {}TCPClient::TCPClient(int clientfd){m_sockfd = clientfd;}TCPClient::~TCPClient() {}/** client端特有的send/receive **/static ssize_t readn(int fd, void *buf, size_t count);static ssize_t writen(int fd, const void *buf, size_t count);//sendsize_t TCPClient::send(const std::string& message)const throw(SocketException){Packet buf;buf.msgLen = htonl(message.length());strcpy(buf.text, message.c_str());if (writen(m_sockfd, &buf, sizeof(buf.msgLen)+message.length()) == -1)throw SocketException("tcp client writen error");return message.length();}//receivesize_t TCPClient::receive(std::string& message)const throw(SocketException){//首先读取头部Packet buf = {0, 0};size_t readBytes = readn(m_sockfd, &buf.msgLen, sizeof(buf.msgLen));if (readBytes == (size_t)-1)throw SocketException("tcp client readn error");else if (readBytes != sizeof(buf.msgLen))throw SocketException("peer connect closed");//然后读取数据部分unsigned int lenHost = ntohl(buf.msgLen);readBytes = readn(m_sockfd, buf.text, lenHost);if (readBytes == (size_t)-1)throw SocketException("tcp client readn error");else if (readBytes != lenHost)throw SocketException("peer connect closed");message = buf.text;return message.length();}size_t TCPClient::read(void *buf, size_t count) throw(SocketException){ssize_t readBytes = ::read(m_sockfd, buf, count);if (readBytes == -1)throw SocketException("tcp client read error");return (size_t)readBytes;}void TCPClient::write(const void *buf, size_t count) throw(SocketException){if ( ::write(m_sockfd, buf, count) == -1 )throw SocketException("tcp client write error");}size_t TCPClient::write(const char *msg) throw(SocketException){if ( ::write(m_sockfd, msg, strlen(msg)) == -1 )throw SocketException("tcp client write error");return strlen(msg);}/** Server TCP Socket**/TCPServer::TCPServer(unsigned short int port, const char *ip, int backlog)throw(SocketException){if (create() == false)throw SocketException("tcp server create error");if (reuseaddr() == false)throw SocketException("tcp server reuseaddr error");if (bind(port, ip) == false)throw SocketException("tcp server bind error");if (listen(backlog) == false)throw SocketException("tcp server listen error");}TCPServer::~TCPServer() {}void TCPServer::accept(TCPClient &client) constthrow(SocketException){//显式调用基类TCPSocket的acceptif (TCPSocket::accept(client) == -1)throw SocketException("tcp server accept error");}TCPClient TCPServer::accept() constthrow(SocketException){TCPClient client;if (TCPSocket::accept(client) == -1)throw SocketException("tcp server accept error");return client;}/** readn/writen实现部分 **/static ssize_t readn(int fd, void *buf, size_t count){size_t nLeft = count;ssize_t nRead = 0;char *pBuf = (char *)buf;while (nLeft > 0){if ((nRead = read(fd, pBuf, nLeft)) < 0){//如果读取操作是被信号打断了, 则说明还可以继续读if (errno == EINTR)continue;//否则就是其他错误elsereturn -1;}//读取到末尾else if (nRead == 0)return count-nLeft;//正常读取nLeft -= nRead;pBuf += nRead;}return count;}static ssize_t writen(int fd, const void *buf, size_t count){size_t nLeft = count;ssize_t nWritten = 0;char *pBuf = (char *)buf;while (nLeft > 0){if ((nWritten = write(fd, pBuf, nLeft)) < 0){//如果写入操作是被信号打断了, 则说明还可以继续写入if (errno == EINTR)continue;//否则就是其他错误elsereturn -1;}//如果 ==0则说明是什么也没写入, 可以继续写else if (nWritten == 0)continue;//正常写入nLeft -= nWritten;pBuf += nWritten;}return count;}SocketException类//SocketException类的设计与实现class SocketException{public:typedef std::string string;SocketException(const string &_msg = string()): msg(_msg) {}string what() const{if (errno == 0)return msg;//如果errno!=0, 则会加上错误描述return msg + ": " + strerror(errno);}private:string msg;};Socket类测试(echo)昨晚多几分钟的准备,今天少几小时的麻烦。

Class封装(改进版v2)

相关文章:

你感兴趣的文章:

标签云: