【问题标题】:Handle socket descriptors like file descriptor (fstream)? C++/Linux处理像文件描述符(fstream)这样的套接字描述符? C++/Linux
【发布时间】:2012-11-12 12:58:24
【问题描述】:

偶然我发现我可以在套接字描述符上使用读写。我可以以某种方式(ab)使用 fstream 机制将数据输出到套接字描述符中吗?

【问题讨论】:

  • 您可以创建自己的 streambuf 类,供流使用。事实上,如果你稍微搜索一下,有些库已经为你做到了。
  • 您在寻找this吗?

标签: c++ linux sockets stream


【解决方案1】:

标准文件流不支持使用文件描述符。然而,I/O 流类使得创建自己的抽象变得相当容易,从而允许创建自己的字符源或目标。魔术类是std::streambuf,其职责是缓冲字符并在适当的时间读取或写入字符。 Nicolai Josuttis 的"The C++ Standard Library" 详细描述了如何做到这一点(多年前我为 Nico 做出了贡献)。使用套接字进行读写的流缓冲区的简单实现如下所示:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <streambuf>
#include <cstddef>
#include <unistd.h>

class fdbuf
    : public std::streambuf
{
private:
    enum { bufsize = 1024 };
    char outbuf_[bufsize];
    char inbuf_[bufsize + 16 - sizeof(int)];
    int  fd_;
public:
    typedef std::streambuf::traits_type traits_type;

    fdbuf(int fd);
    ~fdbuf();
    void open(int fd);
    void close();

protected:
    int overflow(int c);
    int underflow();
    int sync();
};

fdbuf::fdbuf(int fd)
  : fd_(-1) {
    this->open(fd);
}

fdbuf::~fdbuf() {
    this->close();
}

void fdbuf::open(int fd) {
    this->close();
    this->fd_ = fd;
    this->setg(this->inbuf_, this->inbuf_, this->inbuf_);
    this->setp(this->outbuf_, this->outbuf_ + bufsize - 1);
}

void fdbuf::close() {
    if (!(this->fd_ < 0)) {
        this->sync();
        ::close(this->fd_);
    }
}

int fdbuf::overflow(int c) {
    if (!traits_type::eq_int_type(c, traits_type::eof())) {
        *this->pptr() = traits_type::to_char_type(c);
        this->pbump(1);
    }
    return this->sync() == -1
        ? traits_type::eof()
        : traits_type::not_eof(c);
}

int fdbuf::sync() {
    if (this->pbase() != this->pptr()) {
        std::streamsize size(this->pptr() - this->pbase());
        std::streamsize done(::write(this->fd_, this->outbuf_, size));
        // The code below assumes that it is success if the stream made
        // some progress. Depending on the needs it may be more
        // reasonable to consider it a success only if it managed to
        // write the entire buffer and, e.g., loop a couple of times
        // to try achieving this success.
        if (0 < done) {
            std::copy(this->pbase() + done, this->pptr(), this->pbase());
            this->setp(this->pbase(), this->epptr());
            this->pbump(size - done);
        }
    }
    return this->pptr() != this->epptr()? 0: -1;
}

int fdbuf::underflow()
{
    if (this->gptr() == this->egptr()) {
        std::streamsize pback(std::min(this->gptr() - this->eback(),
                                       std::ptrdiff_t(16 - sizeof(int))));
        std::copy(this->egptr() - pback, this->egptr(), this->eback());
        int done(::read(this->fd_, this->eback() + pback, bufsize));
        this->setg(this->eback(),
                   this->eback() + pback,
                   this->eback() + pback + std::max(0, done));
    }
    return this->gptr() == this->egptr()
        ? traits_type::eof()
        : traits_type::to_int_type(*this->gptr());
}

int main()
{
    fdbuf        inbuf(0);
    std::istream in(&inbuf);
    fdbuf        outbuf(1);
    std::ostream out(&outbuf);

    std::copy(std::istreambuf_iterator<char>(in),
              std::istreambuf_iterator<char>(),
              std::ostreambuf_iterator<char>(out));
}

【讨论】:

  • 更正:这个 sn-p 中有两个错误(相互抵消)。当调用overflow() 时,不应写入*pptr。你必须先sync() 腾出空间再写。但是,这是可以的,因为第二个问题是 setp 采用指向过去的指针而不是指向最后一个元素的指针(因此一旦溢出错误被修复,-1 应该被删除)。
  • @BenDarnell:实际上,这两个动作都是故意的!报告给setp() 的缓冲区比实际值小一个字符因为 这个位置将在overflow() 中用于保存额外的字符(如果有)。也就是说,我看不出为什么溢出字符应该存储在一个空缓冲区中而不是与一批缓冲字符一起发送的充分理由。我很确定没有规定设置缓冲区的流缓冲区不能向其中添加字符(假设它安排了必要的空间)。
  • 对 pbump(1) 的调用将 pptr 置于从 pbase 到 epptr 的范围之外,根据cplusplus.com/reference/streambuf/streambuf/pbump,这是一个错误(导致未定义的行为)。看起来这不是强制执行的(至少在我使用的 libc++ 版本中),但是坚持规范字母的 STL 实现仍然可以认为这是一个错误。
  • @BenDarnell:它当然没有被强制执行,而且该标准实际上并没有对overflow() 的实施方式提出任何具体限制!有一个 [次要] 有问题的方面,那就是在没有完全限定流缓冲区类型的情况下调用 sync():理论上,进一步派生的流缓冲区可能会覆盖 sync(),在这种情况下可能会出现问题。当控制传递给 IOStreams 库时,指针设置的约束适用,但在实现特定的流缓冲区时,调整违反约束是完全可以的。
  • @BenDarnell:查看 27.6.3.3.3 [streambuf.put.area] 第 4 段对pbump() 的论点没有任何先决条件。它的字面意思是“void pbump(int n); 效果:将 n 添加到输出序列的下一个指针。”指针的设置方式有一些限制,但在控制发生的事情时,这些显然不适用。另外,既然你提到了libc++:它在std::basic_filebufoverflow()中做了完全相同的把戏!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-13
  • 2014-04-25
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 2015-03-16
相关资源
最近更新 更多