今天调BUG的时候,突然发现boost的streambuf怎么不好使了,明明consume了传输掉的字节,为什么客户端收到的数据仍然是之前的?
开始怀疑是sputn这个手动向streambuf里写数据的函数的问题,因为这个函数是std::streambuf类下的成员函数,而boost::steambuf继承自它。
浪费了老半天时间google,没有查到有人犯这个问题的。
于是写了段代码测试:
char* ch = "123456";
boost::asio::streambuf buf;
buf.sputn(ch, 6);
std::cout<<buf.size()<<std::endl;
std::cout<<boost::asio::buffer_cast<const char*>(buf.data())<<std::endl;
buf.consume(1);
std::cout<<buf.size()<<std::endl;
std::cout<<boost::asio::buffer_cast<const char*>(buf.data())<<std::endl;
buf.consume(5);
char* cc = "123";
buf.sputn(cc, 3);
std::cout<<buf.size()<<std::endl;
std::cout<<boost::asio::buffer_cast<const char*>(buf.data())<<std::endl;
发现完全没问题,奇了个怪。最后发现居然是客户端接收的地方没写consume!因为客户端接收也是用的streambuf,因为一时粗心,导致浪费了大量时间debug,以后写代码还是不能图快啊。
其实主要是因为对boost的streambuf不太熟悉导致的。这里简要说明一下这个boost::streambuf。
- 这是一个与vector机理一致的动态缓存
- gptr返回一个头指针,pptr返回一个尾指针
- consume实际上只是向后移动了gptr的位置
- prepare的作用是确保输出缓冲区足够大,也就是一个resize(Get a list of buffers that represents the output sequence, with the given size.)
- commit实际上是向后移动了pptr的位置
- sputn会直接向后移动pptr的位置,不需要再次调用commit
- 跟vector一样,当到达maxsize之后,会使用allocator重新分配一段两倍的内存空间,然后将gptr到pptr之间的内容move到上面
另外网络程序的DEBUG,得时刻考虑服务端和客户端的对称性,在收发上出现了bug,可能的原因至少会有两处。
#include <boost/asio.hpp>
#include <iostream>
#include <iterator>
#include <functional>
#include "DataBuffer.h"
#include <boost/bind.hpp>
#include <boost/date_time.hpp>
#include <thread>
using namespace boost;
using namespace boost::asio;
using namespace std;
DataBuffer read_buf, write_buf;
string timestamp() {
std::string strTime = boost::posix_time::to_iso_string(boost::posix_time::microsec_clock::local_time());
int pos = strTime.find('T');
strTime.replace(pos,1,std::string("-"));
strTime.replace(pos + 3,0,std::string(":"));
strTime.replace(pos + 6,0,std::string(":"));
return strTime;
}
void handle_read(ip::tcp::socket& sock, const boost::system::error_code& err, size_t byte_transferred) {
if(err) {
std::cout<<err.message()<<std::endl;
boost::system::error_code ignored_ec;
sock.shutdown(ip::tcp::socket::shutdown_both, ignored_ec);
sock.close(ignored_ec);
if(ignored_ec) {
std::cout<<ignored_ec.message()<<std::endl;
}
return;
}
void Read(ip::tcp::socket& sock);
read_buf.Retrive(byte_transferred);
const char* content = read_buf.Peek();
cout << "收到消息:";
for(size_t i = 0; i < byte_transferred; ++i) {
cout << (int)content[i]<<"\t|";
}
cout << endl;
// 这时候strTime里存放时间的格式是YYYYMMDDTHHMMSS,日期和时间用大写字母T隔开了
cout << "接收时间:" << timestamp() << endl;
read_buf.Consume(byte_transferred);
Read(sock);
}
void Read(ip::tcp::socket& sock) {
//若用async_read,则一定要读满缓冲区才会执行句柄
sock.async_read_some(read_buf.prepare(), boost::bind(handle_read, boost::ref(sock), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void Write(ip::tcp::socket& sock) {
void handle_write(ip::tcp::socket& sock, const boost::system::error_code& err, size_t byte_transferred);
async_write(sock, write_buf.Data(), boost::bind(handle_write, boost::ref(sock), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void handle_write(ip::tcp::socket& sock, const boost::system::error_code& err, size_t byte_transferred) {
if(err) {
std::cout<<err.message()<<std::endl;
boost::system::error_code ignored_ec;
sock.shutdown(ip::tcp::socket::shutdown_both, ignored_ec);
sock.close(ignored_ec);
if(ignored_ec) {
std::cout<<ignored_ec.message()<<std::endl;
}
return;
}
write_buf.Consume(byte_transferred);
if(!write_buf.IsEmptyBuf())
Write(sock);//当写缓冲区非空时,继续调用异步写
}
void writebuf(ip::tcp::socket& sock, const void* data, std::size_t len) {
bool emptybefore = true;
if(!write_buf.IsEmptyBuf()) emptybefore = false;
write_buf.Append(data, len);
if(emptybefore) {//若写缓冲之前缓冲为空,则启动异步写
Write(sock);
}
}
void cinrun(ip::tcp::socket& sock) {
while(true) {
string line;
istream_iterator<int> int_it(cin);
if(*int_it == 1024) break;
while(*int_it != 1024) {
line += (char)(*int_it++);
}
writebuf(sock, line.c_str(), line.size());
cout << "发送消息:" ;
ostream_iterator<int> out_it(cout,"\t|");
for(auto c : line) {
*out_it++ = c;
}
cout<<endl;
cout << "发送时间:" << timestamp() << endl;
//Read(sock);
//sock.read_some(read_buf.prepare());
}
}
int main() {
cout<<"测试用客户端程序"<<endl;
cout<<"Input 1: 验证服务器逻辑流程"<<endl;
cout<<"Input 2: 验证服务器并发性能"<<endl;
int i;
cin>>i;
cin.ignore();
if(i == 1) {
io_service ios;
boost::asio::io_service::work work(ios);
std::thread ios_thread([&](){ios.run();});
ip::tcp::socket sock(ios);
ip::tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 8384);
sock.connect(ep);
Read(sock);
cinrun(sock);
}
}