C++网络编程


986 浏览 6 years, 2 months

2.1 套接字类

版权声明: 转载请注明出处 http://www.codingsoho.com/

套接字

基类是socket,派生类包括监控套接字和数据套接字

namespace SocketLib
{
    class Socket
    {
    public:
        inline sock GetSock() const
        {
            return m_sock;
        }
        inline port GetLocalPort() const
        {
            return ntohs( m_localinfo.sin_port );
        }
        inline ipaddress GetLocalAddress() const
        {
            return m_localinfo.sin_addr.s_addr;
        }
        void Close();
        void SetBlocking( bool p_blockmode );
    protected:
        Socket( sock p_socket = -1 );
        sock m_sock;                    // this is the underlying representation of the actual socket.
        struct sockaddr_in m_localinfo; // structure containing information about the local connection
        bool m_isblocking;              // this tells whether the socket is blocking or not.
    };
}   // end namespace SocketLib

变量m_isblocking是一个布尔变量,确定套接字是否阻塞

设置套接字阻塞
    void Socket::SetBlocking( bool p_blockmode )
    {
        int err;
        #ifdef WIN32
            unsigned long mode = !p_blockmode;
            err = ioctlsocket( m_sock, FIONBIO, &mode );
        #else
            // get the flags
            int flags = fcntl( m_sock, F_GETFL, 0 );
            // set or clear the non-blocking flag
            if( p_blockmode == false )
            {
                flags |= O_NONBLOCK;
            }
            else
            {
                flags &= ~O_NONBLOCK;
            }
            err = fcntl( m_sock, F_SETFL, flags );
        #endif
        if( err == -1 )
        {
            throw( Exception( GetError() ) );
        }
        m_isblocking = p_blockmode;
    }

windows和linux的设置方法是不一样的。

监听套接字
class ListeningSocket : public Socket
    {
    public:
        ListeningSocket();
        void Listen( port p_port );
        DataSocket Accept();
        inline bool IsListening() const
        {
            return m_listening;
        }
        void Close();
    protected:
        bool m_listening;               // is the socket listening?
    };
数据套接字
   class DataSocket : public Socket
    {
    public:
        DataSocket( sock p_socket = -1 );
        inline ipaddress GetRemoteAddress() const
        {
            return m_remoteinfo.sin_addr.s_addr;
        }
        inline port GetRemotePort() const
        {
            return ntohs( m_remoteinfo.sin_port );
        }
        inline bool IsConnected() const
        {
            return m_connected;
        }
        void Connect( ipaddress p_addr, port p_port );
        int Send( const char* p_buffer, int p_size );
        int Receive( char* p_buffer, int p_size );
        void Close();
    protected:
        bool m_connected;               // is the socket connected?
        struct sockaddr_in m_remoteinfo;// structure containing information about the remote connection
    };    

数据套接字是另外一个子类,也是从普通套接字继承而来,但是需要更多的数据和更复杂的函数。例如,数据套接字添加了一个sockaddr_in结构,用于连接到远程地址,由于监听套接字不能连到任何地方,所以它没有远程地址。

使用套接字
using namespace SocketLib;
ListeningSocket lsock;
DataSocket dsock;
lsock.Listen(5000);
dsock = lsock.Accept();

上面代码在端口5000上开始监听,等待流入连接。只有5行代码就完成了Socket API里的30~40行的代码。
代码执行之后,dsock包含一个套接字(如果客户机连接到了本机),能够向它发送和接收数据。

char buffer[128] = "Hello There!";
dsock.Send( buffer, strlen( buffer ));
dsock.Receive(buffer, 128);

套接字节

创建类SocketSet,用于封装select()函数

namespace SocketLib
{    
    const int MAX = FD_SETSIZE;
    class SocketSet
    {
    public:
        SocketSet();
        void AddSocket( const Socket& p_sock );
        void RemoveSocket( const Socket& p_sock );        
        inline int Poll( long p_time = 0 )
        {
            // this is the time value structure. It will determine how long
            // the select function will wait.
            struct timeval t = { 0, p_time * 1000 };
            // copy the set over into the activity set.
            m_activityset = m_set;
            // now run select() on the sockets.
            #ifdef WIN32
                return select( 0, &m_activityset, 0, 0, &t );
            #else
                if( m_socketdescs.size() == 0 ) return 0;
                return select( *(m_socketdescs.rbegin()), &m_activityset, 0, 0, &t );
            #endif
        }
        inline bool HasActivity( const Socket& p_sock )
        {
            return FD_ISSET( p_sock.GetSock(), &m_activityset ) != 0;
        }
    protected:
        // a set representing the socket descriptors.
        fd_set m_set;
        // this set will represent all the sockets that have activity on them.
        fd_set m_activityset;
        // this is only used for linux, since select() requires the largest
        // descriptor +1 passed into it. BLAH!
        #ifndef WIN32
            std::set<sock> m_socketdescs;
        #endif
    };
}   // end namespace SocketLib

C++实现

namespace SocketLib
{
SocketSet::SocketSet()
{
    FD_ZERO( &m_set );
    FD_ZERO( &m_activityset );
}
void SocketSet::AddSocket( const Socket& p_sock )
{
    // add the socket desc to the set
    FD_SET( p_sock.GetSock(), &m_set );
    // if linux, then record the descriptor into the vector, 
    // and check if it's the largest descriptor.
    #ifndef WIN32
        m_socketdescs.insert( p_sock.GetSock() );
    #endif
}
void SocketSet::RemoveSocket( const Socket& p_sock )
{
    FD_CLR( p_sock.GetSock(), &m_set );
    #ifndef WIN32
        // remove the descriptor from the vector
        m_socketdescs.erase( p_sock.GetSock() );
    #endif
}
}   // end namespace SocketSet

用法示例:

using namespace SocketLib;
DataSocket socks[3];
SocketSet sset;
sset.AddSocket( socks[0] );
sset.AddSocket( socks[1] );
sset.AddSocket( socks[2] );
int active = sset.Poll( 1000 );
if( sset.HasActivity( socks[0] ){}
sset.RemoveSocket( socks[0] );

Winsock初始化

namespace SocketLib
{
    #ifdef WIN32                // windows 95 and above
        class System
        {
        public:
            System()
            {
                // attempt to start up the winsock lib
                WSAStartup( MAKEWORD( 2, 2 ), &m_WSAData );
            }
            ~System()
            {
                // attempt to close down the winsock lib
                WSACleanup();
            }
        protected:
            // holds information about winsock
            WSADATA m_WSAData;
        };
        System g_system;
    #endif
    ipaddress GetIPAddress( const std::string p_address )
    {
        if( IsIPAddress( p_address ) )
        {
            // if the address is just a regular IP address, there's no need
            // to do a DNS lookup, so just convert the string directly into
            // its binary format.
            ipaddress addr = inet_addr( p_address.c_str() );
            // if the address is invalid, throw a HOST_NOT_FOUND exception.
            if( addr == INADDR_NONE )
            {
                throw Exception( EDNSNotFound );
            }
            // by this point, the address is valid, so return it.
            return addr;
        }
        else
        {
            // the address isn't an IP address, so we need to look it up using
            // DNS. 
            struct hostent* host = gethostbyname( p_address.c_str() );
            // if there was an error, throw an exception.
            if( host == 0 )
            {
                // get the error from h_errno.
                throw Exception( GetError( false ) );
            }
            // now perform some really wierd casting tricks to get the value.
            // h_addr is a char*, so cast it into an ipaddress*, and 
            // dereference it to get the value.
            return *((ipaddress*)host->h_addr);
        }
    }
    std::string GetIPString( ipaddress p_address )
    {
        // return a new string containing the address.
        // (god that is some ugly casting going on... stupid language)
        char* str = inet_ntoa( *((in_addr*)&p_address) );
        if( str == 0 )
        {
            return std::string( "Invalid IP Address" );
        }
        return std::string( str );
    }
    std::string GetHostNameString( ipaddress p_address )
    {
        // get the host info.
        struct hostent* host = gethostbyaddr( (char*)&p_address, 4, AF_INET );
        // if there was an error, throw an exception.
        if( host == 0 )
        {
            // get the error from h_errno.
            throw Exception( GetError( false ) );
        }
        return std::string( host->h_name );
    }
    bool IsIPAddress( const std::string p_address )
    {
        // scan through the string to see if it's a pure IP address or not.
        // basically, we assume that any string with characters other than
        // numerics and periods needs to be DNS'ed.
        for( size_t i = 0; i < p_address.length(); i++ )
        {
            if( ( p_address[i] < '0' || p_address[i] > '9' ) && 
                p_address[i] != '.' )
                return false;
        }
        return true;
    }
}

声明此系统的全局实例

System g_system;

另外包装了一些网络函数

namespace SocketLib
{
    ipaddress GetIPAddress( const std::string p_address );
    std::string GetIPString( ipaddress p_address );
    std::string GetHostNameString( ipaddress p_address );
    bool IsIPAddress( const std::string p_address );
}