diff options
author | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-09-05 21:05:04 +0000 |
---|---|---|
committer | jcej <jcej@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1998-09-05 21:05:04 +0000 |
commit | ef1a88ade3bbde1f660baceb470135cd920e0b53 (patch) | |
tree | ee33bff80057b1b4ca600b5ae9a4d8aa61a3787f /docs/tutorials/008 | |
parent | aed7cb18ee0d109023ab548231219adff8e2281a (diff) | |
download | ATCD-ef1a88ade3bbde1f660baceb470135cd920e0b53.tar.gz |
Datagram tutorials begun
Diffstat (limited to 'docs/tutorials/008')
-rw-r--r-- | docs/tutorials/008/Makefile | 63 | ||||
-rw-r--r-- | docs/tutorials/008/broadcast_client.cpp | 72 | ||||
-rw-r--r-- | docs/tutorials/008/directed_client.cpp | 112 | ||||
-rw-r--r-- | docs/tutorials/008/page01.html | 60 | ||||
-rw-r--r-- | docs/tutorials/008/page02.html | 237 | ||||
-rw-r--r-- | docs/tutorials/008/page03.html | 187 | ||||
-rw-r--r-- | docs/tutorials/008/page04.html | 156 | ||||
-rw-r--r-- | docs/tutorials/008/page05.html | 43 | ||||
-rw-r--r-- | docs/tutorials/008/server.cpp | 128 |
9 files changed, 1058 insertions, 0 deletions
diff --git a/docs/tutorials/008/Makefile b/docs/tutorials/008/Makefile new file mode 100644 index 00000000000..59ac6bca63f --- /dev/null +++ b/docs/tutorials/008/Makefile @@ -0,0 +1,63 @@ +#---------------------------------------------------------------------------- +# $Id$ +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = server directed_client broadcast_client + +FILES = + +BUILD = $(VBIN) + +# SRC = $(addsuffix .cpp,$(BIN)) $(addsuffix .cpp,$(FILES)) + +HDR = *.h + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(ACE_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(ACE_ROOT)/include/makeinclude/macros.GNU +include $(ACE_ROOT)/include/makeinclude/rules.common.GNU +include $(ACE_ROOT)/include/makeinclude/rules.nonested.GNU +include $(ACE_ROOT)/include/makeinclude/rules.lib.GNU +include $(ACE_ROOT)/include/makeinclude/rules.bin.GNU +include $(ACE_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +Indent : # + for i in $(SRC) $(HDR) ; do \ + indent -npsl -l80 -fca -fc1 -cli0 -cdb < $$i | \ + sed -e 's/: :/::/g' \ + -e 's/^.*\(public:\)/\1/' \ + -e 's/^.*\(protected:\)/\1/' \ + -e 's/^.*\(private:\)/\1/' \ + -e 's/:\(public\)/ : \1/' \ + -e 's/:\(protected\)/ : \1/' \ + -e 's/:\(private\)/ : \1/' \ + > $$i~ ;\ + mv $$i~ $$i ;\ + done + +Depend : depend + perl ../fix.Makefile + +.depend : # + touch .depend + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + + # Don't put anything below here. Between the "depend" target and fix.Makefile + # it's guaranteed to be lost! + + # This is inserted by the fix.Makefile script +include .depend diff --git a/docs/tutorials/008/broadcast_client.cpp b/docs/tutorials/008/broadcast_client.cpp new file mode 100644 index 00000000000..2f754806d03 --- /dev/null +++ b/docs/tutorials/008/broadcast_client.cpp @@ -0,0 +1,72 @@ + +// $Id$ + +#include "ace/SOCK_Dgram_Bcast.h" +#include "ace/INET_Addr.h" + +static const u_short PORT = ACE_DEFAULT_SERVER_PORT; + +int main(int argc,char *argv[] ) +{ + ACE_INET_Addr local((u_short)0); + + /* + Instead of creating the ACE_SOCK_Dgram we created last time, + we'll create an ACE_SOCK_Dgram_Bcast. "Bcast" means, of course, + "Broadcast". This ACE object is clever enough to go out to the + OS and find all of the network interfaces. When you send() + on a Dgram_Bcast, it will send the datagram out on all of those + interfaces. This is quiet handy if you do it on a multi-homed + host that plays router... + */ + ACE_SOCK_Dgram_Bcast dgram; + + if( dgram.open(local) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "datagram open"),-1); + } + + char buf[512]; + + sprintf(buf, "Hello World!"); + + /* + The only other difference between us and the directed client + is that we don't specify a host to receive the datagram. + Instead, we use the magic value "INADDR_BROADCAST". All hosts + are obliged to respond to datagrams directed to this address + the same as they would to datagrams sent to their hostname. + + Remember, the Dgram_Bcast will send a datagram to all interfaces + on the host. That's true even if the address is for a specific + host (and the host address makes sense for the interface). + The real power is in using an INADDR_BROADCAST addressed datagram + against all interfaces. + */ + + ACE_INET_Addr remote(PORT,INADDR_BROADCAST); + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) Sending (%s) to the server.\n",buf)); + + if( dgram.send(buf,strlen(buf)+1,remote) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1); + } + + if( dgram.recv(buf,sizeof(buf),remote) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"),-1); + } + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) The server said: %s\n",buf)); + + /* + Using the "remote" object instance, find out where the server lives. + We could then save this address and use directed datagrams to chat + with the server for a while. + */ + ACE_DEBUG ((LM_DEBUG, "(%P|%t) The server can be found at: (%s:%d)\n", + remote.get_host_name(), PORT )); + + return(0); +} diff --git a/docs/tutorials/008/directed_client.cpp b/docs/tutorials/008/directed_client.cpp new file mode 100644 index 00000000000..7fac216dbc3 --- /dev/null +++ b/docs/tutorials/008/directed_client.cpp @@ -0,0 +1,112 @@ + +// $Id$ + +#include "ace/SOCK_Dgram.h" +#include "ace/INET_Addr.h" + +/* + Once again, we use the default server port. In a "real" system, + the server's port (or ports) would be published in some way so + that clients would know where to "look". We could even add entries + to the operating system's services file and use a service name + instead of a number. We'll come back to that in some other tutorial + though. For now, let's stay simple. + */ +static const u_short PORT = ACE_DEFAULT_SERVER_PORT; + +/* + Our goal here is to develop a client that can send a datagram to + a server running on a known host. We'll use a command-line argument + to specify the hostname instead of hard-coding it. + */ +int main(int argc,char *argv[] ) +{ + /* + All datagrams have to have a point of origin. Since we intend to + transmit instead of receive, we initialize an address with zero + and let the OS choose a port for us. We could have chosen our + own value between 1025 and 65535 as long as it isn't already in use. + */ + ACE_INET_Addr local((u_short)0); + + /* + And here is our datagram object. + */ + ACE_SOCK_Dgram dgram; + + /* + Notice that this looks a lot like the server application. There's + no difference in creating server datagrams an client datagrams. + You can even use a zero-constructed address for your server datagram + as long as you tell the client where you're listening (eg -- by writting + into a file or some such). + */ + if( dgram.open(local) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "datagram open"),-1); + } + + /* + Yep. We've seen this before too... + */ + char buf[512]; + + /* + Ok, now we're doing something different. + */ + sprintf(buf, "Hello World!"); + + /* + Just like sending a telegram, we have to address our datagram. + Here, we create an address object at the desired port on the + chosen host. To keep us from crashing, we'll provide a default + host name if we aren't given one. + */ + ACE_INET_Addr remote(PORT, argc > 1 ? argv[1] : "localhost" ); + + ACE_DEBUG ((LM_DEBUG, "(%P|%t) Sending (%s) to the server.\n",buf)); + /* + Now we send our buffer of stuff to the remote address. This is + just exactly what the server did after receiving a client message. + Datagrams are rather orthogonal that way: they don't generally make + much of a fuss about being either client or server. + */ + if( dgram.send(buf,strlen(buf)+1,remote) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1); + } + + /* + Now we've turned around and put ourselves into "server mode" by + invoking the recv() method. We now our server is going to send + us something, so we hang out here and wait for it. Because we + know datagrams are unreliable, there is a chance that the server + will respond but we won't hear. You might consider providing a + timeout on the recv() in that case. If recv() fails due to timeout + it will return -1 and you can then resend your query and attempt + the recv() again. + + Like the server application, we have to give the recv() an + uninitialized addr object so that we can find out who is talking + back to us. + */ + if( dgram.recv(buf,sizeof(buf),remote) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"),-1); + } + + /* + Find out what the server had to say. + */ + ACE_DEBUG ((LM_DEBUG, "(%P|%t) The server said: %s\n",buf)); + + /* + Using the "remote" object instance, find out where the server lives. + We could then save this address and use directed datagrams to chat + with the server for a while. + */ + ACE_DEBUG ((LM_DEBUG, "(%P|%t) The server can be found at: (%s:%d)\n", + remote.get_host_name(), PORT )); + + return(0); +} diff --git a/docs/tutorials/008/page01.html b/docs/tutorials/008/page01.html new file mode 100644 index 00000000000..ebe24e9ecc8 --- /dev/null +++ b/docs/tutorials/008/page01.html @@ -0,0 +1,60 @@ +<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>In a lot of IPC programming, the clients know where the servers +are. A mail client, for instance, has a configuration file that says +where the mail host is. Your web browser has a "location" field that +you type into to give it a destination. + +<P>What if you have written a server application and then you execute it +on several systems in your network? All of the instances are probably +more or less equal to the client's point of view, so you don't want to +"configure" the clients to a single server each. Likewise, you +want the ability to add and remove servers at any time so you can't just +give the clients a list to choose from. + +<P>So... how do the clients know where the servers are? + +<P>Let 'em ask! + +<P>Datagrams are great for this. You can toss a datagram out onto +the network and any servers listening at the correct port will* hear it. +Like ACE_SOCK_Stream that we've seen before, you can get the peer address +from a datagram. With that, the server can send a response +back to the client. The client, in turn, can pull the peer address +out and know exactly where the server lives. + +<P>In this tutorial we'll develop three applications: a server listening +for datagrams, a client that can send to a known host and a client that +can send to the entire (sub)network. In the next tutorial, we'll +expand on this to make the server a bit more prudish. +<BR> + +<P><FONT SIZE=-1>* Actually, the servers <I>might</I> hear the datagram. +Datagrams are rather unreliable. (Sort of like some operating systems +I know.) Still, if the network traffic isn't too bad, they generally +get through. Your clients can always send out more queries if there +aren't any responses in a timely fashion.</FONT> + +<P> +<HR WIDTH="100%"> +<CENTER>[<A HREF="..">Tutorial +Index</A>] [<A HREF="page02.html">Continue +This Tutorial</A>]</CENTER> + +</BODY> +</HTML> diff --git a/docs/tutorials/008/page02.html b/docs/tutorials/008/page02.html new file mode 100644 index 00000000000..25a47e7e4f5 --- /dev/null +++ b/docs/tutorials/008/page02.html @@ -0,0 +1,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>. +This is a pretty simple application that listens for datagrams at a known +port and sends back a response. In order to implement a true "discovery" +mechanism, the server will have to be a little bit more picky about who +it responds to. We'll tackle that issue in the next tutorial though... + +<P> +<HR WIDTH="100%"> + +<P><TT>/*</TT> +<BR><TT> Our datagram server will, of course, need to create +a datagram.</TT> +<BR><TT> We'll also need an address object so that we know +where to listen.</TT> +<BR><TT> */</TT> +<BR><TT>#include "ace/SOCK_Dgram.h"</TT> +<BR><TT>#include "ace/INET_Addr.h"</TT> + +<P><TT>/*</TT> +<BR><TT> Use the typical TCP/IP port address for receiving +datagrams.</TT> +<BR><TT> */</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> /*</TT> +<BR><TT> This is where we'll listen +for datagrams coming from the</TT> +<BR><TT> clients. We'll give +this address to the open() method</TT> +<BR><TT> below to enable the listener.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr local(PORT);</TT> + +<P><TT> /*</TT> +<BR><TT> A simply constructed datagram +that we'll listen with.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_SOCK_Dgram dgram;</TT> + +<P><TT> /*</TT> +<BR><TT> Like most ACE objects, the +datagram has to be opened before</TT> +<BR><TT> it can be uses. Of course, +-1 on failure.</TT> + +<P><TT> A datagram will fail to open +if there is already a datagram</TT> +<BR><TT> listening at the port we've +chosen. It *is* OK to open</TT> +<BR><TT> a datagram at a port where +there is an ACE_SOCK_Stream</TT> +<BR><TT> though. This is because +datagrams are UDP and SOCK_Stream</TT> +<BR><TT> is TCP and the two don't cross +paths.</TT> +<BR><TT> */</TT> +<BR><TT> if( dgram.open(local) == -1 )</TT> +<BR><TT> {</TT> +<BR><TT> ACE_ERROR_RETURN ((LM_ERROR, +"%p\n", "open"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> /*</TT> +<BR><TT> Create a simple buffer to +receive the data. You generally need</TT> +<BR><TT> to provide a buffer big enough +for the largest datagram you</TT> +<BR><TT> expect to receive. Some +platforms will let you read a little</TT> +<BR><TT> and then some more later but +other platforms will throw out</TT> +<BR><TT> whatever part of the datagram +you don't get with the first</TT> +<BR><TT> read. (This is on a +per-datagram basis BTW.) The theoretical</TT> +<BR><TT> limit on a datagram is about +64k. The realistic limit (because</TT> +<BR><TT> of routers & such) is +much smaller. Choose your buffer size</TT> +<BR><TT> based on your application's +needs.</TT> +<BR><TT> */</TT> +<BR><TT> char buf[512];</TT> + +<P><TT> /*</TT> +<BR><TT> Unlike ACE_SOCK_Stream, datagrams +are unconnected. That is,</TT> +<BR><TT> there is no "virtual circuit" +between server and client.</TT> +<BR><TT> Because of this, the server +has to provide a placeholder</TT> +<BR><TT> for the OS to fill in the +source (client) address information</TT> +<BR><TT> on the recv. You can +initialize this INET_Addr to anything,</TT> +<BR><TT> it will be overwritten when +the data arrives.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr remote;</TT> + +<P><TT> ACE_DEBUG ((LM_DEBUG, "(%P|%t) starting up server +daemon\n"));</TT> + +<P><TT> /*</TT> +<BR><TT> Receive datagrams as long +as we're able.</TT> +<BR><TT> */</TT> +<BR><TT> while( dgram.recv(buf,sizeof(buf),remote) != +-1 )</TT> +<BR><TT> {</TT> +<BR><TT> /*</TT> +<BR><TT> Display +a brief message about our progress. Notice how we</TT> +<BR><TT> use +the 'remote' object to display the address of the client.</TT> +<BR><TT> With +an ACE_SOCK_Stream we used get_remote_addr() to get the</TT> +<BR><TT> address +the socket is connected to. Because datagrams are</TT> +<BR><TT> unconnected, +we use the addr object provided to recv().</TT> +<BR><TT> */</TT> +<BR><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) Data (%s) from client (%s)\n", buf, remote.get_host_name()));</TT> + +<P><TT> /*</TT> +<BR><TT> To +respond to the client's query, we have to become a client</TT> +<BR><TT> ourselves. +To do so, we need an anonymous local address from</TT> +<BR><TT> which +we'll send the response and a datagram in which to send</TT> +<BR><TT> it. +(An anonymous address is simply one where we let the OS</TT> +<BR><TT> choose +a port for us. We really don't care what it is.O</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr +local((u_short)0);</TT> +<BR><TT> ACE_SOCK_Dgram client;</TT> + +<P><TT> /*</TT> +<BR><TT> Open +up our response datagram as always.</TT> +<BR><TT> */</TT> +<BR><TT> if( client.open(local) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "client open"),-1);</TT> +<BR><TT> +return(0);</TT> +<BR><TT> }</TT> + +<P><TT> /*</TT> +<BR><TT> Build +a witty response...</TT> +<BR><TT> */</TT> +<BR><TT> sprintf(buf,"I am here");</TT> + +<P><TT> /*</TT> +<BR><TT> and +send it to the client. Notice the symetry with the recv()</TT> +<BR><TT> method. +Again, the unconnected nature of datagrams forces</TT> +<BR><TT> us +to specify an address object with each read/write operation.</TT> +<BR><TT> In +the case of read (recv()) that's where the OS stuffs the</TT> +<BR><TT> address +of the datagram sender. In the case of write (send())</TT> +<BR><TT> that +we're doing here, the address is where we want the network</TT> +<BR><TT> to +deliver the data.</TT> + +<P><TT> Of +course, we're assuming that the client will be listening</TT> +<BR><TT> for +our reply...</TT> +<BR><TT> */</TT> +<BR><TT> if( client.send(buf,strlen(buf)+1,remote) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1);</TT> +<BR><TT> +return(0);</TT> +<BR><TT> }</TT> +<BR><TT> }</TT> + +<P><TT> return(0);</TT> +<BR><TT>}</TT> + +<P> +<HR WIDTH="100%"> + +<P>And that's really all there is to it. Obviously there is some +room for improvement. The most blatant is the somewhat small buffer +size for receiving the datagram. I've never been able to get a solid +answer on datagram sizes. The theoretical limit is just under 64k +but you have to deal with fragmentation. Some readings indicate that +8k is a reasonable size, others go much smaller. My general rule +of thumb is to keep datagrams relatively small (eg -- under 8k or so) and +test a lot. If you find that your routers are fragmenting your larger +datagrams, back off to something smaller. Of course, if you must +send 100k and can only do so 1k at a time, you'll have to worry about retransmissions +& reordering. At that point, you might consider going to TCP. +Remember: datagrams are unreliable! Don't try to make 'em do +something they werent' designed for! + +<P> +<HR WIDTH="100%"> +<CENTER>[<A HREF="../../tutorials">Tutorial Index</A>] [<A HREF="page03.html">Continue +This Tutorial</A>]</CENTER> + +</BODY> +</HTML> diff --git a/docs/tutorials/008/page03.html b/docs/tutorials/008/page03.html new file mode 100644 index 00000000000..c572eaf622f --- /dev/null +++ b/docs/tutorials/008/page03.html @@ -0,0 +1,187 @@ +<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>In <A HREF="directed_client.cpp">directed_client.cpp</A> we create a +client that knows how to send a datagram to a server on a known host. +This is a good thing if you know where the server lives and want to have +a conversation. The Unix <I>talk</I> utilitiy, for instance, +could be written this way. + +<P> +<HR WIDTH="100%"> + +<P><TT>#include "ace/SOCK_Dgram.h"</TT> +<BR><TT>#include "ace/INET_Addr.h"</TT> + +<P><TT>/*</TT> +<BR><TT> Once again, we use the default server port. +In a "real" system,</TT> +<BR><TT> the server's port (or ports) would be published in +some way so</TT> +<BR><TT> that clients would know where to "look". We +could even add entries</TT> +<BR><TT> to the operating system's services file and use a +service name</TT> +<BR><TT> instead of a number. We'll come back to that +in some other tutorial</TT> +<BR><TT> though. For now, let's stay simple.</TT> +<BR><TT> */</TT> +<BR><TT>static const u_short PORT = ACE_DEFAULT_SERVER_PORT;</TT> + +<P><TT>/*</TT> +<BR><TT> Our goal here is to develop a client that can send +a datagram to</TT> +<BR><TT> a server running on a known host. We'll use +a command-line argument</TT> +<BR><TT> to specify the hostname instead of hard-coding it.</TT> +<BR><TT> */</TT> +<BR><TT>int main(int argc,char *argv[] )</TT> +<BR><TT>{</TT> +<BR><TT> /*</TT> +<BR><TT> All +datagrams have to have a point of origin. Since we intend to</TT> +<BR><TT> transmit +instead of receive, we initialize an address with zero</TT> +<BR><TT> and +let the OS choose a port for us. We could have chosen our</TT> +<BR><TT> own +value between 1025 and 65535 as long as it isn't already in use.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr +local((u_short)0);</TT> + +<P><TT> /*</TT> +<BR><TT> And +here is our datagram object.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_SOCK_Dgram dgram;</TT> +<BR><TT> </TT> +<BR><TT> /*</TT> +<BR><TT> Notice +that this looks a lot like the server application. There's</TT> +<BR><TT> no +difference in creating server datagrams an client datagrams.</TT> +<BR><TT> You +can even use a zero-constructed address for your server datagram</TT> +<BR><TT> as +long as you tell the client where you're listening (eg -- by writting</TT> +<BR><TT> into +a file or some such).</TT> +<BR><TT> */</TT> +<BR><TT> if( dgram.open(local) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "datagram open"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> /*</TT> +<BR><TT> Yep. +We've seen this before too...</TT> +<BR><TT> */</TT> +<BR><TT> char buf[512];</TT> + +<P><TT> /*</TT> +<BR><TT> Ok, +now we're doing something different.</TT> +<BR><TT> */</TT> +<BR><TT> sprintf(buf, "Hello +World!");</TT> + +<P><TT> /*</TT> +<BR><TT> Just +like sending a telegram, we have to address our datagram.</TT> +<BR><TT> Here, +we create an address object at the desired port on the</TT> +<BR><TT> chosen +host. To keep us from crashing, we'll provide a default</TT> +<BR><TT> host +name if we aren't given one.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr +remote(PORT, argc > 1 ? argv[1] : "localhost" );</TT> + +<P><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) Sending (%s) to the server.\n",buf));</TT> +<BR><TT> /*</TT> +<BR><TT> +Now we send our buffer of stuff to the remote address. This is</TT> +<BR><TT> +just exactly what the server did after receiving a client message.</TT> +<BR><TT> +Datagrams are rather orthogonal that way: they don't generally make</TT> +<BR><TT> +much of a fuss about being either client or server.</TT> +<BR><TT> */</TT> +<BR><TT> if( dgram.send(buf,strlen(buf)+1,remote) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> /*</TT> +<BR><TT> Now +we've turned around and put ourselves into "server mode" by</TT> +<BR><TT> invoking +the recv() method. We now our server is going to send</TT> +<BR><TT> us +something, so we hang out here and wait for it. Because we</TT> +<BR><TT> know +datagrams are unreliable, there is a chance that the server</TT> +<BR><TT> will +respond but we won't hear. You might consider providing a</TT> +<BR><TT> timeout +on the recv() in that case. If recv() fails due to timeout</TT> +<BR><TT> it +will return -1 and you can then resend your query and attempt</TT> +<BR><TT> the +recv() again.</TT> +<BR><TT> */</TT> +<BR><TT> if( dgram.recv(buf,sizeof(buf),remote) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> /*</TT> +<BR><TT> Find +out what the server had to say.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) The server said: %s\n",buf));</TT> + +<P><TT> return(0);</TT> +<BR><TT>}</TT> + +<P> +<HR WIDTH="100%"> + +<P>That's all neat and good but the point of what we're doing here is not +to talk to a server we know about but to discover servers we don't know +about. Now, you could send a directed datagram to every possible +host address on your network but that's not a very nice thing to do. +On the next page, we'll find out the right approach... + +<P> +<HR WIDTH="100%"> +<CENTER>[<A HREF="../../tutorials">Tutorial Index</A>] [<A HREF="page04.html">Continue +This Tutorial</A>]</CENTER> + +</BODY> +</HTML> diff --git a/docs/tutorials/008/page04.html b/docs/tutorials/008/page04.html new file mode 100644 index 00000000000..8b6a65570ec --- /dev/null +++ b/docs/tutorials/008/page04.html @@ -0,0 +1,156 @@ +<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%"> +<BR> +<BR> In <A HREF="broadcast_client.cpp">broadcast_client.cpp</A> we +find out how to send a single datagram to every host on our (sub)network. +I have to say <I>(sub)network</I> because broadcast datagrams typically +are not passed through routers. So, if your network admin has divided +up your network into subnets, your broadcasts will likey only stay on the +subnet you're a part of. + +<P>I've only commented the parts that are different from the directed_client. + +<P> +<HR WIDTH="100%"> + +<P><TT>#include "ace/SOCK_Dgram_Bcast.h"</TT> +<BR><TT>#include "ace/INET_Addr.h"</TT> + +<P><TT>static const u_short PORT = ACE_DEFAULT_SERVER_PORT;</TT> + +<P><TT>int main(int argc,char *argv[] )</TT> +<BR><TT>{</TT> +<BR><TT> ACE_INET_Addr +local((u_short)0);</TT> + +<P><TT> /*</TT> +<BR><TT> Instead +of creating the ACE_SOCK_Dgram we created last time,</TT> +<BR><TT> we'll +create an ACE_SOCK_Dgram_Bcast. "Bcast" means, of course,</TT> +<BR><TT> "Broadcast". +This ACE object is clever enough to go out to the</TT> +<BR><TT> OS +and find all of the network interfaces. When you send()</TT> +<BR><TT> on +a Dgram_Bcast, it will send the datagram out on all of those</TT> +<BR><TT> interfaces. +This is quiet handy if you do it on a multi-homed</TT> +<BR><TT> host +that plays router...</TT> +<BR><TT> */</TT> +<BR><TT> ACE_SOCK_Dgram_Bcast +dgram;</TT> + +<P><TT> if( dgram.open(local) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "datagram open"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> char buf[512];</TT> + +<P><TT> sprintf(buf, "Hello World!");</TT> + +<P><TT> /*</TT> +<BR><TT> The +only other difference between us and the directed client</TT> +<BR><TT> is +that we don't specify a host to receive the datagram.</TT> +<BR><TT> Instead, +we use the magic value "INADDR_BROADCAST". All hosts</TT> +<BR><TT> are +obliged to respond to datagrams directed to this address</TT> +<BR><TT> the +same as they would to datagrams sent to their hostname.</TT> + +<P><TT> Remember, +the Dgram_Bcast will send a datagram to all interfaces</TT> +<BR><TT> on +the host. That's true even if the address is for a specific</TT> +<BR><TT> host +(and the host address makes sense for the interface).</TT> +<BR><TT> The +real power is in using an INADDR_BROADCAST addressed datagram</TT> +<BR><TT> against +all interfaces.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_INET_Addr +remote(PORT,INADDR_BROADCAST);</TT> + +<P><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) Sending (%s) to the server.\n",buf));</TT> +<BR><TT> </TT> +<BR><TT> if( dgram.send(buf,strlen(buf)+1,remote) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> if( dgram.recv(buf,sizeof(buf),remote) +== -1 )</TT> +<BR><TT> {</TT> +<BR><TT> +ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv"),-1);</TT> +<BR><TT> }</TT> + +<P><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) The server said: %s\n",buf));</TT> + +<P><TT> /*</TT> +<BR><TT> Using +the <I>remote</I> object instance, find out where the server lives.</TT> +<BR><TT> We +could then save this address and use directed datagrams to chat</TT> +<BR><TT> with +the server for a while.</TT> +<BR><TT> */</TT> +<BR><TT> ACE_DEBUG ((LM_DEBUG, +"(%P|%t) The server can be found at: (%s:%d)\n",</TT> +<BR><TT> +remote.get_host_addr(), PORT ));</TT> +<BR><TT> </TT> +<BR><TT> return(0);</TT> +<BR><TT>}</TT> + +<P> +<HR WIDTH="100%"> + +<P> About that subnet thing: +<BLOCKQUOTE>If you run this client on a host that has multiple network +interfaces, the broadcast will go to all of those (sub)networks. +What do you do, though, if you need to get past a router? My advice +is to write a server that will run on hosts on both sides of your router. +When a server on one side of the router receives a broadcast, it would +send a directed datagram to it's counterpart on the other side of the router. +The counterpart would then re-broadcast the original datagram on that sub-net. +Cheap, simple and effective.</BLOCKQUOTE> +One final word of warning: +<BLOCKQUOTE>When creating your broadcast datagrams you may see something +like this: <I>ACE_SOCK_Dgram_Bcast::mk_broadcast: Broadcast is not +enable for this interface.: Unknown error</I>. There are some interfaces +(ppp, slip) that don't support broadcast datagrams. That's what you're +seeing here.</BLOCKQUOTE> + +<HR WIDTH="100%"> +<CENTER>[<A HREF="../../tutorials">Tutorial Index</A>] [<A HREF="page05.html">Continue +This Tutorial</A>]</CENTER> + +</BODY> +</HTML> diff --git a/docs/tutorials/008/page05.html b/docs/tutorials/008/page05.html new file mode 100644 index 00000000000..4d683a0429e --- /dev/null +++ b/docs/tutorials/008/page05.html @@ -0,0 +1,43 @@ +<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%"> +<BR> +<BR>That's it for this tutorial. In the next one we'll add some intelligence +to the data put into the datagrams. By doing so, we'll be able to +classify our clients and servers into groups. By combining the data +content and the server's port we can get fairly fine-grained control over +who talks to who. + +<P>For you convenience: +<UL> +<LI> +<A HREF="server.cpp">server.cpp</A></LI> + +<LI> +<A HREF="directed_client.cpp">directed_client.cpp</A></LI> + +<LI> +<A HREF="broadcast_client.cpp">broadcast_client.cpp</A></LI> + +<LI> +<A HREF="Makefile">Makefile</A></LI> +</UL> + +<HR WIDTH="100%"> +<CENTER>[<A HREF="../../tutorials">Tutorial Index</A>]</CENTER> + +</BODY> +</HTML> diff --git a/docs/tutorials/008/server.cpp b/docs/tutorials/008/server.cpp new file mode 100644 index 00000000000..e702b1b93bb --- /dev/null +++ b/docs/tutorials/008/server.cpp @@ -0,0 +1,128 @@ + +// $Id$ + +/* + 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/SOCK_Dgram.h" +#include "ace/INET_Addr.h" + +/* + Use the typical TCP/IP port address for receiving datagrams. + */ +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. + + 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. + */ + 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. + */ + char buf[512]; + + /* + 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_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(). + */ + 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.O + */ + 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 symetry 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... + */ + if( client.send(buf,strlen(buf)+1,remote) == -1 ) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send"),-1); + return(0); + } + } + + return(0); +} |