用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;
};
前几天尝试了一下用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;
};
- 用streambuf简单封装socket
- streambuf
- windows socket简单封装
- Android Socket简单封装
- 一个简单的socket封装
- 简单Socket 封装类及应用
- Android 客户端Socket 实现及简单封装。
- 用C++封装Socket库
- 用C++封装Socket库
- Socket封装
- 探究streambuf
- 基于TCP模式的socket编程 简单封装
- Linux下Socket的简单使用及最简化封装
- Linux下Socket的简单使用及最简化封装
- 简单的Socket 编程 服务端和客户端 (封装)
- 封装一个简单的Windows UDP socket 网络类
- 尝试用C++封装Socket库
- 尝试用C++封装Socket库
- TREEVIEW数据库绑定
- [VC MFC C++ flex bison] flex example(Pascal-like language scanner)
- MySQL优化经验
- Agile 敏捷建模思想
- CSDN新功能建议
- 用streambuf简单封装socket
- 生活小技巧
- 这个冬天有大雪
- 如何导出存储过程和自定义函数?
- 中文字符无法在CB中编译
- struts 2学习(有关Unable to load bean: type: class:com.opensymphony.xwork2.ObjectFactory的错误)
- JAVA 反射之方法调用
- 我心里的流星...
- 为IE找个新死法