如前所述,对于所有类型的socket,并不是都有同样的成员函数。下面的表格列出了3个socket中存在的成员函数:
名称 | TCP | UDP | ICMP |
---|---|---|---|
async_read_some | Yes | - | - |
async_receive_from | - | Yes | Yes |
async_write_some | Yes | - | - |
async_send_to | - | Yes | Yes |
read_some | Yes | - | - |
receive_rom | - | Yes | Yes |
write_some | Yes | - | - |
send_to | - | Yes | Yes |
还有其他的一些函数用户处理连接或者输入/输出:
最后需要注意的是,socket是不能复制的,它的复制构造函数是不可访问的:
ip::tcp::socket s1(service), s2(serviec);
s1 = s2; //编译错误
ip::socket::socket s3(s1); //编译错误
这样可以避免很多麻烦的问题,加入允许复制,那么会出现,两个socket持有一个相同的原始socket,那么到底谁对资源负责呢,谁在合适的时候释放它呢,很麻烦。所以Boost.Asio不允许复制socket。假如你真要复制socket,直接使用智能指针就行了:
typedef boost::shared_ptr<ip::tcp::socket> socket_ptr;
socket_ptr sock1(new ip::tcp::socket(service));
socket_ptr sock2(sock1);
socket_ptr sock3;
sock3 = sock1; // 这是可以的
当向socket读取和写入数据的时候,你需要使用到缓冲区,它负责保存输入和输出的数据。缓冲区中内存的生存期要比I/O操作要长;你必须要保证,在I/O操作最终完成之前,这部分内存不会被释放掉。
这对同步操作来说,是很容易的。毫无疑问,缓冲区的生存期要比receive和send的要长:
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就脱离了作用域,这部分内存就被释放掉了。也就是说我们把需要的数据拷贝到了我们不在拥有的内存上去了,这部分内存很可能被释放,然后重新分配给其他一些代码使用,毫无疑问,内存腐烂了。
有多种方法可以解决上面的问题:
第一个解决方法并不好,我们都知道全局变量是很不好的编码习惯。
第二种方法,我们使用智能指针,当操作完成后,缓冲区能够自动删除:
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的作用域之后,async_receive也返回了,shared_buffer就被自动销毁了
void on_read(shared_buffer, const boost::system::error_code& err,
std::size_t read_bytes)
{
}
...
shared_buffer buff(512);
sock.async_receive(buff.asio_buff(), boost::bind(on_read, buff, _1, _2));
第三种方法,就是用一个单独的对象来管理socket和socket相关的数据,比如buffers对象。这是比较好的解决方案,但是稍显复杂,本章的最后来讨论此方法。
原文地址:http://blog.csdn.net/shangguanwaner/article/details/44348375