Handle socket descriptors like file descriptor (fstream)? C++/Linux

后端 未结 1 400
后悔当初
后悔当初 2021-02-06 09:41

By accident I found out that I can use read and write on socket descriptors. Can I somehow (ab)use the fstream mechanism to output data into the socket descriptor?

相关标签:
1条回答
  • 2021-02-06 10:22

    The standard file stream doesn't support use of a file descriptor. However, the I/O stream classes make it reasonably easy to create your own abstraction which allows creating your own sources of or destination for characters. The magic class is std::streambuf whose responsibility is to buffer characters and read or write characters at appropriate times. Nicolai Josuttis's "The C++ Standard Library" has a detailed description of how to do so (the basis of which I contributed to Nico many years ago). A simple implementation of a stream buffer using a socket for reading and writing would look something like this:

    #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));
    }
    
    0 讨论(0)
提交回复
热议问题