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,一个明显的现象是在转换前后没有数位损失。