Socket編程實踐(7) --Socket-Class封裝(改進版v2)

本篇博客定義一套用於TCP通信比較實用/好用Socket類庫(運用C++封裝的思想,將socket API儘量封裝的好用與實用), 從開發出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~)中, 下載鏈接見下:

http://download.csdn.net/detail/hanqing280441589/8486489

Socket類

TCPSocket/TCPClient/TCPServer類設計

  1. class TCPSocket  
  2. {  
  3. protected:  
  4.     TCPSocket();  
  5.     virtual ~TCPSocket();  
  6.   
  7.     bool create();  
  8.     bool bind(unsigned short int port, const char *ip = NULL) const;  
  9.     bool listen(int backlog = SOMAXCONN) const;  
  10.     bool accept(TCPSocket &clientSocket) const;  
  11.     bool connect(unsigned short int port, const char *ip) const;  
  12.   
  13.     /**注意: TCPSocket基類並沒有send/receive方法**/  
  14.   
  15.     bool reuseaddr() const;  
  16.     bool isValid() const  
  17.     {  
  18.         return (m_sockfd != -1);  
  19.     }  
  20. public:  
  21.     bool close();  
  22.     int getfd() const  
  23.     {  
  24.         return m_sockfd;  
  25.     }  
  26.     //flag: true=SetNonBlock, false=SetBlock  
  27.     bool setNonBlocking(bool flag) const;  
  28.   
  29. protected:  
  30.     int m_sockfd;  
  31. };  
  1. /** TCP Client **/  
  2. class TCPClient : public TCPSocket  
  3. {  
  4. private:  
  5.     struct Packet  
  6.     {  
  7.         unsigned int    msgLen;     //數據部分的長度(網絡字節序)  
  8.         char            text[1024]; //報文的數據部分  
  9.     };  
  10. public:  
  11.     TCPClient(unsigned short int port, const char *ip) throw(SocketException);  
  12.     TCPClient();  
  13.     TCPClient(int clientfd);  
  14.     ~TCPClient();  
  15.   
  16.     size_t send(const std::string& message) const throw(SocketException);  
  17.     size_t receive(std::string& message) const throw(SocketException);  
  18.     size_t read(void *buf, size_t count) throw(SocketException);  
  19.     void   write(const void *buf, size_t count) throw(SocketException);  
  20.     size_t write(const char *msg) throw(SocketException);  
  21. };  
  1. /** TCP Server **/  
  2. class TCPServer : public TCPSocket  
  3. {  
  4. public:  
  5.     TCPServer(unsigned short int port, const char *ip = NULL, int backlog = SOMAXCONN) throw(SocketException);  
  6.     ~TCPServer();  
  7.     void accept(TCPClient &client) const throw(SocketException);  
  8.     TCPClient accept() const throw(SocketException);  
  9. };  

TCPSocket/TCPClient/TCPServer類實現

  1. TCPSocket::TCPSocket(): m_sockfd(-1) {}  
  2. TCPSocket::~TCPSocket()  
  3. {  
  4.     if (isValid())  
  5.         ::close(m_sockfd);  
  6. }  
  7.   
  8. bool TCPSocket::create()  
  9. {  
  10.     if (isValid())  
  11.         return false;  
  12.   
  13.     if ((m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  14.         return false;  
  15.     return true;  
  16. }  
  17.   
  18. bool TCPSocket::bind(unsigned short int port, const char *ip) const  
  19. {  
  20.     if (!isValid())  
  21.         return false;  
  22.   
  23.     struct sockaddr_in addr;  
  24.     addr.sin_family = AF_INET;  
  25.     addr.sin_port = htons(port);  
  26.     if (ip == NULL)  
  27.         addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  28.     else  
  29.         addr.sin_addr.s_addr = inet_addr(ip);  
  30.     if ( ::bind(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1 )  
  31.         return false;  
  32.     return true;  
  33. }  
  34. bool TCPSocket::listen(int backlog) const  
  35. {  
  36.     if (!isValid())  
  37.         return false;  
  38.   
  39.     if ( ::listen(m_sockfd, backlog) == -1)  
  40.         return false;  
  41.     return true;  
  42. }  
  43. bool TCPSocket::accept(TCPSocket &clientSocket) const  
  44. {  
  45.     if (!isValid())  
  46.         return false;  
  47.   
  48.     clientSocket.m_sockfd =  
  49.         ::accept(this->m_sockfd, NULL, NULL);  
  50.     if (clientSocket.m_sockfd == -1)  
  51.         return false;  
  52.     return true;  
  53. }  
  54.   
  55. bool TCPSocket::connect(unsigned short int port, const char *ip) const  
  56. {  
  57.     if (!isValid())  
  58.         return false;  
  59.   
  60.     struct sockaddr_in addr;  
  61.     addr.sin_family = AF_INET;  
  62.     addr.sin_port = htons(port);  
  63.     addr.sin_addr.s_addr = inet_addr(ip);  
  64.     if ( ::connect(m_sockfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)  
  65.         return false;  
  66.     return true;  
  67. }  
  68.   
  69. bool TCPSocket::setNonBlocking(bool flag) const  
  70. {  
  71.     if (!isValid())  
  72.         return false;  
  73.     int opt = fcntl(m_sockfd, F_GETFL, 0);  
  74.     if (opt == -1)  
  75.         return false;  
  76.     if (flag)  
  77.         opt |= O_NONBLOCK;  
  78.     else  
  79.         opt &= ~O_NONBLOCK;  
  80.     if (fcntl(m_sockfd, F_SETFL, opt) == -1)  
  81.         return false;  
  82.     return true;  
  83. }  
  84. bool TCPSocket::reuseaddr() const  
  85. {  
  86.     if (!isValid())  
  87.         return false;  
  88.   
  89.     int on = 1;  
  90.     if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)  
  91.         return false;  
  92.     return true;  
  93. }  
  94. bool TCPSocket::close()  
  95. {  
  96.     if (!isValid())  
  97.         return false;  
  98.     ::close(m_sockfd);  
  99.     m_sockfd = -1;  
  100.     return true;  
  101. }  
  1. /** client TCP Socket **/  
  2. TCPClient::TCPClient(unsigned short int port, const char *ip)  
  3. throw(SocketException)  
  4. {  
  5.     if (create() == false)  
  6.         throw SocketException("tcp client create error");  
  7.     if (connect(port, ip) == false)  
  8.         throw SocketException("tcp client connect error");  
  9. }  
  10. TCPClient::TCPClient() {}  
  11. TCPClient::TCPClient(int clientfd)  
  12. {  
  13.     m_sockfd = clientfd;  
  14. }  
  15. TCPClient::~TCPClient() {}  
  16. /** client端特有的send/receive **/  
  17. static ssize_t readn(int fd, void *buf, size_t count);  
  18. static ssize_t writen(int fd, const void *buf, size_t count);  
  19.   
  20. //send  
  21. size_t TCPClient::send(const std::string& message)  
  22. const throw(SocketException)  
  23. {  
  24.     Packet buf;  
  25.     buf.msgLen = htonl(message.length());  
  26.     strcpy(buf.text, message.c_str());  
  27.     if (writen(m_sockfd, &buf, sizeof(buf.msgLen)+message.length()) == -1)  
  28.         throw SocketException("tcp client writen error");  
  29.     return message.length();  
  30. }  
  31. //receive  
  32. size_t TCPClient::receive(std::string& message)  
  33. const throw(SocketException)  
  34. {  
  35.     //首先讀取頭部  
  36.     Packet buf = {0, 0};  
  37.     size_t readBytes = readn(m_sockfd, &buf.msgLen, sizeof(buf.msgLen));  
  38.     if (readBytes == (size_t)-1)  
  39.         throw SocketException("tcp client readn error");  
  40.     else if (readBytes != sizeof(buf.msgLen))  
  41.         throw SocketException("peer connect closed");  
  42.   
  43.     //然後讀取數據部分  
  44.     unsigned int lenHost = ntohl(buf.msgLen);  
  45.     readBytes = readn(m_sockfd, buf.text, lenHost);  
  46.     if (readBytes == (size_t)-1)  
  47.         throw SocketException("tcp client readn error");  
  48.     else if (readBytes != lenHost)  
  49.         throw SocketException("peer connect closed");  
  50.     message = buf.text;  
  51.     return message.length();  
  52. }  
  53. size_t TCPClient::read(void *buf, size_t count) throw(SocketException)  
  54. {  
  55.     ssize_t readBytes = ::read(m_sockfd, buf, count);  
  56.     if (readBytes == -1)  
  57.         throw SocketException("tcp client read error");  
  58.     return (size_t)readBytes;  
  59. }  
  60. void TCPClient::write(const void *buf, size_t count) throw(SocketException)  
  61. {  
  62.     if ( ::write(m_sockfd, buf, count) == -1 )  
  63.         throw SocketException("tcp client write error");  
  64. }  
  65. size_t TCPClient::write(const char *msg) throw(SocketException)  
  66. {  
  67.     if ( ::write(m_sockfd, msg, strlen(msg)) == -1 )  
  68.         throw SocketException("tcp client write error");  
  69.     return strlen(msg);  
  70. }  
  1. /** Server TCP Socket**/  
  2. TCPServer::TCPServer(unsigned short int port, const char *ip, int backlog)  
  3. throw(SocketException)  
  4. {  
  5.     if (create() == false)  
  6.         throw SocketException("tcp server create error");  
  7.     if (reuseaddr() == false)  
  8.         throw SocketException("tcp server reuseaddr error");  
  9.     if (bind(port, ip) == false)  
  10.         throw SocketException("tcp server bind error");  
  11.     if (listen(backlog) == false)  
  12.         throw SocketException("tcp server listen error");  
  13. }  
  14. TCPServer::~TCPServer() {}  
  15. void TCPServer::accept(TCPClient &client) const  
  16. throw(SocketException)  
  17. {  
  18.     //顯式調用基類TCPSocket的accept  
  19.     if (TCPSocket::accept(client) == -1)  
  20.         throw SocketException("tcp server accept error");  
  21. }  
  22. TCPClient TCPServer::accept() const  
  23. throw(SocketException)  
  24. {  
  25.     TCPClient client;  
  26.     if (TCPSocket::accept(client) == -1)  
  27.         throw SocketException("tcp server accept error");  
  28.     return client;  
  29. }  
  1. /** readn/writen實現部分 **/  
  2. static ssize_t readn(int fd, void *buf, size_t count)  
  3. {  
  4.     size_t nLeft = count;  
  5.     ssize_t nRead = 0;  
  6.     char *pBuf = (char *)buf;  
  7.     while (nLeft > 0)  
  8.     {  
  9.         if ((nRead = read(fd, pBuf, nLeft)) < 0)  
  10.         {  
  11.             //如果讀取操作是被信號打斷了, 則說明還可以繼續讀  
  12.             if (errno == EINTR)  
  13.                 continue;  
  14.             //否則就是其他錯誤  
  15.             else  
  16.                 return -1;  
  17.         }  
  18.         //讀取到末尾  
  19.         else if (nRead == 0)  
  20.             return count-nLeft;  
  21.   
  22.         //正常讀取  
  23.         nLeft -= nRead;  
  24.         pBuf += nRead;  
  25.     }  
  26.     return count;  
  27. }  
  28. static ssize_t writen(int fd, const void *buf, size_t count)  
  29. {  
  30.     size_t nLeft = count;  
  31.     ssize_t nWritten = 0;  
  32.     char *pBuf = (char *)buf;  
  33.     while (nLeft > 0)  
  34.     {  
  35.         if ((nWritten = write(fd, pBuf, nLeft)) < 0)  
  36.         {  
  37.             //如果寫入操作是被信號打斷了, 則說明還可以繼續寫入  
  38.             if (errno == EINTR)  
  39.                 continue;  
  40.             //否則就是其他錯誤  
  41.             else  
  42.                 return -1;  
  43.         }  
  44.         //如果 ==0則說明是什麼也沒寫入, 可以繼續寫  
  45.         else if (nWritten == 0)  
  46.             continue;  
  47.   
  48.         //正常寫入  
  49.         nLeft -= nWritten;  
  50.         pBuf += nWritten;  
  51.     }  
  52.     return count;  
  53. }  

SocketException類

  1. //SocketException類的設計與實現  
  2. class SocketException  
  3. {  
  4. public:  
  5.     typedef std::string string;  
  6.     SocketException(const string &_msg = string())  
  7.         : msg(_msg) {}  
  8.     string what() const  
  9.     {  
  10.         if (errno == 0)  
  11.             return msg;  
  12.         //如果errno!=0, 則會加上錯誤描述  
  13.         return msg + ": " + strerror(errno);  
  14.     }  
  15.   
  16. private:  
  17.     string msg;  
  18. };  

Socket類測試(echo)

Server端測試代碼

  1. void sigHandler(int signo)  
  2. {  
  3.     while (waitpid(-1, NULL, WNOHANG) > 0)  
  4.         ;  
  5. }  
  6.   
  7. int main()  
  8. {  
  9.     signal(SIGCHLD, sigHandler);  
  10.     signal(SIGPIPE, SIG_IGN);  
  11.     try  
  12.     {  
  13.         TCPServer server(8001);  
  14.         std::string msg;  
  15.         while (true)  
  16.         {  
  17.             TCPClient client = server.accept();  
  18.             pid_t pid = fork();  
  19.             if (pid == -1)  
  20.                 err_exit("fork error");  
  21.             else if (pid > 0)  
  22.                 client.close();  
  23.             else if (pid == 0)  
  24.             {  
  25.                 try  
  26.                 {  
  27.                     while (true)  
  28.                     {  
  29.                         client.receive(msg);  
  30.                         cout << msg << endl;  
  31.                         client.send(msg);  
  32.                     }  
  33.                 }  
  34.                 catch (const SocketException &e)  
  35.                 {  
  36.                     cerr << e.what() << endl;  
  37.                     exit(EXIT_FAILURE);  
  38.                 }  
  39.                 exit(EXIT_SUCCESS);  
  40.             }  
  41.         }  
  42.     }  
  43.     catch (const SocketException &e)  
  44.     {  
  45.         cerr << e.what() << endl;  
  46.         exit(EXIT_FAILURE);  
  47.     }  
  48. }  

Client端測試代碼

  1. int main()  
  2. {  
  3.     signal(SIGPIPE, SIG_IGN);  
  4.     try  
  5.     {  
  6.         TCPClient client(8001, "127.0.0.1");  
  7.         std::string msg;  
  8.         while (getline(cin, msg))  
  9.         {  
  10.             client.send(msg);  
  11.             msg.clear();  
  12.             client.receive(msg);  
  13.             cout << msg << endl;  
  14.             msg.clear();  
  15.         }  
  16.     }  
  17.     catch (const SocketException &e)  
  18.     {  
  19.         cerr << e.what() << endl;  
  20.     }  
  21. }  

Makefile

[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. .PHONY: clean all   
  2. CC = g++   
  3. CPPFLAGS = -Wall -g -pthread -std=c++11  
  4. BIN = serverclient  
  5. SOURCES = $(BIN.=.cpp)  
  6.   
  7. all: $(BIN)  
  8. $(BIN): $(SOURCES) Socket.cpp  
  9.   
  10. clean:  
  11.     -rm -rf $(BIN) bin/ obj/ core  
發佈了52 篇原創文章 · 獲贊 10 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章