socket、TLI、STREAM管道和FIFO为访问局部和全局IPC机制提供广泛的接口。但是,有许多问题与这些不统一的接口有关联。比如类型安全的缺乏和多维度的复杂性会导致成问题的和易错的编程。ACE的IPC SAP类属提供了统一的层次类属,对那些麻烦而易错的接口进行封装。在保持高性能的同时,IPC SAP被设计用于改善通信软件的正确性、易学性、可移植性和可复用性。

IPC SAP类属


  根据底层使用的不同IPC接口,IPC SAP类被划分为四种主要的类属,图2-1描述了这一划分。ACE_IPC_SAP类提供的一些函数是所有IPC接口公有的。有四个不同的类由此类派生而出,每个类各自代表ACE包含的一种IPC SAP包装类属。这些类封装适用于特定IPC接口的功能。例如,ACE_SOCK类包含的功能适用于BSD socket编程接口,而ACE_TLI包装TLI编程接口。

  在这四个类的每一个类下面都有一整层次的包装类,它们完整地包装底层接口,并提供高度可复用、模块化、安全和易用的包装类。下面重点介绍一下最常用的socket类属。

socket类属(ACE_SOCK)


  该类属中的类都位于ACE_SOCK之下;它提供使用BSD socket编程接口的Internet域和UNIX域协议族的接口。这个类属中的类被进一步划分为:
  Dgram类和Stream类:Dgram类基于UDP数据报协议,提供不可靠的无连接消息传递功能。另一方面,Stream类基于TCP协议,提供面向连接的消息传递。
  Acceptor、Connector类和Stream类:Acceptor和Connector类分别用于被动和主动地建立连接。Acceptor类封装BSD accept()调用,而Connector封装BSD connect()调用。Stream类用于在连接建立之后提供双向的数据流,并包含有发送和接收方法。

 下表详细描述了该类属中的类以及它们的职责:

类名

职责

ACE_SOCK_Acceptor

用于被动的连接建立,基于BSD accept()和listen()调用。

ACE_SOCK_Connector

用于主动的连接建立,基于BSD connect()调用。

ACE_SOCK_Dgram

用于提供基于UDP(用户数据报协议)的无连接消息传递服务。封装了sendto()和receivefrom()等调用,并提供了简单的send()和recv()接口。

ACE_SOCK_IO

用于提供面向连接的消息传递服务。封装了send()、recv()和write()等调用。该类是ACE_SOCK_Stream和ACE_SOCK_CODgram类的基类。

ACE_SOCK_Stream

用于提供基于TCP(传输控制协议)的面向连接的消息传递服务。派生自ACE_SOCK_IO,并提供了更多的包装方法。

ACE_SOCK_CODgram

用于提供有连接数据报(connected datagram)抽象。派生自ACE_SOCK_IO;它包含的open()方法使用bind()来绑定到指定的本地地址,并使用UDP连接到远地地址。

ACE_SOCK_Dgram_Mcast

用于提供基于数据报的多点传送(multicast)抽象。包括预订多点传送组,以及发送和接收消息的方法

ACE_SOCK_Dgram_Bcast

用于提供基于数据报的广播(broadcast)抽象。包括在子网中向所有接口广播数据报消息的方法

  在下面的部分,我们将要演示怎样将IPC_SAP包装类直接用于处理进程间通信。

使用ACE的流


  ACE中的流包装提供面向连接的通信。流数据传输包装类包括ACE_SOCK_Stream和ACE_LSOCK_Stream,它们分别包装TCP/IP和UNIX域socket协议数据传输功能。连接建立类包括针对TCP/IP的ACE_SOCK_Connector和ACE_SOCK_Acceptor,以及针对UNIX域socket的ACE_LSOCK_Connector和ACE_LSOCK_Acceptor。

  Acceptor类用于被动地接受连接(使用BSD accept()调用),而Connector类用于主动地建立连接(使用BSD connect()调用)。

  下面的例子演示接收器和连接器是怎样用于建立连接的。该连接随后将用于使用流数据传输类来传输数据。

#include "ace/SOCK_Acceptor.h"
#include "ace/SOCK_Stream.h"
#include "ace/Log_Msg.h"
#define SIZE_DATA 18
#define SIZE_BUF 1024
#define NO_ITERATIONS 5 class Server
{
public:
///初始化
Server(int port): server_addr_(port),peer_acceptor_(server_addr_)
{
data_buf_= new char[SIZE_BUF];
} ///建立监听
int accept_connections()
{
if (peer_acceptor_.get_local_addr (server_addr_) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"%p\n","Error in get_local_addr"),1);
ACE_DEBUG ((LM_DEBUG,"Starting server at port %d\n",
server_addr_.get_port_number ()));
}
while(1)
{
ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT);
if (peer_acceptor_.accept (new_stream_, &client_addr_, &timeout)== -1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "accept"));
continue;
}
else
{
ACE_DEBUG((LM_DEBUG,
"Connection established with remote %s:%d\n",
client_addr_.get_host_name(),client_addr_.get_port_number()));
handle_read();
}
}
} ///读取流内容
int handle_read()
{
for(int i=0;i<NO_ITERATIONS;i++)
{
int byte_count=0;
if( (byte_count=new_stream_.recv_n (data_buf_, SIZE_DATA, 0))==-1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "Error in recv"));
}
else
{
data_buf_[byte_count]=0;
ACE_DEBUG((LM_DEBUG,"Server received %s \n",data_buf_));
}
}
if (new_stream_.close () == -1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "close"));
}
return 0;
}
private:
char *data_buf_; ACE_INET_Addr server_addr_;//地址 ACE_INET_Addr client_addr_; ACE_SOCK_Acceptor peer_acceptor_; ACE_SOCK_Stream new_stream_;//流 }; int ACE_TMAIN (int, ACE_TCHAR *[])
{ Server server(8002);
server.accept_connections(); return 0;
};

  上面的例子创建了一个被动服务器,侦听到来的客户连接。在连接建立后,服务器接收来自客户的数据,然后关闭连接。Server类表示该服务器。

  Server类包含的accept_connections()方法使用接受器(也就是ACE_SOCK_Acceptor)来将连接接受“进”ACE_SOCK_Stream new_stream_。该操作是这样来完成的:调用接受器上的accept(),并将流作为参数传入其中;我们想要接受器将连接接受进这个流。一旦连接已建立进流中,流的包装方法send()和recv()就可以用来在新建立的链路上发送和接收数据。还有一个空的ACE_INET_Addr client_addr_也被传入接受器的accept()方法,并在其中被设定为发起连接的远地机器的地址。

  在连接建立后,服务器调用handle_read()方法,它开始从客户那里读取一个预先知道的单词,然后将流关闭。对于要处理多个客户的服务器来说,这也许并不是很实际的情况。在现实世界的情况中可能发生的是,连接在单独的线程或进程中被处理。在后续章节中将反复演示怎样完成这样的多线程和多进程类型的处理。

  连接关闭通过调用流上的close()方法来完成,该方法会释放所有的socket资源并终止连接。

  下面的例子演示怎样与前面例子中演示的接受器协同使用连接器。

#include "ace/SOCK_Connector.h"
#include "ace/INET_Addr.h"
#include "ace/Log_Msg.h"
#define SIZE_BUF 128
#define NO_ITERATIONS 5 class Client
{
public:
Client(char *hostname, int port):remote_addr_(port,hostname)
{
data_buf_="Hello from Client";
} int connect_to_server()
{
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Starting connect to %s:%d\n",
remote_addr_.get_host_name(),remote_addr_.get_port_number())); if (connector_.connect (client_stream_, remote_addr_) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","connection failed"),-1);
}
else
{
ACE_DEBUG ((LM_DEBUG,"(%P|%t) connected to %s\n",
remote_addr_.get_host_name ()));
} return 0; } int send_to_server()
{
for(int i=0;i<NO_ITERATIONS; i++)
{
if (client_stream_.send_n (data_buf_,
ACE_OS::strlen(data_buf_)+1, 0) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","send_n"),0);
break;
}
}
close();
} int close()
{
if (client_stream_.close () == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","close"),-1);
}
else
{
return 0;
}
}
private:
ACE_SOCK_Stream client_stream_; ACE_INET_Addr remote_addr_; ACE_SOCK_Connector connector_; char *data_buf_; }; int ACE_TMAIN (int, ACE_TCHAR *[])
{ Client client("127.0.0.1", 8002);
client.connect_to_server();
client.send_to_server(); return 0;
};

  上面的例子演示的客户主动连接到服务器。在建立连接后,客户将单个字符串发送若干次到服务器,并关闭连接。

  客户由单个Client类表示。Client含有connect_to_server()和send_to_server()方法。

  Connect_to_server()方法使用类型为ACE_SOCK_Connector的连接器(connector_)来主动地建立连接。连接的设置通过调用连接器connector_上的connect()方法来完成:传入的参数为我们想要连接的机器的地址remote_addr_,以及用于在其中建立连接的空ACE_SOCK_Stream client_stream_。远地机器在例子的运行时参数中指定。一旦connect()方法成功返回,通过使用ACE_SOCK_Stream封装类中的send()和recv()方法族,流就可以用于在新建立的链路上发送和接收数据了。

  在此例中,一旦连接建立好,send_to_server()方法就会被调用,以将一个字符串发送NO_ITERATIONS次到服务器。如前面所提到的,这是通过使用流包装类的send()方法来完成的。

ACE网络编程:IPC SAP、ACE_SOCKET和TCP/IP通信实例的更多相关文章

  1. 网络编程的基本概念,TCP/IP协议简介

    8.1.1 网络基础知识 计算机网络形式多样,内容繁杂.网络上的计算机要互相通信,必须遵循一定的协议.目前使用最广泛的网络协议是Internet上所使用的TCP/IP协议. 网络编程的目的就是指直接或 ...

  2. Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信

    链接地址:http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html 使用TCP/IP的套接字(Socket)进行通信 套接字Socke ...

  3. Linux 网络编程详解五(TCP/IP协议粘包解决方案二)

    ssize_t recv(int s, void *buf, size_t len, int flags); --与read相比,只能用于网络套接字文件描述符 --当flags参数的值设置为MSG_P ...

  4. 《Unix网络编程》卷一(简介TCP/IP、基础套接字编程)

    通常说函数返回某个错误值,实际上是函数返回值为-1,而全局变量errno被置为指定的常值(即称函数返回这个错误值). exit终止进程,Unix在一个进程终止时总是关闭该进程所有打开的描述符. TCP ...

  5. 网络编程 套接字socket TCP UDP

    网络编程与套接字 网络编程 网络编程是什么: ​ 网络通常指的是计算机中的互联网,是由多台计算机通过网线或其他媒介相互链接组成的 ​ 编写基于网络的应用程序的过程序称之为网络编程. 网络编程最主要的工 ...

  6. golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期

    欢迎访问我的个人网站获取更佳阅读排版 golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期 | yoko blog (https://pengrl.com/p/47401/) 本篇文章部 ...

  7. linux高性能服务器编程 (四) --TCP/IP通信案例

    第四章 TCP/IP通信案例 HTTP代理服务器的大致工作原理        在HTTP通信链上,客户端和服务器之间通常存在某些中转代理服务器.它们提供对目标资源的中转访问.一个HTTP请求可能被多个 ...

  8. TCP/IP通信网络基础

    TCP/IP是互联网相关的各类协议族的总称. TCP/IP的分层管理 分层的优点:如果只有一个协议在互联网上统筹,某个地方修改就要把所有的部分整体换掉,采用分层则只需要改变相应的层.把各个接口部分规划 ...

  9. 第4章 TCP/IP通信案例:访问Internet上的Web服务器

    第4章 TCP/IP通信案例:访问Internet上的Web服务器 4.2 部署代理服务器 书中为了演示访问Internet上的Web服务器的全过程,使用了squid代理服务器程序模拟了一个代理服务器 ...

随机推荐

  1. java基础 Unsafe

    参考文章: https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html

  2. jboss_log4j.xml配置

    log4j是个优秀的开源的java日志系统,jboss内部也集成他,在jboss下默认的只是对server做了每日日志,并没有对你部署的项目进行每日的日志构建,但我们能通过修改jboss-log4j. ...

  3. 快速缓存刷新CDN节点的方法

    缓存刷新方式有 URL 刷新.目录刷新和 URL 预热.URL 刷新是以文件为单位进行缓存刷新.目录刷新是以目录为单位,将目录下的所有文件进行缓存刷新.URL 预热是以文件为单位进行资源预热. 刷新后 ...

  4. python asyncio 使用ThreadPoolExecutor和asyncio完成阻塞IO请求

    #使用多线程:在协程中集成阻塞io import asyncio from concurrent.futures import ThreadPoolExecutor import socket fro ...

  5. Layui新手教程----帮助小白少走弯路

    Layui的学习 Layui官方文档:https://www.layui.com/ 先说说为啥我接触到了layui,因为需要去参与做一个项目,被学长推荐去学习layui,用来处理一些前端的问题. La ...

  6. date——系统时间的命令

    这是一个可以用各种姿势获得各种时间的命令.最近在写自动化定时脚本时学了一下. 参考:https://www.cnblogs.com/ginvip/p/6357378.html 比如: 利用cronta ...

  7. 练手WPF(二)——2048游戏的简易实现(下)

    接着上一篇继续~~~ 6.动画显示增加分数 /// <summary> /// 动画显示增加得分 /// </summary> /// <param name=" ...

  8. Ext.NET-WebForm之TreePanel组件

    开启VS2017创建WebForm项目 打开NuGet搜索Ext.NET并安装 安装后可见 还自动帮我们创建了一个页面和文件夹项 打开自动添加的页面Ext.NET.Default.aspx,运行项目出 ...

  9. Java构造函数执行顺序

    首先执行基类的构造函数 然后执行派生类的构造函数之外的初始化语句 最后执行派生类的构造函数 在Java中,如果派生类构造函数需要调用基类的构造函数,那么基类构造函数必须作为派生类构造函数的第一句话.在 ...

  10. 【IPHONE开发-OBJECTC入门学习】文件的操作,读写复制文件

    转自:http://blog.csdn.net/java886o/article/details/9041547 FileTools.h FileTools.m #import "FileT ...