该类属中的类都位于ACE_SOCK之下;它提供使用BSD socket编程接口的Internet域和UNIX域协议族的接口。这个类属中的类被进一步划分为: Dgram类, Acceptor类和Stream类:Dgram类基于UDP数据报协议,提供不可靠的无连接消息传递功能。另一方面,Stream类基于TCP协议,提供面向连接的消息传递。 、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)抽象。包括在子网中向所有接口广播数据报消息的方法 |
ACE的Socket
使用ACE进行Socket编程,需要使用到下面几个类:
ACE_SOCK_Connector:连接器,主动建立连接,用于Socket Client;
ACE_SOCK_Acceptor:接受器,被动建立连接,用于Socket Server;
ACE_SOCK_Stream:传输数据的流,用于传输数据;
ACE_INET_Addr:用于表示通信端点的地址;
ace/INET_Addr.h文件中定义了一些有用的ACE_INET_Addr构造函数,用于创建通信端点的地址;一旦构造好一个通信端点的ACE_INET_Addr信息,那么,就可以使用这个地址去连接服务器了;ACE中使用ACE_SOCK_Stream类的对象来表示已经连接成功的TCP Socket;之所以这样命名,是因为TCP连接代表的是面向连接的虚连接,或者是"字节流";
短写问题:当你试图把一些字节写往远程主机时,由于网络缓冲区溢出、拥塞,或其它任何原因,导致你的字节没有被全部送出去,那么随后,你必须移动你的数据指针,发送剩余的数据;你必须持续地做这样的发送操作,直到把原来所有的自己全部都发送出去为;止.这样的问题在网络编程中发生的非常频繁,ACE的send_n()方法调用封装了这些操作,它会把所有这些重试操作都变成了它自己内部的事务,这样,只有把指定的字节全部都发送完,或者时发送时遇到错误,它才返回;
短读问题:当你试图从远程主机接收一些数据的时候,由于网络的拥塞、延迟等原因,会导致你不能一次性地接收到全部的数据;这个时候,你就必须通过计算已经接收到的数据的字节数,来接收剩余的数据;recv()方法,它将从对端读取最多n个字节的数据,并把这n个自己的数据放到自己的接收缓冲区中;当然,如果你确切地知道需要接收的数据的字节数,那么就必须处理"短读"问题;ACE提供了recv_n()方法调用为你解决了"短读"问题,它与send_n()方法一样,你必须告诉它需要读取的确切的字节数,它会在调用返回之前接收到你所指定的全部字节数的数据;
ACE_INET_Addr::set():这个方法比较灵活,使用它,可以修改地址对象的各个属性,这样可以重复使用一个地址对象,而不用创建多个地址对象;与ACE_INET_Addr的构造函数一样灵活;当set()调用失败的时候,set()方法返回-1,可以使用ACE_OS::last_error()检查错误码;Unix或类Unix中,ACE_OS::last_error()只是简单地返回errno的值,但是一在Windows中,它会调用GetLastError()函数来返回错误码;
ACE_SOCK_Connector::connet():主动连接服务器;连接失败,返回-1;连接成功,返回0;大多数情况下,你会让操作系统为你选择本地端口,ACE使用ACE_Addr::sap_any来表示这个值;但是在很多情况下,你可能想自己选择本地端口值;也就时说,我们在作为客户而主动连接服务器的时候,可以选择客户使用的本地端口;这是不很安全的保护应用的一种做法,但是可以在防止欺骗方面发挥作用;我们可以在我们的连接上设置服务质量参数,甚至是启动阻塞与非阻塞连接操作;对于Socket上面的操作,它们都支持为长时间运行的操作设置超时时间;与ACE_SOCK_Connector的connect方法一样,我们需要根据我们所需要的超时时间提供一个ACE_Time_Value类的对象;比如:send_n()、recv_n()等操作都可以接收一个ACE_Time_Value对象作为超时时间参数;
readv()、writev()分别与read()和write()的区别:
readv()和writev()与read()和write()的功能一样,都是系统调用,都是IO的读写系统调用,但是read()和write()必须用于连续的数据区域,而readv()和writev()则是可以用于不连续的数据区域或数据块;可以使用结构体iovec的数组来定义不连续的数据区;readv()和writev()以及结构iovec都是在BSD4.3操作系统中引入的,最常用于需要使用非连续的缓冲区来接收和发送数据的情况下;常见的例子就是,发送一个包头和一个与这个包头相关联的、存放在另外一个缓冲区中的数据;如果使用标准的系统调用write()的话,你必须连续两次调用write()来分别发送头和体,这样的操作比较麻烦,而且效率也跟不上;如果你能把头和体合在一起能按照一种原子方式写出去,或者是需要避开Nagle算法, 进行两次调用是不可接受的;如果你把头和体都复制到一个更大的缓冲区中,那么这在内存需求上不太现实;那么这个时候可以选择使用系统调用writev()和结构体iovec的数组来解决这个问题;writev()和ACE_SOCK_Stream::sendv()这两个方法会按照原子的方式把结构体iovec的数组中的所有条目都一一地发送出去;而readv()和ACE_SOCK_Stream::recvv()则与writev()和ACE_SOCK_Stream::sendv()相反,它们两个则是把接收到的数据依次填充到结构体iovec的数组中的每个条目所标记的不连续的缓冲区中,然后写指针移向下一个不连续的缓冲区的起始位置处;
typedef char* caddr_t; /* ?<core address> type */
struct iovec
{
int iov_len;
caddr_t iov_base;
};
成员iov_base可以指向内存映射文件区域、共享内存段或者是其它某个有意义的地方;你可以自己指定接收缓冲区地址,也可以让recvv()方法自动为你分配接收缓冲区,并用指针和缓冲区的长度来填充iovec结构,recvv()方法会计算到底有多少数据要接收,并分配一个大小刚好与要接收的数据的大小相同的缓冲区;如果你不清楚对方到底有多少数据要发送给你,但你相当地清楚,这些数据全都能放进一个尺寸合理的空间中,而且你希望这块空间是连续的,那么这个功能就可以使用上了;但是,一定要注意,由于是recvv()方法帮你分配的接收缓冲区,所以,在你使用完这些缓冲区之后,一定要记着释放这些内存空间,以避免内存泄露;
ACE_const_cast(type, variable);
ACE_static_cast(type, variable);
ACE_reinterpret_cast(type, variable);
这三个函数都是执行变量类型转换的,它们把变量variable的类型转换成type类型;
构建一个服务器:
要创建一个服务器,首先需要创建一个ACE_INET_Addr类的对象,以定义你想要用于侦听连接请求的端口.随后,需要使用一个ACE_SOCK_Acceptor对象在该端口上打开一个侦听器;ACE_SOCK_Acceptor::accept()方法会照管低层的细节,包括bind()、listen()、accept()等动作;如果accept()方法调用成功,那么会通过accept()的参数返回一个已经初始化成功的有效对端对象,它代表与客户端通讯的连接;
值得一提的是:在默认情况下,如果accept()方法被某一个UNIX信号中断了,那么它将会重启自身.对你的应用而言,这可能是合适的,也可能是不合适的;我们可以通过accept()方法的第四个参数来指定是否需要重启accept()方法自身,如果该参数的值为0,那么就表示,当accept()方法被信号中断之后,accept()方法不需要重启自身,而是返回-1,表示出错;如果该参数的值是1,则表示accept()方法被信号中断之后,accept()方法需要重启自身;
如果accept()方法调用成功,并返回了一个客户连接,那么在同时,它也会把已经连接上来的客户端的地址信息填充到一个ACE_INET_Addr对象中;ACE_INET_Addr::addr_to_string(),这个方法用于把IP地址转换成字符穿的形式来表示;