ACE技术论文集-第3章 高效可移植和灵活的网络编程的C++包装
发布: 2008-6-13 14:57 | 作者: Prashant Jain and D | 来源: 转载 | 查看: 597次
这一部分通过使用ACE SOCK SAP C++包装开发一个客户/服务器流式应用来对它们进行演示。该应用是[1]中描述的ttcp程序的简化版本。为了比较,该应用还用socket进行了编写。为保持简短,例子中的大多数错误检查都被省略了。自然,健壮的程序应该检查库和系统调用的返回值。
图3-10和图3-11介绍一个用C编写的客户/服务器程序,它使用Internet域的socket和select来实现流应用。图3-11所示的服务器创建一个被动模式的侦听者socket,并等待客户与它连接。一旦连接,服务器接收来自客户的数据,并将其显示在它的标准输出流上。图3-10所示的客户端建立一个到服务器的TCP连接,并将它的标准输入流通过连接进行传输。客户使用非阻塞连接来限制它等待连接被接受或拒绝的时间数量。
大多数的返回值错误检查被省略了,以节省空间。但是,即使是要使这个简单的例子正确工作,所有socket初始化、网络寻址和流控制细节都必须被显式地编写;注意到这一点富有启发意义。而且,图3-10和3-11中的代码对于不同时支持socket和select的平台来说是不可移植的。
图3-12和3-13使用SOCK SAP来重新实现C版本的客户/服务器程序。该SOCK SAP程序实现了与图3-10和图3-11所介绍的相同的功能。与基于socket的C实现相比,SOCK SAP C++程序展示了下列好处:
增强的清晰性:例如,网络寻址和主机定位由图3-6所示的Addr类来处理,它隐藏了在图3-10和3-11中必须显式编写的微妙而又易错的细节。而且,非阻塞连接建立的低级细节是由SOCK Connector工厂来完成的。此外,模板traits的使用使在对参数化函数进行实例化时必须指定的类型参数的数目减到了最少。
增强的类型安全性:例如,ACE_SOCK_Acceptor和ACE_SOCK_Connector连接工厂创建ACE_SOCK_Stream对象,从而防止了在运行时发生图3-3所示的类型错误。
更小的程序大小:使主动和被动连接建立局限在ACE_SOCK_Acceptor和ACE_SOCK_Connector连接工厂中大量地减少了代码的行数。此外,为构造器和方法参数提供的缺省值减少了常见的使用模式所需的参数数目。
增强的可移植性:例如,由于使用了模板traits,在socket和TLI之间切换只需要将客户中的
send_data (s_addr);
改变为
send_data (s_addr);
以及将服务器中的
recv_data (s_addr);
改变为
recv_data (s_addr);
如3.8所示,可用条件编译指令来进一步使通信软件与对特定类型的网络编程接口的依赖去耦合。
#define PORT_NUM 10000
#define TIMEOUT 5
/* Socket client. */
void send_data (const char host[], u_short port_num)
{
struct sockaddr_in peer_addr;
struct hostent *hp;
char buf[BUFSIZ];
int s_sd, w_bytes, r_bytes, n;
/* Create a local endpoint of communication */
s_sd = socket (PF_INET, SOCK_STREAM, 0);
/* Set s_sd to non-blocking mode. */
n = fcntl (s_sd, F_GETFL, 0);
fcntl (s_sd, F_SETFL, n | O_NONBLOCK);
/* Determine IP address of the server */
hp = gethostbyname (host);
/* Set up address information to contact server */
memset ((void *) &peer_addr, 0, sizeof peer_addr);
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = port_num;
memcpy (&peer_addr.sin_addr,
hp->h_addr, hp->h_length);
/* Establish non-blocking connection server. */
if (connect (s_sd, (struct sockaddr *) &peer_addr,
sizeof peer_addr) == -1)
{
if (errno == EINPROGRESS)
{
struct timeval tv = {TIMEOUT, 0};
fd_set rd_sds, wr_sds;
FD_ZERO (&rd_sds);
FD_ZERO (&wr_sds);
FD_SET (s_sd, &wr_sds);
FD_SET (s_sd, &rd_sds);
/* Wait up to TIMEOUT seconds to connect. */
if (select (s_sd + 1, &rd_sds, &wr_sds, 0, &tv) <= 0)
perror ("connection timedout"), exit (1);
// Recheck if connection is established.
if (connect (s_sd, (struct sockaddr *) &peer_addr,
sizeof peer_addr) == -1 && errno != EISCONN)
perror ("connect failed"), exit (1);
}
}
/* Send data to server (correctly handles
"short writes" due to flow control) */
while ((r_bytes = read (0, buf, sizeof buf)) > 0)
for (w_bytes = 0; w_bytes < r_bytes; w_bytes += n)
n = write (s_sd, buf + w_bytes, r_bytes - w_bytes);
/* Close down the connection. */
close (s_sd);
}
int main (int argc, char *argv[])
{
char *host = argc > 1 ? argv[1] : "ics.uci.edu";
u_short port_num = htons (argc > 2 ? atoi (argv[2]) : PORT_NUM);
/* Send data to the server. */
send_data (host, port_num);
return 0;
}
图3-10 基于socket的客户例子
#define PORT_NUM 10000
/* Socket server. */
void recv_data (u_short port_num)
{
struct sockaddr_in s_addr;
int s_sd;
/* Create a local endpoint of communication */
s_sd = socket (PF_INET, SOCK_STREAM, 0);
/* Set up the address information for a server */
memset ((void *) &s_addr, 0, sizeof s_addr);
s_addr.sin_family = AF_INET;
s_addr.sin_port = port_num;
s_addr.sin_addr.s_addr = INADDR_ANY;
/* Associate address with endpoint */
bind (s_sd, (struct sockaddr *) &s_addr, sizeof s_addr);
/* Make endpoint listen for service requests */
listen (s_sd, 5);
/* Performs the iterative server activities */
for (;;)
{
char buf[BUFSIZ];
int r_bytes, n_sd;
struct sockaddr_in peer_addr;
int peer_addr_len = sizeof peer_addr;
struct hostent *hp;
/* Create a new endpoint of communication */
while ((n_sd = accept (s_sd, &peer_addr,
&peer_addr_len)) == -1 && errno == EINTR)
continue;
hp = gethostbyaddr (&peer_addr.sin_addr, peer_addr_len, AF_INET);
printf ("client %s\n", hp->h_name);
/* Read data from client (terminate on error) */
while ((r_bytes = read (n_sd, buf, sizeof buf)) > 0)
write (1, buf, r_bytes);
/* Close the new endpoint (listening endpoint remains open) */
close (n_sd);
}
/* NOTREACHED */
}
int main (int argc, char *argv[])
{
u_short port_num = htons (argc > 1 ? atoi (argv[1]) : PORT_NUM);
// Receive data from clients.
recv_data (port_num);
return 0;
}
图3-11 基于socket的服务器例子
static const int PORT_NUM = 10000;
static const int TIMEOUT = 5;
// SOCK_SAP Client.
template
void send_data (CONNECTOR::PEER_ADDR peer_addr)
{
// Data transfer object.
CONNECTOR::PEER_STREAM peer_stream;
// Establish connection without blocking.
CONNECTOR connector (peer_stream, peer_addr, ACE_NONBLOCK);
if (peer_stream.get_handle () == -1)
{
// If non-blocking connection is in progress,
// wait up to TIMEOUT seconds to complete.
Time_Value timeout (TIMEOUT);
if (errno != EWOULDBLOCK ||
connector.complete (peer_stream, peer_addr, &timeout) == -1)
perror ("connector"), exit (1);
}
// Send data to server (send_n() handles
// "short writes" correctly).
char buf[BUFSIZ];
for (int r_bytes; (r_bytes = read (0, buf, sizeof buf)) > 0;)
peer_stream.send_n (buf, r_bytes);
// Explicitly close the connection.
peer_stream.close ();
}
int main (int argc, char *argv[])
{
char *host = argc > 1 ? argv[1] : "ics.uci.edu";
u_short port_num = htons (argc > 2 ? atoi (argv[2]) : PORT_NUM);
// Address of the server.
ACE_INET_Addr s_addr (port_num, host)
// Use SOCK SAP wrappers on client’s side.
send_data (s_addr);
return 0;
}
图3-12 基于SOCK SAP的客户例子
static const int PORT_NUM = 10000;
// SOCK_SAP Server.
template
void recv_data (ACCEPTOR::PEER_ADDR s_addr)
{
// Factory for passive connection establishment.
ACCEPTOR acceptor (s_addr);
// Data transfer object.
ACCEPTOR::PEER_STREAM peer_stream;
// Remote peer address.
ACCEPTOR::PEER_ADDR peer_addr;
// Performs iterative server activities.
for (;;)
{
// Create a new STREAM endpoint
// (automatically restarted if errno == EINTR).
acceptor.accept (peer_stream, &peer_addr);
printf ("client %s\n", peer_addr.get_host_name ());
// Read data from client (terminate on error).
char buf[BUFSIZ];
for (int r_bytes = 0;;)
{
r_bytes = peer_stream.recv (buf, sizeof buf);
if (r_bytes > 0)
write (1, buf, r_bytes);
else
break;
}
// Close peer_stream endpoint
// (acceptor endpoint stays open).
peer_stream.close ();
}
/* NOTREACHED */
}
int main (int argc, char *argv[])
{
u_short port_num = argc == 1 ? PORT_NUM : atoi (argv[1]);
// Port for the server.
ACE_INET_Addr s_addr (port_num);
// Use Socket wrappers on server’s side.
recv_data (s_addr);
return 0;
}
图3-13 基于SOCK SAP的服务器例子
