开始搞实验的杂谈(三)-------主要关于第一个比较完整的boost支撑的client和单线程server
第一:好久不见关键字explicit,额,其实我自己以前也没怎么用,这里就引用个链接来权当备忘吧:http://developer.51cto.com/art/201002/183398.htm
第二:为了调试的观察方便,我在每一个地方基本都加了输出的语句,包括构造函数,然后发现很神奇的一样东西就是在Connection的类在创建new一个指针的时候,request_parser的构造函数里面的那句文本会输出,而其他的不会,或许是因为在connection的构造函数中是这样的:
connection::connection(boost::asio::io_service& io_service, connection_manager& manager, request_handler& handler) :socket_(io_service), connection_manager_(manager), request_handler_(handler) { cout << "Now the connection constructor!\n"; }
只有顺带初始化了其他几个私有变量而没有初始化request_parser_这个私有变量,所以这个类的构造函数就被调用了,是这样的么?先记录下,也不知道有没用呢。
第三:boost的官网上的文档的例程是只有实现了简单的client那边发一个GET请求然后服务端那边收到之后就找到这个文件把内容读出来发送过去,然后我在client那里就加上把这些内容打印出来,这里有几点,我把根目录就随便设置成了c盘,反正这些与我事先整体算法思路无关的东西就先不顾及得太全了,包括线程的安全等等,然后windows下的路径名是不用双斜杠了在这里,我也不知道为什么,比如这里服务端就直接制定根目录 string doc_root = "I:"; 客户端就请求的时候 request_path = "/test.txt";就这样ok了
第四:现在要实现以下客户端上传一个文件给server那边的,几点吧,首先请求就是要用POST请求,然后要注意设置缓冲区的大小,然后注意分段传输,利用好content-length,然后有一个什么
Content-Type: multipart/form-data; boundary=---------------------------40612316912668 这个东西,我不知道之后用不用得上,先记录着。
第五:哎,我怎么那么笨呢,想了一个下午应该怎样在我自己的程序当中来表示出我写的request和response,其实想半天无非就是用两个类呗,然后但是在async_write的时候要用buffer的形式来写进socket里面啊,不能写个对象吧?因为这个又不是网络的标准,好吧,最终其实证明这些都是我自己很愚蠢的表现,因为其实在例程里面也有教我做法了,当时因为觉得reply的那个类比较多的东西都是很形式化的所以就有些没有自己动手写,所以就导致忽略了,先贴一段代码:
request_handler_.handle_request(request_, reply_); boost::asio::async_write(socket_, reply_.to_buffers(), boost::bind(&connection::handle_write, shared_from_this(), boost::asio::placeholders::error));
其实无非就是reply_.to_buffer()的这个函数了,reply是一个类来的,然后最终还是要搞成buffer的形式来写进socket里面去的,所以呢,就自己在类里面定义一个函数来写成那样的http通讯形式呗,当时就觉得这个to_buffer的函数很无聊,所以就直接复制没有自己动手写了,哎,好吧,偷懒总是要付出代价的!不过今天杭州下了一天的雨,空气应该会好很多了,前两天那个PM2.5的指数高得真心不适合人类居住啊。。。。
第六:嗯嗯,看了下那个to_buffers的函数,就是就是它本来自己有个vector变量的,用来放boost:asio:buffer,然后就把header的每个部分,content的全部,全部用这样就压进去了,然后return那个vector:
std::vector<boost::asio::const_buffer> reply::to_buffers() { std::vector<boost::asio::const_buffer> buffers; buffers.push_back(status_strings::to_buffer(status)); for (std::size_t i = 0; i < headers.size(); ++i) { header& h = headers[i]; buffers.push_back(boost::asio::buffer(h.name)); buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); buffers.push_back(boost::asio::buffer(h.value)); buffers.push_back(boost::asio::buffer(misc_strings::crlf)); } buffers.push_back(boost::asio::buffer(misc_strings::crlf)); buffers.push_back(boost::asio::buffer(content)); return buffers; }
话说我一直都不知道原来字符数组可以这样写:其实也就是表示一行,没有空格没有换行的,只不过看起来美观,而且就像是网页上的html原代码一样:
const char created[] = "<html>" "<head><title>Created</title></head>" "<body><h1>201 Created</h1></body>" "</html>";
第七:自己改了改例程里面的tobuffer函数,因为我自己写的client也需要用到,发送post请求无非就是跟例程里面的server的动作反过来而已,添加了分别生成get和post请求的两个函数,几点稍微注意下的,第一个就是那些什么string啊char* 啊 const buffer之类的转换,编译不通过的话就多试试几个好了,然后file.open那个函数第一个参数要用char*,所以这里我就用string。c_str来处理了,然后一个就是读写文件的时候,一般都是先用定义一个固定长度的缓冲区,char buf[500]例如,然后就每次读出或写入缓冲区容量大小的内容,比如用一个while循环,while(file.read(buf,sizeof(buf)).gcount>0),然后就继续读,当然这里我也因为基础比较拙计所以也想知道每次这个缓冲区是怎样工作的,首先就是定义的时候建议先初始化,这个也是标准的做法,比如 char buf【500】 = “”,这样,它就会默认留个500个空格字符在那里了,然后每次赋值的话它会完全覆盖掉上一次的内容,是从头开始覆盖,比如3个字符大小的buffer,总共要写进abcef5个字符,那么第一个循环过后就是abc,第二次之后就是efc。
第八:发现要自己改改它原本的那个parse函数,它的parse函数写得倒挺正规的,不过这个是为了解析任何时候的http请求而写的,而在我自己的实验当中,我完全可以自己定好固定的格式来,比如不会有任何的bad request,然后第一行是 request line,应答报文的话第一行的是status line,请求报文的第二行开始就是一些headers,而规定好了如果是get请求的话,就要哪几个header就足够,如果是post请求的话那么就加上content type和content length,然后在空出一行之后,就是content的内容,以string转buffer之后的形式来标示,,不过我还是先保留着人家例程的parse,我自己再写一个便是了。然后为了统一,在原本的例程的request的基础上加上了 unsigned long content_length和string content 这两个属性吧。
额,不知不觉又好长了,新开一篇!