int | ||
CString | ||
BOOL | ||
CStringList | ||
CArray(int,int) | ||
CArray(double,double) |
s1 | s2 | s3 | s4 | ||||
get_int | |||||||
get_string | |||||||
get_string_size | |||||||
get_string_boody | |||||||
get_bool | |||||||
get_string_list | |||||||
get_list_size | |||||||
get_list_body | |||||||
get_item_n | |||||||
get_string_size | |||||||
get_string_body | |||||||
get_int_array | |||||||
get_array_size | |||||||
get_array_body | |||||||
get_double_array | |||||||
get_array_size | |||||||
get_array_body |
完全可以,我就这样做过。利用重载和宏,可让它更简单,可
bool read( bool v ,void* buf,size_t len);
bool read( int v,void* buf,size_t len);
bool read( std::string& v,void* buf,size_t len);
template< typename T>
bool read( std::list< T >& v ,void* buf,size_t len);
template< typename T>
bool read( std::vector< T >& v ,void* buf,size_t len);
template< typename K,typename V>
bool read( std::map< K,V >& v ,void* buf,size_t len );
然后,定义一些宏
#define READ_1( C,T ) bool read( C c , void* buf, size_t len){ read(
c.T, buf,len ); }
#define READ_2( C,T1,T2 ) bool read( C c , void* buf, size_t len){
read( c.T1,buf,len);read( c.T2,buf,len)
...
如果你要读一个结构时,可以如
class Tester
{
public:
int v1;
bool v2;
string v3;
};
READ_3( Tester, v1,v2,v3 );
这样还可嵌套定义。
由于我的项目是基于iocp的异步io,我想,只要读多于一个字节的数据,都不要奢望能够一次读全,所以要记录下中间状态,以指导下次有数据到来时的处理
我是这么做的,协议分两层,通讯层协议和应用层协议。你的“要记录下中间状态,以指导下次有数据到来时的处理”在我看来就是要处理半包和粘包的问题,这个我是通过通讯层协议完成的。比如定义通讯层的包格式为:
| proto_flag | data_length | data |
你要有一个用于拼包和拆包的buf,大小通常是最大包size的两倍,当收到数据后先和该buf中的数据append,然后从buf头开始,根据proto_flag,
data_length,
data分析出完整的通讯层数据包,依次进行,可能拆出多个或者0个,最后可能有不完整的剩余数据,那就把这段数据copy到buf的首地址,等待下次收到数据并合并后再处理。上面说的是通讯层协议的工作,对于拆分出来的完整的数据包,应该copy
or ref
给应用层的协议层,应用层的协议格式和你列出来的东东差不多,eg:
| command | parameters |
对于POD来说,应用层协议可以根据command把parameters
memncpy 给你指定的struct or
class变量,对于非POD,它还要处理深拷贝的问题。
所以,我觉得你的问题(似乎要记录很多的中间状态)是由于你没有把协议分层造成的,你的中间状态只存在于通讯层,它是很简单的,一个append_buf,一个pending_data_length就可以解决了,应用层是不存在该问题的。
不知道我是不是理解了你的问题,希望有用吧。
谢谢您!
您的作法是建立在有通讯层协议支持的基础上的,由于我要读取数据的另外一端的通讯层我是不能动的,牵涉到十几个版本和遍布世界的客户,原先的通讯作的很糟糕,数据包并没有加上分割标志或包的长度等信息,包的长度是不定的,由尤其是CStringList字段,我无法在搞清List长度和每一个String长度的情况下,得知整个StringList数据的长度,(苦啊)
由于无法得知整个包的长度,不得已将整个包的逻辑分析下移到通讯层来做
再次谢谢您!
原来是这样……,那你只好把发来的数据当成一个字节流来处理了。我观察了你的数据结构,如果这6个域是固定的,那么可以用一个N叉树来描述它,什么时候这6个域的数据收齐了,一个完整的数据包就得到了。你的问题是,由于网络的问题,这棵数的任何一个节点的数据都可能是不完整的,所以我建议:用N叉树来管理数据的接收状态,树的每个节点同时保存位于该节点的数据,以及一个表示该节点数据是否完成的bool标识。当收到新数据时,首先遍历这棵树,根据这个bool值定位到第一个等于false的节点,然后把收到的数据和这个节点的数据对接,取足这个节点需要的数据后,再把剩下的数据用于下一节点的处理,依次类推,直到整棵树结束,最后再从头遍历一次,把数据导出到你的目标结构。因为每个结点的数据类型可能不同,可以考虑用boost::any。
good luck
这是个不错的思路!
多谢指教!
对于通讯用来发送和接收的缓冲区,一般是用缓存池技术管理比较高效,而且提高了系统的稳定性,假如数据包最大为4k,考虑有1k用户的连接的情况,那么程序初始化时,至少需要4K*3*1024为12M,其中4M用于接收数据缓存,8M用于处理包的整理缓存,OK,没有问题
但是,如果不可避免要用到大于1M的数据包(比如传输数据库中整个表),那么用于缓存的数据就是1M*3*1024为3个G了,显然是不现实的,而且绝大多数数据包的大小仅有几十至几百个字节,为每个连接投递3M的缓存,简直是太浪费了,不知大家是如何考虑的,欢迎讨论!
谢谢
vector<LittleBuffer> m_littleBuffers;
vector<MiddleBuffer> m_middleBuffers;
vector<BigBuffer> m_bigBuffers;
}
为了提高效率,你可以用3个stack来管理空闲buffer,这样就省去了查询的开销。
您好!
我也有类似的想法,这样当发现一个大包时,必须有一个交换缓冲区的过程了。
如果有粘包,处理就会更复杂,可能限定每次接收的大小来避免粘包的发生,逻辑会简单一点,但是性能损失就会大一点。
谢谢您!
-----Original Message-----
From: dev4s...@googlegroups.com [mailto:dev4s...@googlegroups.com]On
Behalf Of Collins
Sent: 2006年5月24日 10:20
To: 高性能网络编程邮件列表
Subject: Re: 关于通讯的buffer管理
补充一句,由于是多线程环境,所以Get/Put函数实现时要加锁,所以这是一个以时间换空间的作法。
您好!
我是用Cycle queue 来管理空闲缓冲区的。
-----Original Message-----
From: dev4s...@googlegroups.com [mailto:dev4s...@googlegroups.com]On
Behalf Of Collins
Sent: 2006年5月24日 10:14
To: 高性能网络编程邮件列表
Subject: Re: 关于通讯的buffer管理