用streambuf简单封装socket

来源:互联网 发布:ps淘宝主图广告 编辑:程序博客网 时间:2024/05/01 09:09
C++的I/O流是很强大的. 一方面在于易于使用, 类型安全. 另一方面在于容易扩展.
前几天尝试了一下用streambuf来封装客户端使用的socket. 虽然代码运行正确, 自己
的概念确实很模糊. 今天下回来cuj的光盘, 浏览了一下其中一篇也是关于使用stream来
封装socket的文章, 发现作者的概念也不比我清楚到哪里去. ;-)

先考虑一下最后客户代码的写法, 我希望是这样

        huxw::Socket so = huxw::TCPv4_Connect("echo", "166.111.172.6");
        huxw::tcpstream ts(so);
        std::string tmpstr;
        while( std::cin >> tmpstr )
        {
                ts << tmpstr << std::endl;
                ts >> tmpstr;
                std::cout << "We got " << tmpstr << std::endl;
        }
        return 0;

以上就是一个echo client.

我们知道stream操作streambuf, streambuf才真正和设备(或者是更底层的buffer)打交道.
而stream操作streambuf是通过一系列标准接口来完成的. 只要我们能够继承basic_streambuf,
并且改写其中若干接口, 就可以使得原有的stream使用新的streambuf.

而这个改写的过程, 比想象的要简单的多. 基本上, 如果我们比较懒的话, 改三个就足够了
overflow, underflow, sync. overflow表示写缓冲写满了的时候, 应该怎么办? underflow表示
读缓冲读完了的时候应该怎么办? sync是针对写缓冲的同步.

想象一个典型的读写流程
        ts << str << std::endl;
        ts >> str;
假设开始的时候ts的读写缓冲都是空的.
ts<<str会将str中的内容放入写buffer, ts<<std::endl;会调用sync.
ts>>str会试图从读buffer中读取内容, 但是读buffer是空的, 所以他会去调用underflow.
一旦str的内容如此之大, 使得ts的写buffer中再也放不下了, 那么overflow会被自动调用

理解了以上流程, 我们就可以动手打造自己的streambuf
template<typename charT, 
         typename traits=std::char_traits<charT>
        >
class basic_tcp_streambuf : public std::basic_streambuf<charT, traits>
这是定义的标准方法, 于basic_streambuf类似的是, 我们通过typedef来实现char的和
wchar_t的两种流.

        basic_tcp_streambuf(Socket& so, int bufsize = 512)
        : m_bufsize(bufsize)
        {
                //我们需要两个缓冲区, 一个是put buffer, 一个是get buffer
                //gback()返回get buffer的起始地址, egptr()返回get buffer的结束地址
                //pbase()返回put buffer的起始地址, epptr()返回put buffer的结束地址
                //分别以pptr, gptr表示put buffer和get buffer的当前位置
                m_sock = so;
                char_type* buf;
                buf = new char_type[bufsize];
                setg(buf, buf, buf);      //setg设置gback(), gptr(), egptr()
                buf = new char_type[bufsize];
                setp(buf, buf + bufsize); //setp设置pbase(), pptr(), epptr() 
                                          //其中pptr()被自动设置为pbase()
                                          //有的stl实现给了一个扩展函数, 能够单独
                                          //设置pptr(), 不推荐使用
        }

        virtual int_type overflow(typename traits::int_type c = traits::eof())
        {
                //当sputc失败的时候, 会调用overflow
                //如果c不是eof(), 本函数尝试将traits::to_char_type(c)插入put buffer当中
                
                _flush(); //清空缓冲
                setp(pbase(), epptr()); //重新设定put缓冲, 将pptr()重置到pbase()处
                
                if (traits::eq_int_type(traits::eof(), c))
                        return traits::not_eof(c);
                
                sputc(traits::to_char_type(c));  //put c into buffer again
                return c;
        }
        
        virtual int_type underflow()
        {
                //此时get buffer中已经没有内容, 重新读入
                int r = m_sock.Recv(eback(), m_bufsize * sizeof(char_type));
                if (r == 0)
                        return traits::to_int_type(traits::eof());
                setg(eback(), eback(), eback() + r / sizeof(char_type));
                return traits::to_int_type(*gptr());    
        }
        
        virtual int sync() //比如endl这样的操作符会调用sync()
        {
                _flush();
                setp(pbase(), epptr());
                return 0;
        }

private:
        inline void _flush()
        {
                m_sock.SendAll(pbase(), (pptr() - pbase()) * sizeof(char_type));
        }

这样基本上就可以了.

template<typename charT, typename traits = std::char_traits<charT> >
class basic_tcpstream : public std::basic_iostream<charT, traits>
{
public:
        explicit basic_tcpstream(Socket& so, int bufsize = 512) 
        : m_buf(so, bufsize), std::basic_iostream<charT, traits>(&m_buf)                
        {
        
        }
        
        basic_tcp_streambuf<charT, traits>* rdbuf()
        {
                return &m_buf;
        }
private:
        basic_tcp_streambuf<charT, traits> m_buf;
}; 
原创粉丝点击