一 点睛
Socket的定义和实现分别在文件Socket.hpp和Socket.cpp。
它的主要功能是封装socket文件描述符、此Socket对应的端口号以及socket接口中的listen、accept、connect和close等函数,为用户提供了一个简单易用而又同一的接口。同时作为其他派生类的基类。
二 类定义
class Socket {
public:
// stores server port and TCP/UDP mode
Socket( unsigned short inPort, bool inUDP = false );
// destructor
virtual ~Socket();
protected:
// get local address
SocketAddr getLocalAddress( void );
// get remote address
SocketAddr getRemoteAddress( void );
// server bind and listen
void Listen( const char *inLocalhost = NULL, bool isIPv6 = false );
// server accept
int Accept( void );
// client connect
void Connect( const char *inHostname, const char *inLocalhost = NULL );
// close the socket
void Close( void );
// to put setsockopt calls before the listen() and connect() calls
virtual void SetSocketOptions( void ) {
}
// join the multicast group
void McastJoin( SocketAddr &inAddr );
// set the multicast ttl
void McastSetTTL( int val, SocketAddr &inAddr );
int mSock; // socket file descriptor (sockfd)
unsigned short mPort; // port to listen to
bool mUDP; // true for UDP, false for TCP
}; // end class Socket
#endif // SOCKET_H
该类主要提供了4个函数: Listen、Accept、Connect、Close。
getLocalAddress和getRemoteAddress的作用分别是获得Socket本端的地址和对端的地址,两个地址返回一个SocketAddr实例。
SetSocketOptions的作用是设置Socket的属性,它是一个虚函数,因此不同Socket的派生类在实现此函数时会执行不同的操作。
三 类的实现
1 Listen函数
void Socket::Listen( const char *inLocalhost, bool isIPv6 ) {
int rc;
SocketAddr serverAddr( inLocalhost, mPort, isIPv6 );
// create an internet TCP socket
int type = (mUDP ? SOCK_DGRAM : SOCK_STREAM);
int domain = (serverAddr.isIPv6() ?
#ifdef IPV6
AF_INET6
#else
AF_INET
#endif
: AF_INET);
mSock = socket( domain, type, 0 );
FAIL_errno( mSock == INVALID_SOCKET, "socket" );
SetSocketOptions();
// reuse the address, so we can run if a former server was killed off
int boolean = 1;
Socklen_t len = sizeof(boolean);
// this (char*) cast is for old headers that don't use (void*)
setsockopt( mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
// bind socket to server address
rc = bind( mSock, serverAddr.get_sockaddr(), serverAddr.get_sizeof_sockaddr());
FAIL_errno( rc == SOCKET_ERROR, "bind" );
// listen for connections (TCP only).
// default backlog traditionally 5
if ( ! mUDP ) {
rc = listen( mSock, 5 );
FAIL_errno( rc == SOCKET_ERROR, "listen" );
}
} // end Listen
Socket的Listen方法将地址解析、socket、bind和listen等系统调用组合为一个函数。在应用时,调用一个Listen方法就可以完成Server端socket初始化的所有工作。
2 Accept函数
int Socket::Accept( void ) {
iperf_sockaddr clientAddr;
Socklen_t addrLen;
int connectedSock;
while ( true ) {
// accept a connection
addrLen = sizeof( clientAddr );
connectedSock = accept( mSock, (struct sockaddr*) &clientAddr, &addrLen );
// handle accept being interupted
if ( connectedSock == INVALID_SOCKET && errno == EINTR ) {
continue;
}
return connectedSock;
}
} // end Accept
Accept函数为Accept系统调用增添了在中断后自动重启的功能。Server线程在执行Accept函数后被阻塞,直到有请求到达,或者接收到某个信号。若是后面一种情况,Accept会返回INVALID_SOCKET,并设置 errno 为EINTR。Accept方法检查这种情况,并重新调用Accept函数。
3 Connect函数
void Socket::Connect( const char *inHostname, const char *inLocalhost ) {
int rc;
SocketAddr serverAddr( inHostname, mPort );
assert( inHostname != NULL );
// create an internet socket
int type = (mUDP ? SOCK_DGRAM : SOCK_STREAM);
int domain = (serverAddr.isIPv6() ?
#ifdef IPV6
AF_INET6
#else
AF_INET
#endif
: AF_INET);
mSock = socket( domain, type, 0 );
FAIL_errno( mSock == INVALID_SOCKET, "socket" );
SetSocketOptions();
if ( inLocalhost != NULL ) {
SocketAddr localAddr( inLocalhost );
// bind socket to local address
rc = bind( mSock, localAddr.get_sockaddr(), localAddr.get_sizeof_sockaddr());
FAIL_errno( rc == SOCKET_ERROR, "bind" );
}
// connect socket
rc = connect( mSock, serverAddr.get_sockaddr(), serverAddr.get_sizeof_sockaddr());
FAIL_errno( rc == SOCKET_ERROR, "connect" );
} // end Connect
Connect函数是Client端调用的函数,作用是连接指定的Server。