日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Socket编程实践(7) --Socket-Class封装(改进版v2)

發布時間:2025/3/17 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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類設計

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; //數據部分的長度(網絡字節序)char text[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);//send size_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(); } //receive size_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) const throw(SocketException) {//顯式調用基類TCPSocket的acceptif (TCPSocket::accept(client) == -1)throw SocketException("tcp server accept error"); } TCPClient TCPServer::accept() const throw(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)

Server端測試代碼

void sigHandler(int signo) {while (waitpid(-1, NULL, WNOHANG) > 0); }int main() {signal(SIGCHLD, sigHandler);signal(SIGPIPE, SIG_IGN);try{TCPServer server(8001);std::string msg;while (true){TCPClient client = server.accept();pid_t pid = fork();if (pid == -1)err_exit("fork error");else if (pid > 0)client.close();else if (pid == 0){try{while (true){client.receive(msg);cout << msg << endl;client.send(msg);}}catch (const SocketException &e){cerr << e.what() << endl;exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}}}catch (const SocketException &e){cerr << e.what() << endl;exit(EXIT_FAILURE);} }

Client端測試代碼

int main() {signal(SIGPIPE, SIG_IGN);try{TCPClient client(8001, "127.0.0.1");std::string msg;while (getline(cin, msg)){client.send(msg);msg.clear();client.receive(msg);cout << msg << endl;msg.clear();}}catch (const SocketException &e){cerr << e.what() << endl;} }

Makefile

.PHONY: clean all CC = g++ CPPFLAGS = -Wall -g -pthread -std=c++11 BIN = serverclient SOURCES = $(BIN.=.cpp)all: $(BIN) $(BIN): $(SOURCES) Socket.cppclean:-rm -rf $(BIN) bin/ obj/ core


總結

以上是生活随笔為你收集整理的Socket编程实践(7) --Socket-Class封装(改进版v2)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。