Javen-Studio 咖啡小屋

http://javenstudio.org - C++ Java 分布式 搜索引擎
Naven's Research Laboratory - Thinking of Life, Imagination of Future

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  24 随笔 :: 57 文章 :: 170 评论 :: 4 Trackbacks

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


 

posted on 2006-08-10 00:46 Javen-Studio 阅读(7943) 评论(15)  编辑 收藏 引用

评论

# re: SOCKET的封装 2006-08-10 06:57 万连文
代码呢???可以给我一份???
wlwlxj@gmail.com
谢谢  回复  更多评论
  

# re: SOCKET的封装 2006-08-10 10:23 Javen-Studio
首页可以下载试用版本,源码等我完成后会open source的,thx  回复  更多评论
  

# re: SOCKET的封装 2006-08-10 18:16 子弹
不错,

我最近也在学习NETWORK编程

希望能与你交流  回复  更多评论
  

# re: SOCKET的封装 2006-08-10 18:22 navy
好啊,一起学习~  回复  更多评论
  

# re: SOCKET的封装 2006-08-15 12:21 可冰
不错。
不知道naven了解ACE不,感觉有一点类似。不过我觉得更像JAVA。  回复  更多评论
  

# re: SOCKET的封装 2006-08-15 13:56 Javen-Studio
是啊,我就是学习了一些ACE的设计想法做的,接口都仿照java  回复  更多评论
  

# re: SOCKET的封装 2011-07-22 14:17 this 王
能给我份源码?  回复  更多评论
  

# re: SOCKET的封装 2012-02-28 22:13 FrankWang
380330439@qq.com
能给我一份源代码吗?
我也在学这个。  回复  更多评论
  

# re: SOCKET的封装[未登录] 2012-04-30 14:59 cc
371549734@qq.com
如果可以,麻烦也给我一份 谢谢了  回复  更多评论
  

# re: SOCKET的封装 2012-11-12 12:38 sammy
看了你的文章,感觉封装的不错。能给我一份源代码吗?
sammylymhk@yahoo.com.cn  回复  更多评论
  

# re: SOCKET的封装 2013-04-01 18:33 杨平
有源代码吗?请转一份给我啊,qq:448086006@qq.com,多谢!  回复  更多评论
  

# re: SOCKET的封装 2014-05-08 16:55 weiqinyu
写的真好。能发给我一份吗?weiqinyu2005@163.com  回复  更多评论
  

# re: SOCKET的封装 2015-03-21 18:32 陈甜
写的真好。能发给我一份吗?283063526@qq.com  回复  更多评论
  

# re: SOCKET的封装 2015-07-19 17:29 莫莫
看了你的文章,感觉封装的不错。能给我一份源代码吗?@Javen-Studio
gypzfabc@126.com  回复  更多评论
  


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理