【ACE】ACE 框架简单了解C++的C/S框架(ADAPTIVE Communication Environment)

1、ACE 是一个C/S框架(ACE自适配通信环境(ADAPTIVE Communication Environment))

网络程序开发常遇见的问题:
a)    跨平台。这一点相对比较容易,主要是针对类似多线程、socket通信等操作,定义统一的接口,使用define实现。基本上,只要定义良好,使用起来就很方便,也不需要多少改动。
b)    Socket通信。在socket通信过程中,除了我们实际收发的数据,也有很多其他的数据需要处理,如keep-alive信息、socket关闭或者各种出错信息等。这些都大大增加了代码的复杂度。
c)    多线程。多线程操作socket或者数据,要考虑同步等问题
d)    对各种系统API的操作,特别是socket的操作,要有很多错误处理的代码,大大增加了代码复杂度。
e)    还有许多其他问题。

 

ACE的事件机制重构socket通信的功能。

首先来了解一下ACE框架,下面是ACE的体系结构图:

从这个图中,可以很明显的看出,ACE框架从底层往上,依次是C风格的OS适配层,也就是对不同的操作系统底层调用的封装;

上一层是C++的封装类,就是把各种系统调用和系统对象封装成C++类对象;

再往上就是框架层,主要就是Reactor, Acceptor, Connector和Proactor。

在上面就是ACE提供的一些服务组件

      从这个结构图中还可以看出,在C++封装层,ACE框架还为我们提供了进程、线程管理,日志记录,内存管理等模块。进程和线程管理可以用来方便的创建和管理进程、线程,还提供了各种机制实现线程同步。使用ACE的日志模块,我们可以很方便的记录不同级别的日志,并在实际运行过程中控制日志记录的级别。使用ACE的内存管理,我们可以预先分配一块内存,这样可以避免程序频繁的向OS请求内存,使得程序的性能无论从时间上还是空间上都能得到很大的提升。内存管理还提供了进程间共享内存的内存分配方式。

 

使用ACE进行C/S结构程序就是使用Reactor框架,实现我们的事件处理方法。下面是一个使用ACE开发的通信系统的简单的类图:

 

其中我们需要开发的部分主要就是Server_svc_handler和Client_svc_handler,这两个对象都是继承自ACE_svc_handler。用来处理建立(接受)连接,以及进行数据的通信和处理。


Server_acceptor是用于在服务器上接受连接,它与Server_svc_handler绑定,每当有新的连接时,ACE的Reactor框架就会创建一个Server_svc_handler的对象,然后把新连接的socket(ACE_SOCK_STREAM)绑定到这个handler上,以后就用这个handler处理这个socket上接收到的数据。


Client_connector与Acceptor类似,用于在客户端连接服务器,连接成功后,也由Reactor框架创建一个Client_svc_handler对象,并将它与连接后的socket绑定,来处理从服务器端接收到的数据。

下面就是几个类的代码。
Server_svc_handler:

 

 C++ Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

 

class Server_svc_handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH>

{

public:

    Server_svc_handler()

    {

        data = new char[DATA_SIZE];

    }

    int open(void *)

    {

        ACE_DEBUG((LM_DEBUG, "%P|%t, %s", " Connection established.\n")); //打印debug级别的log,%P%t表示进程号和线程号,%s是要打印的消息。

        ACE_Reactor::instance()->register_handler(this, ACE_Event_Handler::READ_MASK); //注册socket上可读事件的处理器,当当前的socket上有可读事件时,就会触发这个handler的handle_input方法。

        char *msg = "Client connected successfully in server.";

        peer().send(msg, strlen(msg));

        return 0;

    }



    int handle_input(ACE_HANDLE)

    {

        ssize_t cnt = peer().recv(data, DATA_SIZE);

        if (cnt <= 0)

        {

            ACE_DEBUG((LM_WARN, " No data received.\n")); //打印warn级别日志

        }

        else

        {

            ACE_DEBUG((LM_DEBUG, “ % s”, data));

            //在这里添加具体业务处理代码

            char *msg = "Request is processed successfully";

            peer().send(msg, strlen(msg));

        }

        return 0;

    }



private:

    char *data;

    static const int DATA_SIZE = 64;

};

下面是Client_svc_handler,它与server端的类似。

 

 C++ Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

 


class Agent_svc_handler : public ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH>

{

public:

    Agent_svc_handler()

    {

        data = new char[DATA_SIZE];

    }

    int handle_input(ACE_HANDLE)

    {

        peer().recv(data, DATA_SIZE);

        ACE_DEBUG((LM_DEBUG, “ % s”, data));

        return 0;

    }

private:

    char *data;

    static const int DATA_SIZE = 64;

};

你们可能会注意到,Server_svc_handler实现了open方法,但是client端的处理函数却没有,是因为,open方法在ACE_svc_handler中就已经有默认实现,就是将当前的handler进行注册:
        ACE_Reactor::instance()->register_handler(this,    ACE_Event_Handler::READ_MASK);
因为在服务器端接收到客户连接以后,给客户端发了一个回应,所以我们才重新实现。
接下来就是Server端的主程序server.cpp

 

 C++ Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

 


typedef ACE_Acceptor<Server_svc_handler, ACE_SOCK_ACCEPTOR> ServerAcceptor;

int main(int argc, char *argv[])

{

    ACE_DEBUG((LM_DEBUG, "Test server running...\n"));

    const int PORT_NUM = 12345;

    ACE_INET_Addr addr(PORT_NUM);

    ServerAcceptor acceptor(addr, ACE_Reactor::instance());

    ACE_Reactor::run_event_loop(); //会一直不停的监听注册的事件。

    return 0;

};

ServerAcceptor不需要实现,只需要用我们实现的handler定义就可以。

下面是客户端的代码client.cpp

typedef ACE_Connector<Client_svc_handler, ACE_SOCK_CONNECTOR> ClientConnector;

int main(int argc, char* argv[])

{

	const int SERVER_PORT_NUM = 12345;

	ACE_INET_Addr remoteAddr(SERVER_PORT_NUM, "localhost");

	ClientConnector connector;

	Client_svc_handler *handler= new Client_svc_handler;

	if(connector.connect(handler, remoteAddr) == -1 )

	{

		ACE_ERROR(LM_ERROR, "%P|%t, %s", "Connection failed");

	}

	ACE_Reactor::run_event_loop();

	return 0;

};

这个例子只是对socket的可读操作注册处理器,在Reactor框架中,我们可以对任何的I/O操作的读或者写进行处理,也可以对系统的信号量注册处理函数。

使用ACE框架,我们就可以把重心放在业务的处理上,而不用为底层的系统调用和错误处理费太多脑筋。上面的一个简单的服务器端和客户端通信的程序,代码总共加起来就30,40行,但是也实现了完整的服务器与客户端的交互,还是非常方便的。
转载:点击打开链接

转自:https://blog.csdn.net/suxinpingtao51/article/details/11800183

《Boost.Asio与ACE比较(C++)》:https://blog.csdn.net/legend050709/article/details/39584353

《C++开源TCP/IP网络开发框架:ACE从入门到精通V9.5》:https://blog.csdn.net/calmreason/article/details/50757535

 

posted on 2022-10-04 01:28  bdy  阅读(667)  评论(0编辑  收藏  举报

导航