summaryrefslogtreecommitdiff
path: root/docs/tutorials/008/page02.html
blob: 85b8c083d88436fd8aacc80ad142a6d1828a295d (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="GENERATOR" CONTENT="Mozilla/4.05 [en] (WinNT; I) [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%">

<P><TT>/*</TT>
<BR><TT>&nbsp;&nbsp; Our datagram server will, of course, need to create
a datagram.</TT>
<BR><TT>&nbsp;&nbsp; We'll also need an address object so that we know
where to listen.</TT>
<BR><TT>&nbsp;*/</TT>
<BR><TT>#include "ace/SOCK_Dgram.h"</TT>
<BR><TT>#include "ace/INET_Addr.h"</TT>

<P><TT>/*</TT>
<BR><TT>&nbsp;&nbsp; Use the typical TCP/IP port address for receiving
datagrams.</TT>
<BR><TT>&nbsp;*/</TT>
<BR><TT>static const u_short PORT = ACE_DEFAULT_SERVER_PORT;</TT>

<P><TT>int main(int,char**)</TT>
<BR><TT>{</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This is where we'll listen
for datagrams coming from the</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clients.&nbsp; We'll give
this address to the open() method</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; below to enable the listener.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; ACE_INET_Addr&nbsp; local(PORT);</TT>

<P><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A simply constructed datagram
that we'll listen with.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; ACE_SOCK_Dgram dgram;</TT>

<P><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Like most ACE objects, the
datagram has to be opened before</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it can be uses.&nbsp; Of course,
-1 on failure.</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A datagram will fail to open
if there is already a datagram</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listening at the port we've
chosen.&nbsp; It *is* OK to open</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a datagram at a port where
there is an ACE_SOCK_Stream</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; though.&nbsp; This is because
datagrams are UDP and SOCK_Stream</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; is TCP and the two don't cross
paths.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; if( dgram.open(local) == -1 )</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_ERROR_RETURN ((LM_ERROR,
"%p\n", "open"),-1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; }</TT>

<P><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Create a simple buffer to
receive the data.&nbsp; You generally need</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; to provide a buffer big enough
for the largest datagram you</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expect to receive.&nbsp; Some
platforms will let you read a little</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and then some more later but
other platforms will throw out</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; whatever part of the datagram
you don't get with the first</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read.&nbsp; (This is on a
per-datagram basis BTW.)&nbsp; The theoretical</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; limit on a datagram is about
64k.&nbsp; The realistic limit (because</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; of routers &amp; such) is
much smaller.&nbsp; Choose your buffer size</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; based on your application's
needs.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; char buf[512];</TT>

<P><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Unlike ACE_SOCK_Stream, datagrams
are unconnected.&nbsp; That is,</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; there is no "virtual circuit"
between server and client.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Because of this, the server
has to provide a placeholder</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for the OS to fill in the
source (client) address information</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on the recv.&nbsp; You can
initialize this INET_Addr to anything,</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it will be overwritten when
the data arrives.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; ACE_INET_Addr remote;</TT>

<P><TT>&nbsp;&nbsp;&nbsp; ACE_DEBUG ((LM_DEBUG, "(%P|%t) starting up server
daemon\n"));</TT>

<P><TT>&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Receive datagrams as long
as we're able.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; while( dgram.recv(buf,sizeof(buf),remote) !=
-1 )</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Display
a brief message about our progress.&nbsp; Notice how we</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; use
the 'remote' object to display the address of the client.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; With
an ACE_SOCK_Stream we used get_remote_addr() to get the</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; address
the socket is connected to.&nbsp; Because datagrams are</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unconnected,
we use the addr object provided to recv().</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_DEBUG ((LM_DEBUG,
"(%P|%t) Data (%s) from client (%s)\n", buf, remote.get_host_name()));</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; To
respond to the client's query, we have to become a client</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ourselves.&nbsp;
To do so, we need an anonymous local address from</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; which
we'll send the response and a datagram in which to send</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it.&nbsp;
(An anonymous address is simply one where we let the OS</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; choose
a port for us.&nbsp; We really don't care what it is.O</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_INET_Addr&nbsp;
local((u_short)0);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACE_SOCK_Dgram client;</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Open
up our response datagram as always.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( client.open(local)
== -1 )</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "client open"),-1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
return(0);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Build
a witty response...</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(buf,"I am here");</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and
send it to the client.&nbsp; Notice the symetry with the recv()</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method.&nbsp;
Again, the unconnected nature of datagrams forces</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; us
to specify an address object with each read/write operation.</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; In
the case of read (recv()) that's where the OS stuffs the</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; address
of the datagram sender.&nbsp; In the case of write (send())</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; that
we're doing here, the address is where we want the network</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; to
deliver the data.</TT>

<P><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Of
course, we're assuming that the client will be listening</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for
our reply...</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( client.send(buf,strlen(buf)+1,remote)
== -1 )</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
return(0);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; }</TT>

<P><TT>&nbsp;&nbsp;&nbsp; return(0);</TT>
<BR><TT>}</TT>

<P>
<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="..">Tutorial Index</A>] [<A HREF="page03.html">Continue
This Tutorial</A>]</CENTER>

</BODY>
</HTML>