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