• ACE Tutorial [翻译] 08-page02

    2004-11-29

    Tag:ACE_TAO

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://jnn.blogbus.com/logs/519776.html

    首先,我们看一下server.cpp是如何实现的。 这是一个特别简单的应用,该应用只负责监听指定端口的数据报文,并且将收到的消息转发过去。为了能够真实地展现“服务发现“的机制,服务器需要在确定反馈消息部分做一些特别地工作。这将在我们地下面地教程中详细介绍。


    // page02.html,v 1.13 2000/11/27 17:56:43 othman Exp
     
    /* Our datagram server will, of course, need to create a datagram.
       We'll also need an address object so that we know where to listen.
       我们的数据报文服务器,显而易见,需要创建一个数据报。
       我们需要一个地址对象来知道我们希望监听的端口
    */
    #include "ace/Log_Msg.h"
    #include "ace/SOCK_Dgram.h"
    #include "ace/INET_Addr.h"
     
    /* Use the typical TCP/IP port address for receiving datagrams.  
       使用一个典型的TCP/IP端口来接收数据。
    */
    static const u_short PORT = ACE_DEFAULT_SERVER_PORT;
     
    int
    main (int, char**)
    {
      /* This is where we'll listen for datagrams coming from the clients.
        We'll give this address to the open() method below to enable the
    listener.  
    这里我是我们接收客户端所发送过来的数据报。我们使用这个地址来打开
    我们所希望的监听。
    */
      ACE_INET_Addr local (PORT);
     
      /* A simply constructed datagram that we'll listen with.  
       简单构造函数来创建我们所希望监听的数据报
    */
      ACE_SOCK_Dgram dgram;
     
      /* Like most ACE objects, the datagram has to be opened before it
    can be uses.  Of course, -1 on failure.
    和其他的ACE对象一样,数据报在使用的之前需要调用open方法。当然,
    如果调用失败返回值为-1。
     
        A datagram will fail to open if there is already a datagram
        listening at the port we've chosen.  It *is* OK to open a datagram
        at a port where there is an ACE_SOCK_Stream though.  This is
        because datagrams are UDP and SOCK_Stream is TCP and the two don't
    cross paths.
    如果在我们所选定的端口已经在监听数据报了,那么数据报接收器在创建的
    时候会出现错误。但是它在打开一个ACE_SOCK_Stream所占用的端口是没有
    问题的。这是由于UDP的数据报文和TCP SOCK_Stream的端口是不相关的。  
    */
      if (dgram.open (local) == -1)
        ACE_ERROR_RETURN ((LM_ERROR,
                           "%p\n",
                           "open"),
                          -1);
     
      /* Create a simple buffer to receive the data.  You generally need
        to provide a buffer big enough for the largest datagram you expect
        to receive.  Some platforms will let you read a little and then
        some more later but other platforms will throw out whatever part
        of the datagram you don't get with the first read.  (This is on a
        per-datagram basis BTW.)  The theoretical limit on a datagram is
        about 64k.  The realistic limit (because of routers & such) is
        much smaller.  Choose your buffer size based on your application's
    needs.
    为接收到的数据创建一个缓冲区。你需要设置一个比你所期望接收的数据报
    更大一些的缓冲区。一些系统平台运行你读取一小部分数据,过一段时间之
    后在继续读剩余的内容,但是另一下系统平台在你第一次读取部分数据之后,
    就将数据报的剩余内容丢弃。(这是一个面向单数据报的模式。)一个数据报
    的理论最大值是64K。在现实中的最大值(由于路由或者其他)比这个值要小
    很多。你需要根据应用需要选择你的缓冲大小。  
    */
      char buf[BUFSIZ];
     
      /* Unlike ACE_SOCK_Stream, datagrams are unconnected.  That is,
        there is no "virtual circuit" between server and client.  Because
        of this, the server has to provide a placeholder for the OS to
        fill in the source (client) address information on the recv.  You
        can initialize this INET_Addr to anything, it will be overwritten
    when the data arrives. 
    ACE_SOCK_Stream不同,数据报是非面向连接的。这就是说,在服务器和客户端
    之间是没有虚拟链路的。正是因为这样,服务器需要提供存储空间来为存放操作
    系统接收到的(客户端)的地址信息。你可以给INET_Addr初始化任何一个值,当
    接收到数据报时,这个值将会被覆盖。
    */
      ACE_INET_Addr remote;
     
      ACE_DEBUG ((LM_DEBUG,
                  "(%P|%t) starting up server daemon\n"));
     
      /* Receive datagrams as long as we're able.  
       一直接收我们所能接收到的数据报
    */
      while (dgram.recv (buf,
                         sizeof (buf),
                         remote) != -1)
        {
          /* Display a brief message about our progress.  Notice how we
            use the 'remote' object to display the address of the client.
            With an ACE_SOCK_Stream we used get_remote_addr() to get the
            address the socket is connected to.  Because datagrams are
            unconnected, we use the addr object provided to recv().  
      显示现在进展情况的简要信息。注意我们怎样使用‘remot’对象来显示
      客户端的地址。在ACE_SOCK_Stream中,我们使用get_remote_addr()来
      获取连接到我们的socket地址。由于数据报时非面向连接的,我们使用
      recv()方法所提供的地址对象来获取。
    */
          ACE_DEBUG ((LM_DEBUG,
                      "(%P|%t) Data (%s) from client (%s)\n",
                      buf,
                      remote.get_host_name ()));
     
          /* To respond to the client's query, we have to become a client
            ourselves.  To do so, we need an anonymous local address from
            which we'll send the response and a datagram in which to send
            it.  (An anonymous address is simply one where we let the OS
            choose a port for us.  We really don't care what it is.  
      为了响应客户端的请求。我们需要创建一个客户端对象。为了这样做,
    我们需要一个匿名的本地地址,这样我们可以通过这个地址向发送响应
    的数据报。(一个匿名地址包含操作系统提供为我们选定的一个端口号,
    在这里,我们并不关心其详细内容)
    */
          ACE_INET_Addr local ((u_short) 0);
          ACE_SOCK_Dgram client;
     
    /* Open up our response datagram as always. 
       初始化我们的响应报文
     */
          if (client.open (local) == -1)
            {
              ACE_ERROR_RETURN ((LM_ERROR,
                                 "%p\n",
                                 "client open"),
                                -1);
              return 0;
            }
     
    /* Build a witty response...  
       创建一个诙谐的响应
    */
          sprintf (buf,
                   "I am here");
     
          /* and send it to the client.  Notice the symmetry with the
            recv() method.  Again, the unconnected nature of datagrams
            forces us to specify an address object with each read/write
            operation.  In the case of read (recv()) that's where the OS
            stuffs the address of the datagram sender.  In the case of
            write (send()) that we're doing here, the address is where we
            want the network to deliver the data.
            将信息回传到客户端。注意这是一个和recv()方法对应的函数。由于数
    据报时非面向连接的,这就要求我们在进行读写操作时,需要指明地址
    对象。在读取(recv())情况下,我们从操作系统获得的地址,就是发送
    数据的源地址在写数据(send())时,这个地址就是我们希望发送的目的
    地址。 
     
            Of course, we're assuming that the client will be listening
            for our reply...  
    当然,我们需要假设客户端已经在监听我们的回应了
    */
          if (client.send (buf,
                           ACE_OS::strlen (buf) + 1,
                           remote) == -1)
            ACE_ERROR_RETURN ((LM_ERROR,
                               "%p\n",
                               "send"),
                              -1);
        }
     
      return 0;
    }

    这就是我们涉及到的内容。显然这里还有很多可以扩展的空间。这里最值得争议的是在接收报文时设置小一点的缓冲区。现在我还不能给一个明确的设置数据报大小的回答。理论上来说,缓冲区有一个64K大小的限制,所以你需要自己进行关分包的操作。一些文章指出8K是一般比较合适的大小,其他这说更小一点的数据包比较合适。我认为最好的保证数据尽可能小(例如――小于8K)并且进行大量测试。如果你发现你的路由器已经对你的大数据报进行了分包的操作,你需要减小你的数据包的大小。当然,如果你需要发送100K并且一次只能发送1K,你需要考虑有关重传或者从组的问题。从这一点来说,你也许会考虑采用TCP。

    注意:数据包是不可靠的。


    收藏到:Del.icio.us