Boost.Asio c++ 网络编程翻译(27)

streambuf类
我之前说过,streambuf继承自std::streambuf。就像std::streambuf本身,它不能拷贝构造。
另外,它有一些额外的方法,如下:
streambuf([max_size,][allocator]):这个方法构造了一个streambuf对象。你可以选择指定一个最大的buffer大小和一个分配器,分配器用来在需要的时候分配/释放内存。
prepare(n):这个方法返回一个子buffer,用来容纳连续的n个字符。它可以用来读取或者写入。方法返回的结果可以在任何Boost.Asio处理read/write的自由函数中使用,而不仅仅是那些用来处理streambuf对象的方法。
data():这个方法以连续的字符串形式返回整个buffer然后用来写入。方法返回的结果可以在任何Boost.Asio处理写入的自由函数中使用,而不仅仅是那些用来处理streambuf对象的方法。
comsume(n):在这个方法中,数据从输入队列中被移除(从read操作)
commit(n):在这个方法中,数据从输出队列中被移除(从write操作)然后加入到输入队列中(为read操作准备)。
size():这个方法以字节为单位返回整个streambuf对象的大小。
max_size():这个方法返回最多能保存的字节数。
除了最后的两个方法,其他的方法不是那么容易理解。首先,大部分时间你会把streambuf以参数的方式传递给read/write自由函数,就像下面的代码片段展示的一样:
  1. read_until(sock, buf, "\n"); // 读取到buf中
       write(sock, buf); // 从buf写入
    
如果你想之前的代码片段展示的一样把整个buffer都传递到一个自由函数中,方法会保证把buffer的输入输出指针指向的位置进行增加。也就是说,如果有数据需要读,你就能读到它。比如:
  1. read_until(sock, buf, '\n');
       std::cout << &buf << std::endl;
    
上述代码会把你刚从socket写入的东西输出。下面的代码不会输出任何东西:
read(sock, buf.prepare(16), transfer_exactly(16) );
   std::cout << &buf << std::endl;
字节被读取了,但是输入指针没有移动,你需要自己移动它,就像下面的代码片段所展示的:
read(sock, buf.prepare(16), transfer_exactly(16) );
   buf.commit(16);
   std::cout << &buf << std::endl;
同样的,假设你需要从streambuf对象中写入,如果你使用了write自由函数,则需要像下面一样:
streambuf buf;
   std::ostream out(&buf);
   out << "hi there" << std::endl;
   write(sock, buf);
下面的代码会把hi there发送三次:
streambuf buf;
   std::ostream out(&buf);
   out << "hi there" << std::endl;
   for ( int i = 0; i < 3; ++i)
       write(sock, buf.data());
发生的原因是因为buffer从来没有被消耗过,因为数据还在。如果你想消耗它,使用下面的代码片段:
streambuf buf;
   std::ostream out(&buf);
   out << "hi there" << std::endl;
   write(sock, buf.data());
   buf.consume(9);
总的来说,你最好选择处理整个streambuf实例。如果需要调整则使用上述的方法。
尽管你可以在读和写操作时使用同一个streambuf,你建议你分开使用两个,一个读另外一个写,它会让事情变的简单,清晰,同时你也会避免很多可能导致的bug

处理streambuf对象的自由函数
下面的列表展示了Boost.Asio的自由函数也可以处理streambuf对象:
  • read(sock, buf[, completion_function]):这个方法把内容从socket读取到streambuf对象中。completion方法是可选的。如果有,它会在每次read操作成功之后被调用,然后告诉Boost.Asio这个操作是否完成(如果没有,它继续读取)。它的格式是:size_t completion(const boost::system::error_code & err, size_t bytes_transfered);,如果completion方法返回0,我们认为read操作完成了,如果非0,它表示下一次调用stream的read_some方法需要读取的最大的字节数
  • read_at(random_stream, offset, buf [, completion_function]):  这个方法从一个支持随机读取的stream中读取。注意它没有被应用到socket中(因为他们没有随机读取的模型,它们是单向的,一直向前)。
  • read_until(sock, buf, char | string | regex | match_condition): 这个方法一直读到满足一个特性的条件为止。或者是一个char类型的数据被读到,或者是一个字符串被读到,或者是一个目前读到的字符串能匹配的正则表达式,或者match_condition方法告诉我们需要结束这个方法。match_condition方法的格式是:pair<iterator,bool> match(iterator begin, iterator end); ,iterator代表 buffers_ iterator<streambuf::const_buffers_type>。如果匹配到,你需要返回一个pair(passed_end_of_match被设置成true)。如果没有匹配到,你需要返回pair(begin被设置为false)。
  • write(sock, buf [, completion_function]):  这个方法写入streambuf对象所有的内容。completion方法是可选的,它的表现和read()的completion方法类似:当write操作完成时返回0,或者返回一个非0数代表下一次调用stream的write_some方法需要写入的最大的字节数。
  • write_at(random_stream,offset, buf [, completion_function]): 这个方法用来向一个支持随机存储的stream写入。同样,它没有被应用到socket中。
  • async_read(sock, buf [, competion_function], handler):  这个方法是read()的异步实现,handler的格式为:void handler(const boost::system::error_code, size_t bytes) 。
  • async_read_at(radom_stream, offset, buf [, completion_function] , handler): 这个方法是read_at()的异步实现。
  •  async_read_until (sock, buf, char | string | regex | match_ condition, handler):  这个方法是read_until()的异步实现。
  • async_write(sock, buf [, completion_function] , handler):  这个方法是write()的异步实现。
  • async_write_at(random_stream,offset, buf [, completion_function] , handler):  这个方法是write_at()的异步实现。
我们假设你需要一直读到一个元音字母:
  1. streambuf buf;
       bool is_vowel(char c) {
    
           return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
       }
    
       size_t read_complete(boost::system::error_code, size_t bytes) {
           const char * begin = buffer_cast<const char*>( buf.data());
           if ( bytes == 0) return 1;
           while ( bytes > 0)
    
               if ( is_vowel(*begin++)) return 0;
    
               else --bytes;
           return 1;
    
       }
       ...
       read(sock, buf, read_complete);
    
这里需要注意的事情是访问read_complete()中的buffer,也就是buffer_cast<>和buf.data。
如果你想使用正则,上面的例子会更简单:

read_until(sock, buf, boost::regex("^[aeiou]+") ); 

或者我们修改例子来让match_condition方法工作起来:
   streambuf buf;
   bool is_vowel(char c) {
       return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
   }
   typedef buffers_iterator<streambuf::const_buffers_type> iterator;
   std::pair<iterator,bool> match_vowel(iterator b, iterator e) {
       while ( b != e)
           if ( is_vowel(*b++)) return std::make_pair(b, true);
       return std::make_pair(e, false);
   }
   ...
   size_t bytes = read_until(sock, buf, match_vowel);
当使用read_until,这里有个难点;你需要记住你已经读取的字节数,因为下层的buffer可能多读取了一些字节(不像使用read()时)。比如:
std::cout << &buf << std::endl;
上述代码输出的字节可能比read_until读取到的多。

你可能感兴趣的:(C++,异步,网络编程,boost,asio)