Monday, May 07, 2007

SOCKET的封装(Java的Socket类的C++实现)

SOCKET 的封装


C++ 通用框架的设计 作者: naven


1           SOCKET 封装介绍


Socket 通讯程序估计现在大多数应用系统都会涉及到,因为现在的世界是一个由 Internet 网络连接的世界,任何个人电脑或服务都可能会有数据交换,通过 TCP/UDP 这样的 Socket 通讯协议进行联系。开发这样的通讯程序会是很普遍也很类似,每种操作系统都实现了 Socket 通讯库并提供很相似的 API ,通讯步骤都遵循 RFC 规范。但是有些 API 的具体接口却稍有不同,比如 Socket IO API win32 系统和 Unix 系统就不一样, Win32 recv/send Unix 使用标准统一的 read/write ,而且 socket 句柄在不同操作系统处理也不一样,等等这些都造成编写跨平台的 Socket 应用不太容易。另外编写服务器和客户端的处理步骤也很繁琐, IP 地址和域名的转换也很麻烦,所以实现一个标准统一使用更简洁的 API 非常有用。本 C++ 框架基本参考 Java Socket 相关类实现了类似封装,除了 ServerSocket 实现稍有不同,其他原理和方法基本类似。用它编写网络应用基本不用考虑底层的协议处理,使用非常容易,代码也更简洁易读。


 


主要有如下一些类


 


class Socket                                                    代表一个 TCP 连接的 Socket 对象


         class DatagramSocket                                    代表一个 UDP 连接的 Socket 对象(暂未实现)


         class MulticastSocket                                    一个 DatagramSocket 的子类用于多播(暂未实现)


class SocketAcceptor                                    一个 TCP 服务端的接收器


class SocketConnector                                  一个 TCP 客户端的连接器


         class SocketInputStream                               一个 Socket 连接的输入流


         class SocketOutputStream                            一个 Socket 连接的输出流


         class SocketReader                                        一个 Socket 连接的读操作器


         class SocketWriter                                          一个 Socket 连接的写操作器


 


Socket 的意思是在网络的机器之间建立一个通信线路。通过 TCP Sockets 发送或接收的操作时通过 InputStream OutputStream 流处理的, Socket 类的 getInputStream getOutputStream 可以取得该 Socket 连接的输入 / 输出流对象。 SocketAcceptor 是用于服务器端的接收器,它处于接收的状态时会阻塞,直到接收到客户端的连接请求,这时就会创建一个 Socket 对象代表该服务器到此客户端的连接。而对应的 SocketConnector 是用于客户端的连接器,它向服务端发出连接请求,如果服务器允许连接,则也同时建立一个 Socket 对象表示它到服务器端的连接,这时就可以获取输入 / 输出流对象进行 Socket 通讯了。


2           Hello World!


下面的程序示例如何用上面的类进行 Scoket 通讯:


这是服务端的实现


void servertest() 
{
    
// 定义一个接收器绑定 8000 端口
    SocketAcceptor sa(8000); 
    
while(1
{
       
// 阻塞等待连接请求
        Socket sk = sa.accept(); 
        
// 获取此连接的读操作器和写操作器
        Reader &rd = sk.getReader(); 
        Writer 
&wr = sk.getWriter(); 
        String s; 
        
// 从客户端读取 10 个字节
        rd.read(s, 10); 
        
// 向客户端写信息
        wr.write(“read ok”); 
        
// 关闭连接
        sk.close(); 
}
}



这是客户端的实现



void clienttest()
{
    
// 定义一个连接器
SocketConnector sc; 
// 连接指定的服务器地址及端口, 返回一个Socket对象
Socket sk = sc.connect(“localhost”, 8000); 
// 如果已成功连上
    if( sk.isConnected() ) 
{
    
// 获取此连接的读操作器和写操作器
        Reader& reader = sk.getReader(); 
        Writer
& writer = sk.getWriter(); 
        
// 可以在读操作器上建立一个带缓冲的读操作器
        BufferedReader rd(reader);
        
// 向服务器发送信息
        writer.write(“hello server”);
        
// 接收信息, 带缓冲的读操作器可以读取一行
        rd.readLine(s);
        
// 关闭连接
        sk.close();
    }
}




1           Socket


此类定义一个表示 Socket 连接的类,一个 Socket 是一个为在两台机器间通信的端点。一个 Socket 类的真实行为是通过一个 SocketImpl 类的诗体执行的。一个应用程序可以通过改变 Socket Factory 来创建 Socket 的具体实现,以适应本地的局域网防火墙。


Socket 类成员和主要接口定义如下:



class Socket : public AbstractFile 
{
protected
    
/**
     * The implementation of this Socket.
     
*/
    SocketImplAutoPtr _impl; 

    
/**
     * Various states of this socket.
     
*/
    BOOL _connected; 
    BOOL _closed; 
    BOOL _shutIn; 
BOOL _shutOut;

public
    
/**
     * Creates an unconnected socket, with the
     * system-default type of SocketImpl.
     
*/
Socket();

    
/**
     * Returns an input stream for this socket.
     *
     * @return     an input stream for reading bytes from this socket.
     
*/
    InputStream
& getInputStream();

    
/**
     * Gets an Reader for this socket.
     
*/
    Reader
& getReader();

    
/**
     * Returns an output stream for this socket.
     *
     * @return     an output stream for writing bytes to this socket.
     
*/
    OutputStream
& getOutputStream();

    
/**
     * Gets an Writer for this socket.
     
*/
    Writer
& getWriter(); 

    
/**
     * Enable/disable the option specified by <I>optID</I>.  If the option
     * is to be enabled, and it takes an option-specific "optval",  this is
     * passed in <I>value</I>.  The actual type of value is option-specific,
     * and it is an error to pass something that isn't of the expected type:
     * <BR>
     *
     * @param optID identifies the option
     * @param level [in] Level at which the option is defined; the supported levels 
     *              include SOL_SOCKET and IPPROTO_TCP. 
     * @param optval [in] Pointer to the buffer in which the value for the requested 
     *              option is to be returned.
     * @param optlen [in] Pointer to the size of the optval buffer, in bytes. 
     * @return 0 If no error occurs, Otherwise, a value of SOCKET_ERROR(-1) is returned, 
     *          and a specific error code can be retrieved by socketerrno.
     * @see #getOption(int)
     
*/
int setOption(int optID, int level, const void *optval, int optlen);

    
/**
     * Fetch the value of an option.
     * Binary options will return java.lang.Boolean(true)
     * if enabled, java.lang.Boolean(false) if disabled, e.g.:
     * <BR>
     *
     * @param optID an <code>int</code> identifying the option to fetch
     * @param level [in] Level at which the option is defined; the supported levels 
     *              include SOL_SOCKET and IPPROTO_TCP. 
     * @param optval [out] Pointer to the buffer in which the value for the requested 
     *              option is to be returned.
     * @param optlen [in, out] Pointer to the size of the optval buffer, in bytes. 
     * @return 0 If no error occurs, Otherwise, a value of SOCKET_ERROR(-1) is returned, 
     *          and a specific error code can be retrieved by socketerrno.
     * @see #setOption(int, java.lang.Object)
     
*/
    
int getOption(int optID, int level, void *optval, int *optlen);

    
/**
     * Closes this socket.
     * <p>
     * Any thread currently blocked in an I/O operation upon this socket
     * will throw a {@link SocketException}.
     * <p>
     * Once a socket has been closed, it is not available for further networking
     * use (i.e. can't be reconnected or rebound). A new socket needs to be
     * created.
     *
     * <p> If this socket has an associated channel then the channel is closed
     * as well.
     *
     
*/
    
void close();

};


 

1           SocketAcceptor


此类实现一个用于服务器端接收连接的类。一个SocketAcceptor对象等待来自网络的连接请求,它执行一些基于请求的操作,并且可能返回一些信息给请求者。连接成功后SocketAcceptor会生成一个Socket对象用于网络通讯。


SocketAcceptor类成员和主要接口定义如下:


 


class SocketAcceptor
{
protected
    
/**
     * The factory for all server sockets.
     
*/
    
static SocketImplFactoryAutoPtr _factory; 

    
/**
     * The implementation of this Socket.
     
*/
    SocketImplAutoPtr _impl; 

    
/**
     * Various states of this socket.
     
*/
    BOOL _bound; 
    BOOL _created; 
    BOOL _closed; 
    BOOL _stream; 

public
    
/**
     * Creates a socket acceptor with default stream type.
     * <p>
     
*/
    SocketAcceptor(); 

    
/**
     * Creates a socket acceptor with specified stream type, 
     * bound to the specified port and host. A port of 
     * <code>0</code> creates a socket on any free port. 
     * <p>
     * The maximum queue length for incoming connection indications (a 
     * request to connect) is set to <code>50</code>. If a connection 
     * indication arrives when the queue is full, the connection is refused.
     * <p>
     * If the application has specified a server socket factory, that 
     * factory's <code>createSocketImpl</code> method is called to create 
     * the actual socket implementation. Otherwise a "plain" socket is created.
     * <p>
     *
     * @param      host the host address the server will bind to
     * @param      port  the port number, or <code>0</code> to use any
     *                   free port.
     * @param      stream    if <code>true</code>, create a stream socket;
     *                       otherwise, create a datagram socket.
     * @param      backlog the listen backlog
     
*/
SocketAcceptor(
const String &host, int port, BOOL stream = TRUE);

    
/**
     * Binds the <code>ServerSocket</code> to a local address
     * (IP address and port number).
     * <P>
     * The <code>backlog</code> argument must be a positive
     * value greater than 0. If the value passed if equal or less
     * than 0, then the default value will be assumed.
     * @param port the local TCP port
     * @param backlog the listen backlog
     * @return 0 If no error occurs, Otherwise, a value of 
     *          SOCKET_ERROR(-1) is returned
     
*/
    
int bind(int port, int backlog = LISTENQ);

    
/**
     * Listens for a connection to be made to this socket and accepts 
     * it. The method blocks until a connection is made. 
     *
     * <p>A new Socket <code>s</code> is created and, if there 
     * is a security manager, 
     * the security manager's <code>checkAccept</code> method is called
     * with <code>s.getInetAddress().getHostAddress()</code> and
     * <code>s.getPort()</code>
     *
     * @param timeout  timeout to accept in ms. 
     * @return the new Socket
     
*/
    Socket accept(
int timeout); 

    
/**
     * Closes this socket acceptor.
     * <p>
     * Any thread currently blocked in an I/O operation upon this socket
     * will throw a {@link SocketException}.
     * <p>
     * Once a socket has been closed, it is not available for further networking
     * use (i.e. can't be reconnected or rebound). A new socket needs to be
     * created.
     *
     * <p> If this socket has an associated channel then the channel is closed
     * as well.
     *
     
*/
    
void close();

};



1           SocketConnector


此类实现一个用于客户器端连接服务的类。一个SocketConnector对象可以向指定的服务地址和端口发出的连接请求,它执行一些基于请求的操作,并且可能返回一些信息。连接成功后SocketConnector会生成一个Socket对象用于网络通讯。


SocketConnector类成员和主要接口定义如下:


class SocketConnector
{
protected
    
/**
     * The factory for all server sockets.
     
*/
    
static SocketImplFactoryAutoPtr _factory; 

    
/**
     * The implementation of this Socket.
     
*/
    SocketImplAutoPtr _impl; 

    
/**
     * Various states of this socket.
     
*/
    BOOL _bound; 
    BOOL _created; 
    BOOL _closed; 
    BOOL _stream;; 

public
    
/**
     * Creates a socket connector with specified stream type.
     * default is Creates a stream socket connector. 
     * <p>
     *
     * @param      stream    if <code>true</code>, create a stream socket;
     *                       otherwise, create a datagram socket.
     
*/
    SocketConnector(BOOL stream 
= TRUE);

    
/**
     * Creates a socket and connects it to the specified port on
     * the specified host.
     * @param host the specified host
     * @param port the specified port
     * @param timeout the timeout value in milliseconds, or zero for no timeout.
     *          -1 will use default timeout.
     * @return the new Socket
     
*/
Socket connect(
const String &host, int port, int timeout = -1);

    
/**
     * Closes this socket connector.
     * <p>
     * Any thread currently blocked in an I/O operation upon this socket
     * will throw a {@link SocketException}.
     * <p>
     * Once a socket has been closed, it is not available for further networking
     * use (i.e. can't be reconnected or rebound). A new socket needs to be
     * created.
     *
     * <p> If this socket has an associated channel then the channel is closed
     * as well.
     *
     
*/
    
void close();

};



1           SocketInputStream


这个类类似与其他InputStreamFileInputStream类,是InputStream接口类的一个实现,执行Socket流的读取操作,实现的接口均是最基础的操作,如读取一个byte字节的数据,或者读取指定长度的数据。


SocketInputStream类成员和主要接口定义如下:



class SocketInputStream : public InputStream
{
protected
    
/**
     * Pointer to the implementation of this SocketImpl.
     
*/
    PlainSocketImpl 
*_impl; 

    
/**
     * Pointer to the implementation of this Socket.
     
*/
    Socket 
*_socket; 

    BOOL _eof; 
BOOL _closing;

    
/**
     * Creates a new SocketInputStream. Can only be called
     * by a Socket. This method needs to hang on to the owner Socket so
     * that the fd will not be closed.
     * @param impl the implemented socket input stream
     
*/
    SocketInputStream(PlainSocketImpl 
&impl);

public
    
/**
     * Check current SocketInputStream object if is opened. 
     *
     * @return  TRUE if opened else return FALSE
     
*/
    BOOL isOpened();

    
/**
     * Reads a byte of data from this input stream. This method blocks
     * if no input is yet available.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             file is reached.
     
*/
    
int read(); 

    
/**
     * Reads up to <code>len</code> bytes of data from this input stream
     * into an array of bytes. This method blocks until some input is
     * available.
     *
     * @param      b     the buffer into which the data is read.
     * @param      off   the start offset of the data.
     * @param      len   the maximum number of bytes read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>0</code> if there is no more data because the end of
     *             the file has been reached, or -1 if read error.
     
*/
    
int read(void *b, int off, int len);

    
/**
     * Reads up to <code>len</code> bytes of data from this input stream
     * into an array of bytes. This method blocks until some input is
     * available.
     *
     * @param      b     the buffer into which the data is read.
     * @param      len   the maximum number of bytes read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>0</code> if there is no more data because the end of
     *             the file has been reached, or -1 if read error.
     
*/
    
int read(void *b, int len);
    
/**
     * Skips over and discards <code>n</code> bytes of data from the
     * input stream. The <code>skip</code> method may, for a variety of
     * reasons, end up skipping over some smaller number of bytes,
     * possibly <code>0</code>. The actual number of bytes skipped is returned.
     *
     * @param      n   the number of bytes to be skipped.
     * @return     the actual number of bytes skipped.
     
*/
    
long skip(long n); 

    
/**
     * Returns the number of bytes that can be read from this file input
     * stream without blocking.
     *
     * @return     the number of bytes that can be read from this file input
     *             stream without blocking.
     
*/
    
int available(); 

    
/**
     * Closes this file input stream and releases any system resources
     * associated with the stream.
     *
     * <p> If this stream has an associated channel then the channel is closed
     * as well.
     
*/
    
void close();

};


 


1           SocketOutputStream


这个类类似与其他OutputStreamFileOutputStream类,是OutputStream接口类的一个实现,执行Socket流的写操作,实现的接口均是最基础的操作,如写一个byte字节的数据,或者写指定长度的数据。


SocketOutputStream类成员和主要接口定义如下:


 


class SocketOutputStream : public OutputStream
{
protected
    
/**
     * Pointer to the implementation of this SocketImpl.
     
*/
    PlainSocketImpl 
*_impl; 

    
/**
     * Pointer to the implementation of this Socket.
     
*/
Socket 
*_socket;

    
/**
     * Creates a new SocketOutputStream. Can only be called
     * by a Socket. This method needs to hang on to the owner Socket so
     * that the fd will not be closed.
     * @param impl the implemented socket input stream
     
*/
    SocketOutputStream(PlainSocketImpl 
&impl);

public
    
/**
     * Check current SocketInputStream object if is opened. 
     *
     * @return  TRUE if opened else return FALSE
     
*/
    BOOL isOpened(); 

    
/**
     * Report position in output stream.
     
*/ 
long tellp();

    
/**
     * Writes the specified byte to this file output stream. Implements 
     * the <code>write</code> method of <code>OutputStream</code>.
     *
     * @param b   the byte to be written.
     
*/
    SocketOutputStream
& write(int b); 

    
/**
     * Writes <code>len</code> bytes from the specified byte array 
     * starting at offset <code>off</code> to this file output stream. 
     *
     * @param      b     the data.
     * @param      len   the number of bytes to write.
     
*/
    SocketOutputStream
& write(const void *b, int len); 

    
/**
     * Writes <code>len</code> bytes from the specified byte array 
     * starting at offset <code>off</code> to this file output stream. 
     *
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     
*/
    SocketOutputStream
& write(const void *b, int off, int len); 

    
/**
     * Flushes this output stream and forces any buffered 
     * output bytes to be written out.
     
*/
    
void flush(); 

    
/**
     * Closes this file out stream and releases any system resources
     * associated with the stream.
     *
     * <p> If this stream has an associated channel then the channel is closed
     * as well.
     
*/
    
void close();

};


 


1           SocketReader类和SocketWriter


SocketReader类实现了一个对Socket流读设备即SocketInputStream的读取器,它实现了Reader接口类的方法,所以具有与其它Reader相同的功能,也可以与其它一些读操作器如BufferedReader配合使用以达到一些更高级的功能。


SocketWriter 类则实现了一个对Socket流写设备即SocketOutputStream的写操作器,它实现了Writer接口类的方法,所以具有与其它Writer相同的功能,如进行一些operator <<运算符的操作等。


详细例子请参考 httptest.cpp 范例程序。


 


 


 


C++通用框架的设计作者:naven 日期:2006-8-10



 

No comments: