测试用客户端

今天调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);
	}

}

你可能感兴趣的:(客户端)