ACE
基本通信类
:
ACE_SOCK_Acceptor
职责: 接收器,负责接收连接请求并且创建一个 ACE_SOCK_Stream 与客户端进行通信
ACE_SOCK_Connector
职责: 连接器,负责主动连接并且创建一个 ACE_SOCK_Stream 与服务端进行通信
ACE_SOCK_Stream
职责: 通信流,负责两端的通信,Send 和 Revc
ACE按照职责来封装socket操作为三个类,网络通信程序基本都要使用这三个类来完成socket的接收,连接和数据发送接收。由于直接使用
socket句柄编写通信程序的时候很容易造成职责混淆,所以ACE提供职责明确的三个类完成相应的功能操作。
我们用代码例子来讲述
ACE如何使用这三个类,并且对比直接使用WINSOCK
例子代码:
ACE 服务器端代码
#include
"
ace/SOCK_Acceptor.h
"
#include " ace/SOCK_Stream.h "
#include " ace/INET_Addr.h "
// ACE_INET_Addr为网络地址类, nPort为监听的端口
ACE_INET_Addr aListenAddr(nPort);
ACE_SOCK_Acceptor aAcceptor;
// 打开监听关口 ACE下的功能执行成功后统一返回 0,失败返回 -1
If (aAcceptor.open(aListenAddr) == - 1 )
... {
//打开监听端口失败的处理过程
}
// 接收到的远程地址
ACE_INET_Addr remote_Addr;
// 接收到的通信流
ACE_SOCK_Stream aAcceptStream;
// 接收连接的方法将会阻塞
If (aAcceptor. Accept(aAcceptStream, remote_Addr) == - 1 )
... {
//接收失败
}
// 接收到的连接处理过程------------------------à
// aAcceptStream.recv_n 发送 或 aAcceptStream.send_n 接收数据
// aAcceptStream.close() 关闭连接
// 处理过程结束ß-----------------------------------
aAcceptor.close();
#include " ace/SOCK_Stream.h "
#include " ace/INET_Addr.h "
// ACE_INET_Addr为网络地址类, nPort为监听的端口
ACE_INET_Addr aListenAddr(nPort);
ACE_SOCK_Acceptor aAcceptor;
// 打开监听关口 ACE下的功能执行成功后统一返回 0,失败返回 -1
If (aAcceptor.open(aListenAddr) == - 1 )
... {
//打开监听端口失败的处理过程
}
// 接收到的远程地址
ACE_INET_Addr remote_Addr;
// 接收到的通信流
ACE_SOCK_Stream aAcceptStream;
// 接收连接的方法将会阻塞
If (aAcceptor. Accept(aAcceptStream, remote_Addr) == - 1 )
... {
//接收失败
}
// 接收到的连接处理过程------------------------à
// aAcceptStream.recv_n 发送 或 aAcceptStream.send_n 接收数据
// aAcceptStream.close() 关闭连接
// 处理过程结束ß-----------------------------------
aAcceptor.close();
WINSOCK
服务器端代码:
//
初始化Winsock,
WSADATA wsaData;
// Winsock 的版本, 建议用1.1 ,兼容性好
WORD wVersionRequested = MAKEWORD( 1 , 1 );
int nResult = WSAStartup(wVersionRequested, & wsaData);
if (nResult != 0 )
... {
//出错处理
}
// 监听端口-------------------------------------------
UINT nPort = 12345 ;
// 接口对象
SOCKET aSocket, serverSocket;
// 寻址相关结构
sockaddr_in serverSockaddr;
memset( & serverSockaddr, 0 , sizeof (serverSockaddr));
aSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (aSocket == INVALID_SOCKET)
... {
//出错处理
}
// 注意,该处非常重要,取值的正确与否决定关闭scoket后端口是否能正常释放
BOOL bOptVal = TRUE;
int bOptLen = sizeof (BOOL);
// 设置 socket 选项, SOL_SOCKET 和 SO_REUSEADDR 一起使用, 并且后面的参数如上,
关闭scoket后端口便能正常释放
setsockopt(aSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & bOptVal, bOptLen);
// 寻址相关结构
sockaddr_in aSockaddr;
memset( & aSockaddr, 0 , sizeof (aSockaddr));
aSockaddr.sin_family = AF_INET;
aSockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
aSockaddr.sin_port = htons((u_short)nPort);
// 绑定: 注意参数的类型转换
if (bind(aSocket,(sockaddr * ) & aSockaddr, sizeof (aSockaddr)) == SOCKET_ERROR)
... {
//出错处理
}
// 监听
if (listen(aSocket, 10 ) == SOCKET_ERROR)
... {
//出错处理
}
while (TRUE)
... {
//接收外部连接, 非阻塞
serverSocket = accept(aSocket, (sockaddr *)&serverSockaddr, 0);
if(serverSocket == INVALID_SOCKET)
...{
continue;
}
else
...{
char szRecvMsg[256] = ......{0};
char szOutMsg[256] = ......{0};
//接收客户端内容: 阻塞
recv(serverSocket, szRecvMsg, 256, 0);
//发送内容给客户端
send(serverSocket, "Have Receive The Msg", 50, 0);
//关闭
losesocket(serverSocket);
}
}
// 关闭
closesocket(aSocket);
closesocket(serverSocket);
// 当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放
WSACleanup();
WSADATA wsaData;
// Winsock 的版本, 建议用1.1 ,兼容性好
WORD wVersionRequested = MAKEWORD( 1 , 1 );
int nResult = WSAStartup(wVersionRequested, & wsaData);
if (nResult != 0 )
... {
//出错处理
}
// 监听端口-------------------------------------------
UINT nPort = 12345 ;
// 接口对象
SOCKET aSocket, serverSocket;
// 寻址相关结构
sockaddr_in serverSockaddr;
memset( & serverSockaddr, 0 , sizeof (serverSockaddr));
aSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (aSocket == INVALID_SOCKET)
... {
//出错处理
}
// 注意,该处非常重要,取值的正确与否决定关闭scoket后端口是否能正常释放
BOOL bOptVal = TRUE;
int bOptLen = sizeof (BOOL);
// 设置 socket 选项, SOL_SOCKET 和 SO_REUSEADDR 一起使用, 并且后面的参数如上,
关闭scoket后端口便能正常释放
setsockopt(aSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & bOptVal, bOptLen);
// 寻址相关结构
sockaddr_in aSockaddr;
memset( & aSockaddr, 0 , sizeof (aSockaddr));
aSockaddr.sin_family = AF_INET;
aSockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
aSockaddr.sin_port = htons((u_short)nPort);
// 绑定: 注意参数的类型转换
if (bind(aSocket,(sockaddr * ) & aSockaddr, sizeof (aSockaddr)) == SOCKET_ERROR)
... {
//出错处理
}
// 监听
if (listen(aSocket, 10 ) == SOCKET_ERROR)
... {
//出错处理
}
while (TRUE)
... {
//接收外部连接, 非阻塞
serverSocket = accept(aSocket, (sockaddr *)&serverSockaddr, 0);
if(serverSocket == INVALID_SOCKET)
...{
continue;
}
else
...{
char szRecvMsg[256] = ......{0};
char szOutMsg[256] = ......{0};
//接收客户端内容: 阻塞
recv(serverSocket, szRecvMsg, 256, 0);
//发送内容给客户端
send(serverSocket, "Have Receive The Msg", 50, 0);
//关闭
losesocket(serverSocket);
}
}
// 关闭
closesocket(aSocket);
closesocket(serverSocket);
// 当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放
WSACleanup();
ACE
客户端代码:
#include
"
ace/SOCK_Connector.h
"
#include " ace/SOCK_Stream.h "
#include " ace/INET_Addr.h "
ACE_SOCK_Connector aConnector;
ACE_SOCK_Stream aConnStream;
// 连接的地址 nPort为端口,szIP为地址字符串
ACE_INET_Addr aConnAddr(nPort, szIP);
ACE_TCHAR szSend[SEND_BYTE_COUNT] = ... {0} ;
if (aConnector.connect(aConnStream, aConnAddr) == - 1 )
... {
//错误处理
}
// 创建的连接处理过程----------à
// aConnStream.send_n 发送数据 或 aConnStream.revc_n
// aConnStream.close
// 处理过程结束ß---------------
aConnector.close();
#include " ace/SOCK_Stream.h "
#include " ace/INET_Addr.h "
ACE_SOCK_Connector aConnector;
ACE_SOCK_Stream aConnStream;
// 连接的地址 nPort为端口,szIP为地址字符串
ACE_INET_Addr aConnAddr(nPort, szIP);
ACE_TCHAR szSend[SEND_BYTE_COUNT] = ... {0} ;
if (aConnector.connect(aConnStream, aConnAddr) == - 1 )
... {
//错误处理
}
// 创建的连接处理过程----------à
// aConnStream.send_n 发送数据 或 aConnStream.revc_n
// aConnStream.close
// 处理过程结束ß---------------
aConnector.close();
WINSOCK
客户端代码:
WSADATA wsaData;
// Winsock 的版本, 建议用1.1 ,兼容性好
WORD wVersionRequested = MAKEWORD( 1 , 1 );
int nResult = WSAStartup(wVersionRequested, & wsaData);
if (nResult != 0 )
... {
//出错处理
}
// 连接端口-------------------------------------------
UINT nPort = 12345 ;
// 接口对象
SOCKET aSocket, serverSocket;
// 寻址相关结构
sockaddr_in serverSockaddr;
memset( & serverSockaddr, 0 , sizeof (serverSockaddr));
aSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (aSocket == INVALID_SOCKET)
... {
//出错处理
}
// 注意,该处非常重要,取值的正确与否决定关闭scoket后端口是否能正常释放
BOOL bOptVal = TRUE;
int bOptLen = sizeof (BOOL);
// 设置 socket 选项, SOL_SOCKET 和 SO_REUSEADDR 一起使用, 并且后面的参数如上,
// 关闭scoket后端口便能正常释放
setsockopt(aSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & bOptVal, bOptLen);
// 寻址相关结构
sockaddr_in aSockaddr;
memset( & aSockaddr, 0 , sizeof (aSockaddr));
aSockaddr.sin_family = AF_INET;
aSockaddr.sin_addr.s_addr = inet_addr(hostname);
aSockaddr.sin_port = htons((u_short)nPort);
if (connect(aSocket , ( struct sockaddr * ) & addr, sizeof (addr)) != 0 )
... {
//出错处理
}
// 接收到的连接处理过程------------------------à
// recv(aSocket , szRecvMsg, 256, 0);
// 发送 或 send(aSocket , "Have Receive The Msg", 50, 0);接收数据
// losesocket(aSocket );关闭连接
// 处理过程结束ß-----------------------------------
// 当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放
WSACleanup();
// Winsock 的版本, 建议用1.1 ,兼容性好
WORD wVersionRequested = MAKEWORD( 1 , 1 );
int nResult = WSAStartup(wVersionRequested, & wsaData);
if (nResult != 0 )
... {
//出错处理
}
// 连接端口-------------------------------------------
UINT nPort = 12345 ;
// 接口对象
SOCKET aSocket, serverSocket;
// 寻址相关结构
sockaddr_in serverSockaddr;
memset( & serverSockaddr, 0 , sizeof (serverSockaddr));
aSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (aSocket == INVALID_SOCKET)
... {
//出错处理
}
// 注意,该处非常重要,取值的正确与否决定关闭scoket后端口是否能正常释放
BOOL bOptVal = TRUE;
int bOptLen = sizeof (BOOL);
// 设置 socket 选项, SOL_SOCKET 和 SO_REUSEADDR 一起使用, 并且后面的参数如上,
// 关闭scoket后端口便能正常释放
setsockopt(aSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & bOptVal, bOptLen);
// 寻址相关结构
sockaddr_in aSockaddr;
memset( & aSockaddr, 0 , sizeof (aSockaddr));
aSockaddr.sin_family = AF_INET;
aSockaddr.sin_addr.s_addr = inet_addr(hostname);
aSockaddr.sin_port = htons((u_short)nPort);
if (connect(aSocket , ( struct sockaddr * ) & addr, sizeof (addr)) != 0 )
... {
//出错处理
}
// 接收到的连接处理过程------------------------à
// recv(aSocket , szRecvMsg, 256, 0);
// 发送 或 send(aSocket , "Have Receive The Msg", 50, 0);接收数据
// losesocket(aSocket );关闭连接
// 处理过程结束ß-----------------------------------
// 当你使用完Winsock接口后,要调用下面的函数对其占用的资源进行释放
WSACleanup();
ACE
封装了非常多的底层细节
,
所以我们在编写网络应用程序的时候可以花更多的时间去处理业务逻辑问题而不用浪费时间在处理细节的问题上,并且使用
WINSOCK
编写网络应用程序不仅仅代码量多,并且容易出错,而且还不能移植到其它
OS
平台。
推荐
ACE
学习书籍
:
《
C++
网络编程,卷
1 --
运用
ACE
和模式消除复杂性》
电子工业出版社
详细介绍了基本的网络编程
ACE C++ Wrapper Facade
《
C++
网络编程,卷
2 –
基于
ACE
和框架的系统化复用》
电子工业出版社
详细介绍了
ACE
复用的框架
《
ACE
程序员指南
网络与系统编程的使用设计模式》
中国电力出版社