用C++从套接字读取数据并写入到ofstream文件中

5
有没有一种 C/C++ 的方法,可以使用 read() 从套接字读取数据,并让接收缓冲区成为一个文件(例如 ofstream)或类似的自扩展对象(如 vector)?
编辑:这个问题是在我考虑如何读取可能接收到某个 10000+ 字节文件内容的流式套接字时出现的。我不喜欢将 20000 或 50000 字节(足够大了)放在堆栈上作为缓冲区,以便可以将文件暂时存储起来,然后再将其插入到文件中。为什么不直接将其流式传输到文件中呢?
就像你可以获取 std::string 中的 char* 一样,我想到了像下面这样的东西
read( int fd, outFile.front(), std::npos );  // npos = INT_MAX

or something like that.

end edit

Thanks.


1
是的。相当多的人编写了连接到套接字的流缓冲区。虽然它们一开始看起来很酷,但至少从我所见,它们在实践中很少能够正常工作。你(几乎)需要添加某种异步操作(例如,像ASIO那样)才能使其正常工作。http://socketstream.sourceforge.net/,http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html等。 - Jerry Coffin
2个回答

4

以下是我随意想出的简单翻译,但我认为这样的内容可能会起到作用:

template <unsigned BUF_SIZE>
struct Buffer {
    char buf_[BUF_SIZE];
    int len_;
    Buffer () : buf_(), len_(0) {}
    int read (int fd) {
        int r = read(fd, buf_ + len_, BUF_SIZE - len_);
        if (r > 0) len_ += r;
        return r;
    }
    int capacity () const { return BUF_SIZE - len_; }
}

template <unsigned BUF_SIZE>
struct BufferStream {
    typedef std::unique_ptr< Buffer<BUF_SIZE> > BufferPtr;
    std::vector<BufferPtr> stream_;
    BufferStream () : stream_(1, BufferPtr(new Buffer<BUF_SIZE>)) {}
    int read (int fd) {
        if ((*stream_.rbegin())->capacity() == 0)
            stream_.push_back(BufferPtr(new Buffer<BUF_SIZE>));
        return (*stream_.rbegin())->read(fd);
    }
};

在评论中,您提到希望避免创建一个大的字符缓冲区。当使用read系统调用时,通常更有效率的做法是进行少量的大读取,而不是许多小读取。因此,大多数实现都会选择大的输入缓冲区来获得这种效率。您可以实现类似以下的内容:

std::vector<char> input;
char in;
int r;
while ((r = read(fd, &in, 1)) == 1) input.push_back(in);

但这将涉及到每个输入字节至少一个系统调用和一个字节的复制。相比之下,我提出的代码避免了额外的数据复制。
我并不真正期望我提供的代码是你采用的解决方案。我只是想提供给你一个如何创建一个自我扩展对象的示例,它是相当节省空间和时间的。根据你的目的,你可能想要扩展它或编写自己的代码。从我的记忆中,一些改进可能包括:
- 使用std::list而不是vector来避免调整大小。 - 允许API参数指定要读取多少字节。 - 使用readv来始终允许至少读取BUF_SIZE字节(或更多字节)。

有趣。我真正想要的是不必声明一个大的字符缓冲区的方法。但在结构体Buffer中{ char buf_ [BUF_SIZE];...},您正在声明一个字符缓冲区。 - Wes Miller
1
@WesMiller: read 需要传递一个缓冲区,并且您希望数据收集在自扩展的数据结构中,这意味着在 read 返回后存储缓冲区。如果您真的不想在用户空间代码中创建任何缓冲区,则需要创建自己的网络 I/O 设备驱动程序,具有 0 拷贝语义(直接访问内核使用的网络缓冲区)。 - jxh
好的,不需要那么远。我考虑过自扩展对象,以处理接收到大小未知的数据流(它是一个流式套接字)的情况。就像上面原帖中编辑的那样,我将会接收一个大小未知的文件,并希望不必指望 char 缓冲区[50000] 足够大。这肯定早晚会出问题。 - Wes Miller
@WesMiller:BUF_SIZE是一个模板参数,允许调整缓冲区大小。通常,它应该是一个与输入预期数据速率相匹配的大小,乘以读取调用之间的时间间隔。 - jxh

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接