本文共 3757 字,大约阅读时间需要 12 分钟。
http://hi.baidu.com/qingshanyin/blog/item/4235a6a9d1dc13fa1f17a217.html
1、前言
ACE的IPC包按照组关联在一起:
连接器(ACE_SOCKET_Connector):主动建立连接--将流连接到服务器端
接收器(ACE_SOCKET_Acceptor):被动建立连接
流(ACE_SOCKET_Stream):传输数据
地址(ACE_INET_Addr):定义对端点进行寻址的手段
2、一个简单的客户如下:
// BaseSocket_Practice.cpp : 定义控制台应用程序的入口点。
// #define ACE_NTRACE 0 #include "ace/Containers.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Stream.h" #include "ace/SOCK_Connector.h" #include "ace/Log_Msg.h"#include "ace/streams.h"//消息流文件重定向
#include "ace/Log_Record.h"//ACE_Log_Record对象定义 #include "ace/SString.h" int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { ACE_OSTREAM_TYPE *output=new std::ofstream("client.test"); ACE_LOG_MSG->msg_ostream(output); ACE_LOG_MSG->set_flags(ACE_Log_Msg::OSTREAM);//日志重定向到文件 ACE_LOG_MSG->clr_flags(ACE_Log_Msg::STDERR);//禁用标准设备输出ACE_TRACE(ACE_TEXT("client main"));
//服务器地址对象:服务器监听端口5000,服务器计算机名称:"localhost"
//端口为主机字节顺序,地址家族未定(AF_UNSPEC) ACE_INET_Addr srvr(5000,ACE_LOCALHOST);//客户端连接器
ACE_SOCK_Connector connector;//将被连接到服务器的流对象,若客户端与服务器端连接成功,
//它会进入已连接状态,我们可以用它来与服务器通信 ACE_SOCK_Stream peer;//连接服务器端
//连接参数:TCP协议,阻塞模式进行连接 //本地端口由本地计算机自动选取一个空闲的端口,若为对等通信(peer to peer),可能要自行指定本地端口 if(-1==connector.connect(peer,srvr)) { //连接失败,函数返回 ACE_ERROR_RETURN((LM_ERROR,ACE_TEXT("%p/n"),ACE_TEXT("connect")),1); }ACE_DEBUG((LM_DEBUG,ACE_TEXT("connect succed./n")));
ACE_DEBUG((LM_DEBUG,ACE_TEXT("send string.../n")));
//链接成功开始通信
//发送7个字节的数据 //send_n使用事务发送数据,避免“短写”问题 //未设置超时参数,阻塞通信模式发包 peer.send_n("uptime/n",7);ACE_DEBUG((LM_DEBUG,ACE_TEXT("recive string.../n")));
int bc;
iovec buf; //接收信息,recvv自动分配连续缓冲区,存放数据 //未设置超时参数,阻塞通信模式收包 bc=peer.recvv(&buf);ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s"),ACE_reinterpret_cast(char*,buf.iov_base)));
//sendv()函数则用于以原子方式发送非连续的缓存区
iovec send[3]; send[0].iov_base=ACE_const_cast(ACE_TCHAR*,"up"); send[0].iov_len=2; send[1].iov_base=ACE_const_cast(ACE_TCHAR*,"time"); send[1].iov_len=4; send[2].iov_base=ACE_const_cast(ACE_TCHAR* ,"/n"); send[2].iov_len=1;//以原子方式发送所有数据
peer.sendv(send,3);iovec receive[3];
receive[0].iov_base=new char[2]; receive[0].iov_len=2; receive[1].iov_base=new char[4]; receive[1].iov_len=4; receive[2].iov_base=new char[1]; receive[2].iov_len=1;//以原子方式接收所有数据
bc=peer.recvv(receive,3);//处理接收到的数据
for(int i=0;i<3&&bc>0;i++) { size_t wc=receive[i].iov_len; if(ACE_static_cast(size_t,bc)<wc) { wc=ACE_static_cast(size_t,bc); } ACE_DEBUG((LM_DEBUG,ACE_TEXT("%s/n"),ACE_reinterpret_cast(char*,receive[i].iov_base))); //清除对象,否则将导致内存泄漏 delete[](ACE_reinterpret_cast(char*,receive[i].iov_base)); } //通信完毕,关闭流 peer.close();ACE_DEBUG((LM_DEBUG,ACE_TEXT("Socket closed./n")));
ACE_LOG_MSG->clr_flags(ACE_Log_Msg::OSTREAM);//非常重要,否则最后一条日志将尝试在已经删除的output对象上写
delete output;//释放内存 return 0; }1、端口复用复习
因为在Socket的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分。这种多重绑定便称之为端口复用。
这需要在Connect中指定本地端口,以及给第五个参数设定一个非零值,Connect就会为该连接设置为“端口复用”。
注意:端口复用仅仅适用于不同进程之间进行使用,同一进程中不同线程之间不得应用端口复用进行通信。因为不同计算机之间通信的识别决定于三个要素: 计算机IP地址、进程、端口。因此不同进程之间,虽然共享端口,与同意服务器进行通信,却不会至于在通信中遇到混乱。在网上曾经看到有位程序员在一个进程 中开了两个线程,建立了两个本地Socket绑定到相同端口,和同一台服务器通信,当然会运行出错的。因为这种情况下,两个Socket之间,三个要素完 全相同,通信肯定存在问题。
2、小结
(1)ACE将连接对象和通信对象分离,使得职责清晰,避免了常见Socket的常识错误:直接在连接对象上进行通信;
(2)send_n()和recv_n()解决通信的“短读”和“短写”问题;
(3)sendv()和recvv()可实现非连续缓冲区数据的发送和接收,同时recvv()也可以接收数据多少未知(但尺寸不是大得不至于无法放进合理的存储空间中)。
(4)可以简单地实现,支持阻塞连接和非阻塞连接;
(5)可以简单地实现,支持阻塞数据发送;
(6)简单设置,可实现端口复用。
3、复习C++:static_cast和reinterpret_cast的区别
1、C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为隐式类型转换使用。比如:
int i; float f = 166.7f; i = static_cast<int>(f); 此时结果,i的值为166。 2、C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。比如: int i; char *p = "This is a example."; i = reinterpret_cast<int>(p); 此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,一个明显的现象是在转换前后没有数位损失。转载地址:http://qvgxi.baihongyu.com/