对原始socket最重要的一个抽象:在一个TCP连接中,涉及到3类socket,专门负责接收连接的listenner,和接收到的accepter,以及主动进行连接的connecter。各种失效的情况下,抛出 tcp_error 异常让接口更C++化,更c++化就是更人性化其实就是做一些表面文章,上代码 例子: #include <iostream> #include <string> #include <pthread.h> #include "tcpconnect.h" using namespace std; const int port = 3000; void *aaServer(void *nothing) { string str; try { TcpConnect aConnect; TcpConnect server(TcpConnect::listenner(), port); aConnect.accept(server); aConn.recv(str); cout << str << endl; } catch (const tcp_error &e) { cout << "SERVER: " << e.what() << endl; } } int main() { pthread_t tid; pthread_create(&tid, 0, aaServer, 0); sleep(2); try { TcpConnect client(TcpConnect::connecter(), "127.0.0.1", port); client.send("hello, aa"); pthread_join(tid, 0); } catch (const tcp_error &e) { cout << "CLIENT:" << e.what() << endl; } return 0; } 接口: // tcpconnect.h #include <iostream> #include <string> #include <stdexcept> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> class tcp_error : public std::logic_error { public: explicit tcp_error(const std::string &s) : std::logic_error(std::string("tcp error:") + s) { } }; class TcpConnectImpl { protected: typedef enum {connecter, listenner, accepter} ConnectType; static const ConnectType type_connecter = connecter; static const ConnectType type_listenner = listenner; static const ConnectType type_accepter = accepter; static const int maxRecvLen = 1024*1024; TcpConnectImpl(ConnectType type = accepter) : connectType(accepter) , localSocket(-1) { initForAccepter(); } TcpConnectImpl(ConnectType type, int port); TcpConnectImpl(ConnectType type, const std::string &host, int port); ~TcpConnectImpl() { std::cout << "DO RELEASE AUTOMATE,~Tcp() " << std::endl; if (localSocket > 0) { std::cout << "close(" << localSocket << ")" <<std::endl; ::close(localSocket); } } bool connect(); void accept(const TcpConnectImpl &listenner); int getSocket() const { return localSocket; }; std::size_t send(const char *buffer, int len) const; std::size_t recv(char *buffer, int len = maxRecvLen) const; void tcpSendString(const std::string &) const; void tcpRecvString(std::string &)const; // ... private: // helpers, compile controls TcpConnectImpl(const TcpConnectImpl &); const TcpConnectImpl &operator=(const TcpConnectImpl &); void initForListenner(int port); void initForAccepter(); void initForConnecter(const std::string &host, int port); bool isIpAddr(const std::string &host) const { return true; } void initSockAddrByHostOrIp(const std::string &host, int port); struct sockaddr_in peerAddr; // also represents listenner for listen const ConnectType connectType; int localSocket; int listenPort; }; // I just want a pure interface, move TcpConnectImpl::getSocket out class TcpConnect : private TcpConnectImpl { public: TcpConnect(ConnectType type = TcpConnectImpl::accepter) throw() : TcpConnectImpl() { } TcpConnect(ConnectType type, int port) throw(tcp_error) : TcpConnectImpl(type, port) { } TcpConnect(ConnectType type, const std::string &remote_host, int port) throw(tcp_error) : TcpConnectImpl(type, remote_host, port) { } static const ConnectType &connecter() throw() { static const ConnectType type_connecter = TcpConnectImpl::connecter; return type_connecter; } static const ConnectType &listenner() throw() { static const ConnectType type_listenner = TcpConnectImpl::listenner; return type_listenner; } static const ConnectType &accepter() throw() { static const ConnectType type_accepter = TcpConnectImpl::accepter; return type_accepter; } void accept(const TcpConnect &listenner) throw(tcp_error) { //TcpConnectImpl::accept(static_cast<TcpConnectImpl>(*this)); TcpConnectImpl::accept(listenner); } std::size_t send(const char *buffer, int len) const throw(tcp_error) { return TcpConnectImpl::send(buffer, len); } void send(const std::string &str) const throw(tcp_error) { TcpConnectImpl::tcpSendString(str); } std::size_t recv(char *buffer, int len = maxRecvLen) const throw(tcp_error) { return TcpConnectImpl::recv(buffer, len); } void recv(std::string &str) const throw(tcp_error) { TcpConnectImpl::tcpRecvString(str); } // to be expand ... private: TcpConnect(const TcpConnect &); TcpConnect &operator= (const TcpConnect &); }; 实现: #include <iostream> #include <string> #include "tcpconnect.h" using namespace std; TcpConnectImpl::TcpConnectImpl(ConnectType type, int port) : connectType(type) , listenPort(port) , localSocket(-1) { if (connectType == accepter) initForAccepter(); else if (connectType == listenner) initForListenner(listenPort); else throw tcp_error("no host supplied for tcp::connecter"); } TcpConnectImpl::TcpConnectImpl(ConnectType type, const string &host, int port) : connectType(type) , listenPort(port) , localSocket(-1) { if (connectType == accepter) initForAccepter(); else if (connectType == connecter) { initForConnecter(host, port); cout << "/n/nCELEBRATE: a connection esstablished" << endl; cout << "/n/tremote host :/t/t" << host << endl; cout << "/tentrance port :/t/t" << port << endl; } else if (connectType == listenner) initForListenner(port); else throw tcp_error("unsupport type of connection"); } void TcpConnectImpl::initSockAddrByHostOrIp(const string &host, int port) { if (connectType == accepter) { // need to do nothing for accepter, // more initialization is done by this->accept(listenner); return ; } peerAddr.sin_family = AF_INET; peerAddr.sin_port = htons(port); memset(peerAddr.sin_zero, 0, 8); if (connectType == listenner) peerAddr.sin_addr.s_addr = INADDR_ANY; else { const char *host_str = host.c_str(); if (isIpAddr(host)) { peerAddr.sin_addr.s_addr = inet_addr(host_str); } else { struct hostent *he = gethostbyname(host_str); if (!he) { throw tcp_error( string("domain name :") + host_str + "resolve error"); } peerAddr.sin_addr = *((struct in_addr *)he->h_addr_list[0]); // free (he) ??? } //delete host_str; } } void TcpConnectImpl::initForAccepter() { listenPort = -1; localSocket = -1; } void TcpConnectImpl::initForConnecter(const string &host, int port) { int sock; listenPort = port; initSockAddrByHostOrIp(host, port); if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { throw tcp_error("can not get sock fd"); } localSocket = sock; cout << "connect to " << inet_ntoa(peerAddr.sin_addr) << ", " << peerAddr.sin_port << endl; if (::connect(localSocket, (struct sockaddr*)(&peerAddr), sizeof(struct sockaddr_in)) < 0) { cout << "connect() error " << endl; throw tcp_error(string("connect error to") + inet_ntoa(peerAddr.sin_addr) + ", " + strerror(errno)); } } void TcpConnectImpl::initForListenner(int port) { int sock; listenPort = port; initSockAddrByHostOrIp("unused param for listenner", port); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { throw tcp_error("can not get sock fd"); } localSocket = sock; char opt = 1; int len = 1; setsockopt(localSocket, SOL_SOCKET, SO_REUSEADDR, &opt, len); // ............. if (bind(localSocket, (struct sockaddr *)&peerAddr, sizeof(struct sockaddr_in)) < 0) { throw tcp_error(string("bind error,") + strerror(errno)); } static const int backlog = 10; if (listen(localSocket, backlog) < 0) { throw tcp_error(string("bind error,") + (strerror(errno))); } } void TcpConnectImpl::accept(const TcpConnectImpl &aListenner) { int sock; socklen_t addrLen; cout << "accept ( " << aListenner.getSocket() << "," << endl; if ((sock = ::accept(aListenner.getSocket(), (struct sockaddr *)&peerAddr, &addrLen)) < 0) { throw tcp_error(string("accept() error,") + strerror(errno)); } localSocket = sock; } size_t TcpConnectImpl::send(const char *buffer, int len) const { ::ssize_t lenSend; cout << "total sends : " << len << endl; while (len > 0) { lenSend = ::send(localSocket, buffer, len, 0); cout << "send(" << len << "actual len:" << lenSend << endl; if (lenSend <= 0) throw tcp_error(string("send error") + strerror(errno)); len -= lenSend; } return lenSend; } size_t TcpConnectImpl::recv(char *buffer, int len) const { ::ssize_t lenRecv = ::recv(localSocket, buffer, len, 0); if (lenRecv < 0) throw tcp_error(string("recv error,") + strerror(errno)); cout << "recv : " << lenRecv << endl; return lenRecv; } void TcpConnectImpl::tcpSendString(const string &str) const { send(str.c_str(), str.size()); } void TcpConnectImpl::tcpRecvString(string &str) const { char *buffer = new char[maxRecvLen]; size_t size; try { size = recv(buffer); cout << "receive len:" << size << endl; } catch (tcp_error) { delete[] buffer; throw; } buffer[size] = static_cast<char>(0); str = buffer; delete[] buffer; }