套接字缓冲区

来源:互联网 发布:react 引用js 编辑:程序博客网 时间:2024/05/29 12:28

当从一个套接字读写内容时,需要一个缓冲区,用来保存读取和写入的数据。

缓冲区内存的有效时间必须必I/O操作的时间要长,需要保证它们在I/O操作结束之前不被释放。

对于同步操作来说,这很容易。

char buff[512];...sock.receive(buffer(buff));strcpy(buff, "ok\n");sock.send(buffer(buff));

但是在异步操作时就没这么简单了。

// 非常差劲的代码 ...void on_read(const boost::system::error_code & err, std::size_t read_bytes){ ... }void func() {char buff[512];sock.async_receive(buffer(buff), on_read);}
在我们调用async_receive()之后,buff就已经超出有效范围,它的内存当然会被释放。当我们开始从套接字
接收一些数据时,我们会把它们拷贝到一片已经不属于我们的内存中;它可能会被释放,或者被其他代码
重新开辟来存入其他的数据,结果就是:内存冲突。
对于上面的问题有几个解决方案:

  1. 使用全局缓冲区
  2. 创建一个缓冲区,然后在操作结束时释放它
  3. 使用一个集合对象管理这些套接字和其他的数据,比如缓冲区数组
第一个方法显然不是很好,因为我们都知道全局变量非常不好。此外,如果两个实例使用同一个缓冲区怎
么办?
下面是第二种方式的实现:

void on_read(char * ptr, const boost::system::error_code & err, std::size_t read_bytes) {delete[] ptr;}....char * buff = new char[512];sock.async_receive(buffer(buff, 512), boost::bind(on_read,buff,_1,_2))
或者,如果你想要缓冲区在操作结束后自动超出范围,使用共享指针

struct shared_buffer {boost::shared_array<char> buff;int size;shared_buffer(size_t size) : buff(new char[size]), size(size) {}mutable_buffers_1 asio_buff() const {return buffer(buff.get(), size);}};// 当on_read超出范围时, boost::bind对象被释放了,// 同时也会释放共享指针void on_read(shared_buffer, const boost::system::error_code & err, std::size_t read_bytes) {}sock.async_receive(buff.asio_buff(), boost::bind(on_read,buff,_1,_2));
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">shared_buffer类拥有实质的shared_array<>,shared_array<>存在的目的是用来保存shared_buffer实例的</span>

拷贝-当最后一个share_array<>元素超出范围时,shared_array<>就被自动销毁了,而这就是我们想要的

结果。

因为Boost.Asio会给完成处理句柄保留一个拷贝,当操作完成时就会调用这个完成处理句柄,所以你的目的

达到了。那个拷贝是一个boost::bind的仿函数,它拥有着实际的shared_buffer实例。这是非常优雅的!

第三个选择是使用一个连接对象来管理套接字和其他数据,比如缓冲区,通常来说这是正确的解决方案但

是非常复杂.


缓冲区封装函数

纵观所有代码,你会发现:无论什么时候,当我们需要对一个buffer进行读写操作时,代码会把实际的缓冲

区对象封装在一个buffer()方法中,然后再把它传递给方法调用:

char buff[512];

sock.async_receive(buffer(buff), on_read);

基本上我们都会把缓冲区包含在一个类中以便Boost.Asio的方法能遍历这个缓冲区,比方说,你使用下面的代码:

sock.async_receive(some_buffer, on_read);

实例some_buffer需要满足一些需求,叫做ConstBufferSequence或者MutableBufferSequence(你可以在

Boost.Asio的文档中查看它们)。创建你自己的类去处理这些需求的细节是非常复杂的,但是Boost.Asio已

经提供了一些类用来处理这些需求。所以你不用直接访问这些缓冲区,而可以使用buffer()方法。

自信地讲,你可以把下面列出来的类型都包装到一个buffer()方法中:

  • 一个char[] const 数组
  • 一个字节大小的void *指针
  • 一个std::string类型的字符串
  • 一个POD const数组(POD代表纯数据,这意味着构造器和释放器不做任何操作)
  • 一个pod数据的std::vector
  • 一个包含pod数据的boost::array
  • 一个包含pod数据的std::array
下面的代码都是有效的:

struct pod_sample { int i; long l; char c; };

...

char b1[512];

void * b2 = new char[512];

std::string b3;

b3.resize(128);

pod_sample b4[16];

std::vector<pod_sample> b5;

b5.resize(16);

boost::array<pod_sample,16> b6;

std::array<pod_sample,16> b7;

sock.async_send(buffer(b1), on_read);

sock.async_send(buffer(b2,512), on_read);

sock.async_send(buffer(b3), on_read);

sock.async_send(buffer(b4), on_read);

sock.async_send(buffer(b5), on_read);

sock.async_send(buffer(b6), on_read);

sock.async_send(buffer(b7), on_read);

总的来说就是:与其创建你自己的类来处理ConstBufferSequence或者MutableBufferSequence的需求,不

如创建一个能在你需要的时候保留缓冲区,然后返回一个mutable_buffers_1实例的类,而我们早在

shared_buffer类中就这样做了。


0 0