Socket 物件 Part 1

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];}
};
}