Socket 物件
這是一個socket物件
特色如下
1. 可在Windows與Unix-Like的作業系統上編譯
2. 發生錯誤時可以自行拋出異常,並標註異常原因
3. 由網路卡的buffer中讀取資料完畢後記憶體可以自行回收
4. 將一些常用的Option獨立成一個Method ( Option的部分會陸續更新 )
標頭檔
#ifndef __SOCKETCLASS__ #define __SOCKETCLASS__ #ifdef _MSC_VER ///////////////////////////////////////////// #include <Winsock2.h> #include <process.h> #include <direct.h> #include <time.h> #pragma comment(lib, "ws2_32") #define socklen_t int ///////////////////////////////////////////// #else ///////////////////////////////////////////// #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/stat.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #include <signal.h> #include <errno.h> #include <time.h> //#include <stdlib.h> //#ifndef socklen_t //#define socklen_t int //#endif ///////////////////////////////////////////// #endif #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <iostream> #include <typeinfo> #include <math.h> #include <string.h> namespace Net{ class Socket { private: int m_socket; fd_set m_socket_set; Socket(const Socket&); Socket& operator= (const Socket&); inline SocketException Exception(); public: Socket():m_socket(-1){} Socket(int socket):m_socket(socket){} virtual ~Socket(){}; void Connect(int domain, const char* ip, int port); //void Bind(int domain, const char* ip, int port); void Bind(int domain, int port); void Create(int domain, int type, int protocol); int Accept(); void Listen(int backlog); bool Select(double waitsecond=0); int Recv(char* buf, int len, int flags) {return recv(m_socket, buf, len, flags);} int Send(const char* buf, int len, int flags) {return send(m_socket, buf, len, flags);} /*int RecvFrom(char* buf, int len, int flags) {return recvfrom(m_socket, buf, len, flags);} int SendTo(char* buf, int len, int flags) {return sendto(m_socket, buf, len, flags);}*/ void SetOption(int level, int opt, const char* optval, int optlen); void GetOption(int level, int opt, char* optval, int* optlen); int IOControl(int opt, int* val); int ReceivedBuffSize(); bool Disconnected(); int Handle() {return m_socket;} std::string RemoteIP(); std::string LocalIP(); int RemotePort(); int LocalPort(); bool IsValid(); void Close(); bool TcpNodelay(); void TcpNodelay(bool on); bool ReuseAddress(); void ReuseAddress(bool on); int SendBuffer(); void SendBuffer(int size); int RecvBuffer(); void RecvBuffer(int size); }; }
實作
int Socket::RecvBuffer() { int _size = 0; int _len =sizeof(_size); GetOption(SOL_SOCKET, SO_RCVBUF, (char*)&_size, &_len); //if(_rc!=0)throw SocketException("Get RecvBuffer Failed", _rc); return _size; } void Socket::RecvBuffer(int size) { SetOption(SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(int)); //if(_rc!=0)throw SocketException("Set RecvBuffer Failed", _rc); } int Socket::SendBuffer() { int _size = 0; int _len =sizeof(_size); GetOption(SOL_SOCKET, SO_SNDBUF, (char*)&_size, &_len); //if(_rc!=0)throw SocketException("Get SendBuffer Failed", _rc); return _size; } void Socket::SendBuffer(int size) { SetOption(SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(int)); //if(_rc!=0)throw SocketException("Set SendBuffer Failed", _rc); } bool Socket::TcpNodelay() { char _on = 1; int _len =sizeof(_on); GetOption(IPPROTO_TCP,TCP_NODELAY,(char*)&_on, &_len); //if(_rc!=0)throw SocketException("Get TcpNodelay Failed", _rc); return _on == 1; } void Socket::TcpNodelay(bool on) { char _on = 0; if(on)_on = 1; SetOption(IPPROTO_TCP,TCP_NODELAY,(char*)&_on,sizeof(_on)); //if(_rc!=0)throw SocketException("Set TcpNodelay Failed", _rc); } bool Socket::ReuseAddress() { int _on = 1; int _len =sizeof(_on); GetOption(SOL_SOCKET,SO_REUSEADDR,(char*)&_on, &_len); //if(_rc!=0)throw SocketException("Get ReuseAddress Failed", _rc); return _on == 1; } void Socket::ReuseAddress(bool on) { int _on = 0; if(on)_on = 1; SetOption(SOL_SOCKET,SO_REUSEADDR,(char*)&_on,sizeof(_on)); //if(_rc!=0)throw SocketException("Set ReuseAddress Failed", _rc); } bool Socket::Select(double waitsecond) { FD_ZERO(&m_socket_set); FD_SET(m_socket, &m_socket_set); double _sec = floor(waitsecond); struct timeval timeout; timeout.tv_sec = (long)_sec; timeout.tv_usec = (long)(waitsecond-_sec)*1000*1000; int _rc = ::select(FD_SETSIZE, &m_socket_set, NULL, NULL, &timeout); if(_rc<0) throw Exception(); if(_rc==0) return false; if(FD_ISSET( m_socket, &m_socket_set)>0 ) return true; else return false; } int Socket::ReceivedBuffSize() { int _val = 0; int _rc = IOControl(FIONREAD, &_val); if(_rc!=0) throw Exception(); return _val; } int Socket::IOControl(int opt, int* val) { #ifdef _MSC_VER return ioctlsocket(m_socket, opt, (u_long*)val); #else return ioctl(m_socket, opt, val); #endif } void Socket::SetOption(int level, int opt, const char* optval, int optlen) { #ifdef _MSC_VER int _rc = setsockopt( m_socket, level, opt, optval, optlen); #else int _rc = setsockopt( m_socket, level, opt, optval, optlen); #endif if(_rc!=0)throw Exception(); } void Socket::GetOption(int level, int opt, char* optval, int* optlen) { #ifdef _MSC_VER int _rc = getsockopt( m_socket, level, opt, optval, optlen); #else int _rc = getsockopt( m_socket, level, opt, optval, (socklen_t*)optlen); #endif if(_rc!=0)throw Exception(); } void Socket::Connect(int domain, const char* ip, int port) { struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = domain; addr.sin_port = htons( port ); addr.sin_addr.s_addr = inet_addr( ip ); int _rc = connect( m_socket, reinterpret_cast < sockaddr* > ( &addr ), sizeof( addr ) ); if(_rc!=0) throw Exception(); } void Socket::Bind(int domain, int port) { struct sockaddr_in addr; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = domain; addr.sin_port = htons( port ); addr.sin_addr.s_addr = INADDR_ANY; int _rc = bind(m_socket, reinterpret_cast < sockaddr* > ( &addr ), sizeof( addr ) ); if(_rc!=0) throw Exception(); } void Socket::Listen(int backlog) { int _rc = listen(m_socket, backlog); if(_rc!=0) throw Exception(); } int Socket::Accept() { int _rc = accept( m_socket, 0, 0 ); if(_rc<0)throw Exception(); return _rc; } void Socket::Create(int domain, int type, int protocol) { m_socket = socket( domain, type, protocol ); if(m_socket<0)throw Exception(); } bool Socket::Disconnected() { char byte; return recv(m_socket, &byte, sizeof (byte), MSG_PEEK) <= 0; } void Socket::Close() { shutdown( m_socket, 2 ); #ifdef _MSC_VER closesocket( m_socket ); #else close( m_socket ); #endif } bool Socket::IsValid() { #ifdef _MSC_VER return m_socket != INVALID_SOCKET; #else return m_socket >= 0; #endif } SocketException Socket::Exception() { #ifdef _MSC_VER int _err = WSAGetLastError(); char _szError[256]; memset(_szError, 0, sizeof(_szError)); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, _err, 0, _szError, sizeof(_szError), NULL); SocketException _errExc(_szError, _err); #else int _err = errno; SocketException _errExc(_err); #endif return _errExc; } std::string Socket::RemoteIP() { struct sockaddr_in _addr; memset(&_addr, 0, sizeof(struct sockaddr_in)); socklen_t _size = sizeof(_addr); if( getpeername( m_socket,(struct sockaddr*)&_addr, &_size)<0) throw Exception(); //return "unknown"; char* _ip = inet_ntoa( _addr.sin_addr ); std::string _szIP(_ip); return _szIP; } std::string Socket::LocalIP() { struct sockaddr_in _addr; memset(&_addr, 0, sizeof(struct sockaddr_in)); socklen_t _size = sizeof(_addr); if( getsockname( m_socket,(struct sockaddr*)&_addr, &_size)<0) throw Exception(); //return "unknown"; char* _ip = inet_ntoa( _addr.sin_addr ); std::string _szIP(_ip); return _szIP; } int Socket::RemotePort() { struct sockaddr_in _addr; memset(&_addr, 0, sizeof(struct sockaddr_in)); socklen_t _size = sizeof(_addr); if( getpeername( m_socket,(struct sockaddr*)&_addr, &_size)<0) throw Exception(); //return -1; return ntohs(_addr.sin_port); } int Socket::LocalPort() { struct sockaddr_in _addr; memset(&_addr, 0, sizeof(struct sockaddr_in)); socklen_t _size = sizeof(_addr); if( getsockname( m_socket,(struct sockaddr*)&_addr, &_size)<0) throw Exception(); //return -1; return ntohs(_addr.sin_port); }
初始化socket的函式
namespace Net{ void Initial() { #ifdef _MSC_VER WORD version = MAKEWORD( 2, 2 ); WSADATA data; WSAStartup( version, &data ); #else struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset( &sa.sa_mask ); sa.sa_flags = 0; sigaction( SIGPIPE, &sa, 0 ); #endif } void Terminate() { #ifdef _MSC_VER WSACleanup(); #endif } }
異常情況下拋出的Exception
namespace Net{ class SocketException : public std::exception { private: int m_errno; std::string m_errmsg; public: SocketException(int error_no) :m_errno(error_no),m_errmsg(strerror(error_no)){} SocketException(const std::string& errmsg, int error_no=0) :m_errno(error_no),m_errmsg(errmsg){ } virtual ~SocketException()throw(){} virtual const char* what() const throw(){return m_errmsg.c_str();} int no(){return m_errno;} }; }
讀取網路卡buffer的物件
namespace Net{ class DataBlock { private: char* m_ptr; public: DataBlock(int size){m_ptr = new char[size];} virtual ~DataBlock(){delete[] m_ptr;} operator char*(){return m_ptr;} char* operator[](int i){return &m_ptr[i];} }; }