C++ I/O 重定向方法(定向到串口或Socket)(三)

2014-11-24 09:00:29 · 作者: · 浏览: 1
下所示:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include #include class SocketInStreamBuf : public std::streambuf { public: SocketInStreamBuf(SOCKET socket) : m_socket(socket) { } int_type underflow() { char c; if (recv(m_socket, &c, 1, MSG_PEEK) <= 0) { return EOF; } return c; } int_type uflow() { char c; if (recv(m_socket, &c, 1, 0) <= 0) { return EOF; } return c; } private: SOCKET m_socket; };

无缓冲的实现需要同时重写underflow和uflow,根据这两个方法的定义,前者不移动读取位置,后者反之,而recv函数的MSG_PEEK选项刚好可以对应这两种行为。

有缓冲方式

从套接字逐个读取字符也是非常低效的过程,添加缓冲功能是再自然不过的事情,如下所示:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include #include class SocketInStreamBuf : public std::streambuf { public: SocketInStreamBuf(SOCKET socket) : m_socket(socket) { setg(m_buffer, m_buffer, m_buffer); } int_type underflow() { int recvLen = recv(m_socket, m_buffer, BufferSize, 0); if (recvLen <= 0) { return EOF; } setg(m_buffer, m_buffer, m_buffer + recvLen); return *gptr(); } private: SOCKET m_socket; static const int BufferSize = 512; char m_buffer[BufferSize]; };

跟输出的实现一样,我们也需要自己定义一个缓冲区,然后用setg方法设置缓冲区的指针。与setp不同,setg方法需要设置三个指针,分别是缓冲区头指针,当前读取位置指针以及缓冲区尾部下一个位置指针,这些指针可通过eback(),gptr(),egptr()方法获取。这比输出缓冲区复杂,因为输入缓冲区需要支持回退功能。输入缓冲区图示如下:

\

当读取字符时,gptr向右移动,直到gptr() == egptr()时,调用underflow从外部设备补充数据。当回退字符时,gptr向左移动,直到gptr() == gback()时,就不能再回退字符了。

在上面代码的构造方法中,用setg把三个指针都设置到缓冲区头部,这样一来,就不支持回退了,而且第一次读取会导致underflow被调用。在underflow中,将数据读取到缓冲区之后还要调用setg重新设置一下缓冲区指针,由于是gptr() == eback(),所以仍然不支持回退。

上文说过,如果提供了缓冲区,那么就不需要重写uflow了,所以提供了缓冲功能的SocketInStreamBuf看上去比无缓冲功能的还要简单。

使用自定义的输入streambuf

跟输出的一样,只要将SocketInStreamBuf与istream组合在一起,就可以利用强大的IO功能了:

1 2 3 4 5 6 7 8 9 SOCKET socket; SocketInStreamBuf inBuf(socket); std::istream socketStream(&inBuf); std::string line; while (std::getline(socketStream, line)) { std::cout << line << std::endl; }

上面的代码从套接字读取数据,然后输出到控制台上。