summaryrefslogtreecommitdiff
path: root/docs/tutorials/008/page02.html
blob: f1b8e170c281d09079647d1f32cd7353b63ae0b6 (plain)
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!-- $Id$ -->
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="GENERATOR" CONTENT="Mozilla/4.04 [en] (X11; I; Linux 2.0.32 i486) [Netscape]">
   <META NAME="Author" CONTENT="James CE Johnson">
   <TITLE>ACE Tutorial 008</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F">

<CENTER><B><FONT SIZE=+2>ACE Tutorial 008</FONT></B></CENTER>

<CENTER><B><FONT SIZE=+2>Sending and receiving datagrams</FONT></B></CENTER>


<P>
<HR WIDTH="100%">


<P>The first thing we want to look at is <A HREF="server.cpp">server.cpp</A>.&nbsp;
This is a pretty simple application that listens for datagrams at a known
port and sends back a response.&nbsp; In order to implement a true "discovery"&nbsp;
mechanism, the server will have to be a little bit more picky about who
it responds to.&nbsp; We'll tackle that issue in the next tutorial though...

<P>
<HR WIDTH="100%">
<PRE>
<font color=red>// $Id$</font>

<font color=red>/* 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.  */</font>
<font color=blue>#include</font> "<A HREF="../../../ace/SOCK_Dgram.h">ace/SOCK_Dgram.h</A>"
<font color=blue>#include</font> "<A HREF="../../../ace/INET_Addr.h">ace/INET_Addr.h</A>"

<font color=red>/* Use the typical TCP/IP port address for receiving datagrams.  */</font>
static const u_short PORT = ACE_DEFAULT_SERVER_PORT;

int
main (int, char**)
{
  <font color=red>/* 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.  */</font>
  ACE_INET_Addr local (PORT);

  <font color=red>/* A simply constructed datagram that we'll listen with.  */</font>
  ACE_SOCK_Dgram dgram;

  <font color=red>/* Like most ACE objects, the datagram has to be opened before it
    can be uses.  Of course, -1 on failure.

    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.  */</font>
  if (dgram.open (local) == -1)
    ACE_ERROR_RETURN ((LM_ERROR,
                       "<font color=green>%p\n</font>",
                       "<font color=green>open</font>"),
                      -1);

  <font color=red>/* 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.  */</font>
  char buf[BUFSIZ];

  <font color=red>/* Unlike ACE_SOCK_Stream, datagrams are unconnected.  That is,
    there is no "<font color=green>virtual circuit</font>" 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. */</font>
  ACE_INET_Addr remote;

  ACE_DEBUG ((LM_DEBUG,
              "<font color=green>(%P|%t) starting up server daemon\n</font>"));

  <font color=red>/* Receive datagrams as long as we're able.  */</font>
  while (dgram.recv (buf,
                     sizeof (buf),
                     remote) != -1)
    {
      <font color=red>/* 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().  */</font>
      ACE_DEBUG ((LM_DEBUG,
                  "<font color=green>(%P|%t) Data (%s) from client (%s)\n</font>",
                  buf,
                  remote.get_host_name ()));

      <font color=red>/* 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.  */</font>
      ACE_INET_Addr local ((u_short) 0);
      ACE_SOCK_Dgram client;

      <font color=red>/* Open up our response datagram as always.  */</font>
      if (client.open (local) == -1)
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             "<font color=green>%p\n</font>",
                             "<font color=green>client open</font>"),
                            -1);
          return 0;
        }

      <font color=red>/* Build a witty response...  */</font>
      sprintf (buf,
               "<font color=green>I am here</font>");

      <font color=red>/* 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.

        Of course, we're assuming that the client will be listening
        for our reply...  */</font>
      if (client.send (buf,
                       <font color=#008888>ACE_OS::strlen</font> (buf) + 1,
                       remote) == -1)
        ACE_ERROR_RETURN ((LM_ERROR,
                           "<font color=green>%p\n</font>",
                           "<font color=green>send</font>"),
                          -1);
    }

  return 0;
}
</PRE>
<HR WIDTH="100%">

<P>And that's really all there is to it.&nbsp; Obviously there is some
room for improvement.&nbsp; The most blatant is the somewhat small buffer
size for receiving the datagram.&nbsp; I've never been able to get a solid
answer on datagram sizes.&nbsp; The theoretical limit is just under 64k
but you have to deal with fragmentation.&nbsp; Some readings indicate that
8k is a reasonable size, others go much smaller.&nbsp; My general rule
of thumb is to keep datagrams relatively small (eg -- under 8k or so) and
test a lot.&nbsp; If you find that your routers are fragmenting your larger
datagrams, back off to something smaller.&nbsp; Of course, if you must
send 100k and can only do so 1k at a time, you'll have to worry about retransmissions
&amp; reordering.&nbsp; At that point, you might consider going to TCP.&nbsp;
Remember:&nbsp; datagrams are unreliable!&nbsp; Don't try to make 'em do
something they werent' designed for!
<P><HR WIDTH="100%">
<CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page03.html">Continue This Tutorial</A>]</CENTER>