套接字缓冲区

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

缓冲区内存的有效时间必须必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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值