QQ交流群:198941541
file_client演示了运行时替换解包器,这是这篇文章的重头戏。首先,为什么要运行时替换解包器,那是因为我们在传输文件之前是命令模式,消息是有协议的,到了文件传输的时候,我们切换到传输模式,消息是流式无协议的,这两种情况下,解包器是完全不一样的,ftp传输也采用类似的办法,只是它把命令和数据分在了不同的连接上进行。为了运行时替换解包器,必须定义宏ASCS_PASSIVE_RECV,这个宏的意思是由使用者触发消息的接收(异步的),连接建立起来之后的第一次接收除外。所以大家可以看到,在on_msg,on_msg_handle里面都有recv_msg调用(不定义宏ASCS_PASSIVE_RECV的话,recv_msg不可访问),替换解包器必须要在recv_msg之前,替换之后,老的解包器将被释放,你不应该保留解包器里面的任何指针或者引用,否则就要注意在替换解包器之后,它们会变成野指针。替换解包器代码为:
unpacker(std::make_shared(...));
这里的file_unpacker就是新的解包器,它和默认解包器都继承自i_unpacker,所以可以替换。file_unpacker直接在parse_msg里面处理消息(写入文件),并不返回任何消息,然后回到on_msg或者on_msg_handle里面再次调用recv_msg,注意,如果定义了宏ASCS_PASSIVE_RECV,当解包器未出错但一个消息也没返回时,ascs会自动添加一个空消息到接收队列(因为我们需要派发空消息来触发下一次recv_msg调用),我们需要一个空消息用来在handle_msg函数里面判断接收是否完毕:
void handle_msg(out_msg_ctype& msg){if (TRANS_BUSY == state){assert(msg.empty());auto unp = std::dynamic_pointer_cast(unpacker());if (!unp || unp->is_finished())trans_end();return;}...}
但如果定义了宏ASCS_SYNC_DISPATCH,ascs并不会自动添加空消息,所以在on_msg里面,如果msg_can为空,我们新建了一个空消息用来调用handle_msg。数据接收完毕之后,将解包器换回到默认解包器,并进入下一轮(命令模式)。
unpacker(std::make_shared());
下面看看file_server&#xff0c;它也工作在两种模式之下&#xff0c;但它不需要替换解包器&#xff0c;因为它只会解命令然后发送文件内容&#xff0c;不会接收流式数据&#xff1b;它也不需要替换打包器&#xff0c;因为只有命令模式下才需要打包&#xff0c;传输模式下无需打包&#xff0c;当然你也可以写一个流式数据打包器&#xff0c;但在file_server这种情况下&#xff0c;显得有点多余&#xff0c;我们可以直接发送消息&#xff08;direct_send_msg&#xff09;&#xff0c;为此我们必须让命令模式下的打包器生成的数据类型&#xff0c;与调用direct_send_msg时的数据类型一样&#xff0c;所以我们用了这个打包器&#xff1a;#define ASCS_DEFAULT_PACKER packer2<>&#xff0c;它生成的消息类型是unique_buffer&#xff0c;direct_send_msg时&#xff0c;我们生成的消息类型是in_msg_type(new file_buffer(...))&#xff0c;可以看到&#xff0c;file_buffer是继承自i_buffer&#xff0c;所以它们的消息类型都是unique_buffer。那么当一个消息发送成功&#xff0c;要发送下一个时&#xff0c;如何知道当前是命令模式还是传输模式呢&#xff1f;可以如下&#xff1a;
void file_socket::on_msg_send(in_msg_type& msg)
{auto buffer &#61; dynamic_cast(&*msg.raw_buffer());if (nullptr !&#61; buffer){buffer->read();if (buffer->empty())trans_end();elsedirect_send_msg(std::move(msg), true);}
}
packer2<>生成的消息&#xff0c;真实类型是ascs::ext::string_buffer&#xff0c;虽然也继承自i_buffer&#xff0c;但它不可能成功转成file_buffer。更正&#xff1a;最新的file_server已经支持文件接收&#xff08;这样才合理&#xff0c;任何一个文件传输工具&#xff0c;都支持类似put和get&#xff0c;之前只支持get&#xff09;&#xff0c;那么file_server也需要替换解包器&#xff0c;实现方式和file_client差不多&#xff0c;可以参考&#xff0c;这里就不再赘述。
注意&#xff1a;demo主要还是演示运行时替换解包器&#xff0c;并不是演示最高效率&#xff0c;我们可以讨论一下如何提高file_client &#43; file_server的效率&#xff0c;首先可以用内存映射&#xff0c;解包器返回的缓存直接就是内存映射&#xff0c;写满再换到下一片&#xff08;如果文件太多一次映射不完的话&#xff09;&#xff0c;无需再解包&#xff0c;相当于让API recv直接写入文件&#xff0c;我收费为网友实现过类似的&#xff0c;效率高许多&#xff08;由于收了费&#xff0c;就不能把源代码放出来&#xff09;。其次我们可以考虑让文件读写与socket数据收发并行&#xff0c;那至少宏ASCS_PASSIVE_RECV就不能定义了&#xff0c;它是处理完消息再发起下一次数据读取。
上一篇&#xff1a;ascs demo解释&#xff08;四&#xff09;&#xff1a;pingpong_client &#43; pingpong_server, socket_management
下一篇&#xff1a;ascs demo解释&#xff08;六&#xff09;&#xff1a;其余demo