Correct errors reported by textproc/igor:
- wrap long lines - add two spaces after a sentence stop - use tabs instead of spaces - bad tag indent
This commit is contained in:
parent
93632dc9d5
commit
d5508ba579
Notes:
svn2git
2020-12-08 03:00:23 +00:00
svn path=/head/; revision=52177
1 changed files with 563 additions and 593 deletions
|
@ -4,29 +4,37 @@
|
|||
|
||||
$FreeBSD$
|
||||
-->
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="sockets">
|
||||
<info><title>Sockets</title>
|
||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"
|
||||
xml:id="sockets">
|
||||
<info>
|
||||
<title>Sockets</title>
|
||||
|
||||
<authorgroup>
|
||||
<author><personname><firstname>G. Adam</firstname><surname>Stanislav</surname></personname><contrib>Contributed by </contrib></author>
|
||||
<author>
|
||||
<personname>
|
||||
<firstname>G. Adam</firstname>
|
||||
<surname>Stanislav</surname>
|
||||
</personname>
|
||||
<contrib>Contributed by </contrib>
|
||||
</author>
|
||||
</authorgroup>
|
||||
</info>
|
||||
|
||||
|
||||
|
||||
<sect1 xml:id="sockets-synopsis">
|
||||
<title>Synopsis</title>
|
||||
|
||||
<para><acronym>BSD</acronym> sockets take interprocess
|
||||
communications to a new level. It is no longer necessary for the
|
||||
communicating processes to run on the same machine. They still
|
||||
<emphasis>can</emphasis>, but they do not have to.</para>
|
||||
communications to a new level. It is no longer necessary for
|
||||
the communicating processes to run on the same machine. They
|
||||
still <emphasis>can</emphasis>, but they do not have to.</para>
|
||||
|
||||
<para>Not only do these processes not have to run on the same
|
||||
machine, they do not have to run under the same operating
|
||||
system. Thanks to <acronym>BSD</acronym> sockets, your FreeBSD
|
||||
software can smoothly cooperate with a program running on a
|
||||
&macintosh;, another one running on a &sun; workstation, yet another
|
||||
one running under &windows; 2000, all connected with an
|
||||
&macintosh;, another one running on a &sun; workstation, yet
|
||||
another one running under &windows; 2000, all connected with an
|
||||
Ethernet-based local area network.</para>
|
||||
|
||||
<para>But your software can equally well cooperate with processes
|
||||
|
@ -45,9 +53,9 @@
|
|||
|
||||
<para>We have already hinted on the <emphasis>diversity</emphasis>
|
||||
of networking. Many different systems have to talk to each
|
||||
other. And they have to speak the same language. They also have
|
||||
to <emphasis>understand</emphasis> the same language the same
|
||||
way.</para>
|
||||
other. And they have to speak the same language. They also
|
||||
have to <emphasis>understand</emphasis> the same language the
|
||||
same way.</para>
|
||||
|
||||
<para>People often think that <emphasis>body language</emphasis>
|
||||
is universal. But it is not. Back in my early teens, my father
|
||||
|
@ -67,17 +75,17 @@
|
|||
his almonds, and walked away. To an uninformed observer, I did
|
||||
not change the body language: I continued using the language of
|
||||
shaking and nodding my head. What changed was the
|
||||
<emphasis>meaning</emphasis> of the body language. At first, the
|
||||
vendor and I interpreted the same language as having completely
|
||||
different meaning. I had to adjust my own interpretation of that
|
||||
language so the vendor would understand.</para>
|
||||
<emphasis>meaning</emphasis> of the body language. At first,
|
||||
the vendor and I interpreted the same language as having
|
||||
completely different meaning. I had to adjust my own
|
||||
interpretation of that language so the vendor would
|
||||
understand.</para>
|
||||
|
||||
<para>It is the same with computers: The same symbols may have
|
||||
different, even outright opposite meaning. Therefore, for
|
||||
two computers to understand each other, they must not only
|
||||
agree on the same <emphasis>language</emphasis>, but on the
|
||||
same <emphasis>interpretation</emphasis> of the language.
|
||||
</para>
|
||||
different, even outright opposite meaning. Therefore, for two
|
||||
computers to understand each other, they must not only agree on
|
||||
the same <emphasis>language</emphasis>, but on the same
|
||||
<emphasis>interpretation</emphasis> of the language.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 xml:id="sockets-protocols">
|
||||
|
@ -182,12 +190,12 @@
|
|||
<acronym>HTTP</acronym>. This protocol can tell us exactly that
|
||||
the data represents an image, and that it uses the
|
||||
<acronym>PNG</acronym> protocol. It can also tell us some other
|
||||
things, but let us stay focused on protocol layers here.
|
||||
</para>
|
||||
things, but let us stay focused on protocol layers here.</para>
|
||||
|
||||
<para>So, now we have some data wrapped in the <acronym>PNG</acronym>
|
||||
protocol, wrapped in the <acronym>HTTP</acronym> protocol.
|
||||
How did we get it from the server?</para>
|
||||
<para>So, now we have some data wrapped in the
|
||||
<acronym>PNG</acronym> protocol, wrapped in the
|
||||
<acronym>HTTP</acronym> protocol. How did we get it from the
|
||||
server?</para>
|
||||
|
||||
<para>By using <acronym>TCP/IP</acronym> over Ethernet, that is
|
||||
how. Indeed, that is three more protocols. Instead of
|
||||
|
@ -197,11 +205,11 @@
|
|||
<para>Ethernet is an interesting system of connecting computers in
|
||||
a <emphasis>local area network</emphasis>
|
||||
(<acronym>LAN</acronym>). Each computer has a <emphasis>network
|
||||
interface card</emphasis> (<acronym>NIC</acronym>), which has a
|
||||
unique 48-bit <acronym>ID</acronym> called its
|
||||
interface card</emphasis> (<acronym>NIC</acronym>), which has
|
||||
a unique 48-bit <acronym>ID</acronym> called its
|
||||
<emphasis>address</emphasis>. No two Ethernet
|
||||
<acronym>NIC</acronym>s in the world have the same address.
|
||||
</para>
|
||||
<acronym>NIC</acronym>s in the world have the same
|
||||
address.</para>
|
||||
|
||||
<para>These <acronym>NIC</acronym>s are all connected with each
|
||||
other. Whenever one computer wants to communicate with another
|
||||
|
@ -209,22 +217,22 @@
|
|||
over the network. Every <acronym>NIC</acronym> sees the
|
||||
message. But as part of the Ethernet
|
||||
<emphasis>protocol</emphasis>, the data contains the address of
|
||||
the destination <acronym>NIC</acronym> (among other things). So,
|
||||
only one of all the network interface cards will pay attention
|
||||
to it, the rest will ignore it.</para>
|
||||
the destination <acronym>NIC</acronym> (among other things).
|
||||
So, only one of all the network interface cards will pay
|
||||
attention to it, the rest will ignore it.</para>
|
||||
|
||||
<para>But not all computers are connected to the same
|
||||
network. Just because we have received the data over our
|
||||
Ethernet does not mean it originated in our own local area
|
||||
network. It could have come to us from some other network (which
|
||||
may not even be Ethernet based) connected with our own network
|
||||
via the Internet.</para>
|
||||
<para>But not all computers are connected to the same network.
|
||||
Just because we have received the data over our Ethernet does
|
||||
not mean it originated in our own local area network. It could
|
||||
have come to us from some other network (which may not even be
|
||||
Ethernet based) connected with our own network via the
|
||||
Internet.</para>
|
||||
|
||||
<para>All data is transferred over the Internet using
|
||||
<acronym>IP</acronym>, which stands for <emphasis>Internet
|
||||
Protocol</emphasis>. Its basic role is to let us know where in
|
||||
the world the data has arrived from, and where it is supposed to
|
||||
go to. It does not <emphasis>guarantee</emphasis> we will
|
||||
Protocol</emphasis>. Its basic role is to let us know where
|
||||
in the world the data has arrived from, and where it is supposed
|
||||
to go to. It does not <emphasis>guarantee</emphasis> we will
|
||||
receive the data, only that we will know where it came from
|
||||
<emphasis>if</emphasis> we do receive it.</para>
|
||||
|
||||
|
@ -260,10 +268,10 @@
|
|||
|
||||
<para>Luckily for you, you are <emphasis>not</emphasis> supposed
|
||||
to handle it all. You <emphasis>are</emphasis> supposed to
|
||||
handle some of it, but not all of it. Specifically, you need not
|
||||
worry about the physical connection (in our case Ethernet and
|
||||
possibly <acronym>PPP</acronym>, etc). Nor do you need to handle
|
||||
the Internet Protocol, or the Transmission Control
|
||||
handle some of it, but not all of it. Specifically, you need
|
||||
not worry about the physical connection (in our case Ethernet
|
||||
and possibly <acronym>PPP</acronym>, etc). Nor do you need to
|
||||
handle the Internet Protocol, or the Transmission Control
|
||||
Protocol.</para>
|
||||
|
||||
<para>In other words, you do not have to do anything to receive
|
||||
|
@ -327,8 +335,7 @@
|
|||
example, then, sockets would let us receive an <emphasis>HTTP
|
||||
file</emphasis>, so to speak. It would then be up to us to
|
||||
extract the <emphasis><acronym>PNG</acronym> file</emphasis>
|
||||
from it.
|
||||
</para>
|
||||
from it.</para>
|
||||
|
||||
<para>Because of the complexity of internetworking, we cannot just
|
||||
use the <function role="syscall">open</function> system call, or
|
||||
|
@ -368,9 +375,7 @@
|
|||
<para>The one function used by both, clients and servers, is
|
||||
&man.socket.2;. It is declared this way:</para>
|
||||
|
||||
<programlisting>
|
||||
int socket(int domain, int type, int protocol);
|
||||
</programlisting>
|
||||
<programlisting>int socket(int domain, int type, int protocol);</programlisting>
|
||||
|
||||
<para>The return value is of the same type as that of
|
||||
<function>open</function>, an integer. FreeBSD allocates
|
||||
|
@ -439,8 +444,7 @@ int socket(int domain, int type, int protocol);
|
|||
<varname>struct sockaddr</varname>. This structure is
|
||||
declared in the same file:</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* Structure used by kernel to store most
|
||||
* addresses.
|
||||
*/
|
||||
|
@ -449,8 +453,7 @@ struct sockaddr {
|
|||
sa_family_t sa_family; /* address family */
|
||||
char sa_data[14]; /* actually longer; address value */
|
||||
};
|
||||
#define SOCK_MAXADDRLEN 255 /* longest possible addresses */
|
||||
</programlisting>
|
||||
#define SOCK_MAXADDRLEN 255 /* longest possible addresses */</programlisting>
|
||||
|
||||
<para>Please note the <emphasis>vagueness</emphasis> with
|
||||
which the <varname>sa_data</varname> field is declared,
|
||||
|
@ -473,8 +476,7 @@ struct sockaddr {
|
|||
right before the definition of
|
||||
<varname>sockaddr</varname>:</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* Address families.
|
||||
*/
|
||||
#define AF_UNSPEC 0 /* unspecified */
|
||||
|
@ -519,9 +521,7 @@ struct sockaddr {
|
|||
#define AF_SCLUSTER 34 /* Sitara cluster protocol */
|
||||
#define AF_ARP 35
|
||||
#define AF_BLUETOOTH 36 /* Bluetooth sockets */
|
||||
#define AF_MAX 37
|
||||
|
||||
</programlisting>
|
||||
#define AF_MAX 37</programlisting>
|
||||
|
||||
<para>The one used for <acronym>IP</acronym> is
|
||||
<symbol>AF_INET</symbol>. It is a symbol for the constant
|
||||
|
@ -534,13 +534,12 @@ struct sockaddr {
|
|||
used.</para>
|
||||
|
||||
<para>Specifically, whenever the <emphasis>address
|
||||
family</emphasis> is <symbol>AF_INET</symbol>, we can use
|
||||
<varname>struct sockaddr_in</varname> found in
|
||||
family</emphasis> is <symbol>AF_INET</symbol>, we can
|
||||
use <varname>struct sockaddr_in</varname> found in
|
||||
<filename>netinet/in.h</filename>, wherever
|
||||
<varname>sockaddr</varname> is expected:</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* Socket address, internet style.
|
||||
*/
|
||||
struct sockaddr_in {
|
||||
|
@ -549,8 +548,7 @@ struct sockaddr_in {
|
|||
in_port_t sin_port;
|
||||
struct in_addr sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
</programlisting>
|
||||
};</programlisting>
|
||||
|
||||
<para>We can visualize its organization this way:</para>
|
||||
|
||||
|
@ -590,11 +588,13 @@ struct sockaddr_in {
|
|||
that its server will write a text string representing the
|
||||
current date and time to port 13. We want to use
|
||||
<acronym>TCP/IP</acronym>, so we need to specify
|
||||
<constant>AF_INET</constant> in the address family
|
||||
field. <constant>AF_INET</constant> is defined as
|
||||
<constant>AF_INET</constant> in the address family field.
|
||||
<constant>AF_INET</constant> is defined as
|
||||
<constant>2</constant>. Let us use the
|
||||
<acronym>IP</acronym> address of <systemitem class="ipaddress">192.43.244.18</systemitem>, which is the time
|
||||
server of US federal government (<systemitem class="fqdomainname">time.nist.gov</systemitem>).</para>
|
||||
<acronym>IP</acronym> address of <systemitem
|
||||
class="ipaddress">192.43.244.18</systemitem>, which is
|
||||
the time server of US federal government (<systemitem
|
||||
class="fqdomainname">time.nist.gov</systemitem>).</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
|
@ -624,28 +624,27 @@ struct sockaddr_in {
|
|||
type, which is defined in
|
||||
<filename>netinet/in.h</filename>:</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* Internet address (a structure for historical reasons)
|
||||
*/
|
||||
struct in_addr {
|
||||
in_addr_t s_addr;
|
||||
};
|
||||
</programlisting>
|
||||
};</programlisting>
|
||||
|
||||
<para>In addition, <varname>in_addr_t</varname> is a 32-bit
|
||||
integer.</para>
|
||||
|
||||
<para>The <systemitem class="ipaddress">192.43.244.18</systemitem> is
|
||||
just a convenient notation of expressing a 32-bit integer
|
||||
by listing all of its 8-bit bytes, starting with the
|
||||
<para>The <systemitem
|
||||
class="ipaddress">192.43.244.18</systemitem> is just a
|
||||
convenient notation of expressing a 32-bit integer by
|
||||
listing all of its 8-bit bytes, starting with the
|
||||
<emphasis>most significant</emphasis> one.</para>
|
||||
|
||||
<para>So far, we have viewed <varname>sockaddr</varname> as
|
||||
an abstraction. Our computer does not store
|
||||
<varname>short</varname> integers as a single 16-bit
|
||||
entity, but as a sequence of 2 bytes. Similarly, it stores
|
||||
32-bit integers as a sequence of 4 bytes.</para>
|
||||
entity, but as a sequence of 2 bytes. Similarly, it
|
||||
stores 32-bit integers as a sequence of 4 bytes.</para>
|
||||
|
||||
<para>Suppose we coded something like this:</para>
|
||||
|
||||
|
@ -655,8 +654,8 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
|
||||
<para>What would the result look like?</para>
|
||||
|
||||
<para>Well, that depends, of course. On a &pentium;, or other
|
||||
x86, based computer, it would look like this:</para>
|
||||
<para>Well, that depends, of course. On a &pentium;, or
|
||||
other x86, based computer, it would look like this:</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
|
@ -681,8 +680,7 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
</textobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>On a different system, it might look like this:
|
||||
</para>
|
||||
<para>On a different system, it might look like this:</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
|
@ -711,10 +709,10 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
above two are the most common ways in use today.</para>
|
||||
|
||||
<para>Ordinarily, wanting to write portable code,
|
||||
programmers pretend that these differences do not
|
||||
exist. And they get away with it (except when they code in
|
||||
assembly language). Alas, you cannot get away with it that
|
||||
easily when coding for sockets.</para>
|
||||
programmers pretend that these differences do not exist.
|
||||
And they get away with it (except when they code in
|
||||
assembly language). Alas, you cannot get away with it
|
||||
that easily when coding for sockets.</para>
|
||||
|
||||
<para>Why?</para>
|
||||
|
||||
|
@ -725,7 +723,8 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
(<acronym>LSB</acronym>) first.</para>
|
||||
|
||||
<para>You might be wondering, <emphasis><quote>So, will
|
||||
sockets not handle it for me?</quote></emphasis></para>
|
||||
sockets not handle it for
|
||||
me?</quote></emphasis></para>
|
||||
|
||||
<para>It will not.</para>
|
||||
|
||||
|
@ -741,21 +740,21 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
whatever order is native to the computer).</para>
|
||||
|
||||
<para>But the rest of the data is just
|
||||
<varname>sa_data[14]</varname> as far as sockets
|
||||
goes. Depending on the <emphasis>address
|
||||
family</emphasis>, sockets just forwards that data to its
|
||||
destination.</para>
|
||||
<varname>sa_data[14]</varname> as far as sockets goes.
|
||||
Depending on the <emphasis>address family</emphasis>,
|
||||
sockets just forwards that data to its destination.</para>
|
||||
|
||||
<para>Indeed, when we enter a port number, it is because we
|
||||
want the other computer to know what service we are asking
|
||||
for. And, when we are the server, we read the port number
|
||||
so we know what service the other computer is expecting
|
||||
from us. Either way, sockets only has to forward the port
|
||||
number as data. It does not interpret it in any way.</para>
|
||||
number as data. It does not interpret it in any
|
||||
way.</para>
|
||||
|
||||
<para>Similarly, we enter the <acronym>IP</acronym> address
|
||||
to tell everyone on the way where to send our data
|
||||
to. Sockets, again, only forwards it as data.</para>
|
||||
to tell everyone on the way where to send our data to.
|
||||
Sockets, again, only forwards it as data.</para>
|
||||
|
||||
<para>That is why, we (the <emphasis>programmers</emphasis>,
|
||||
not the <emphasis>sockets</emphasis>) have to distinguish
|
||||
|
@ -833,17 +832,17 @@ sa.sin_addr.s_addr = (((((192 << 8) | 43) << 8) | 244) << 8) |
|
|||
order</emphasis>.</para>
|
||||
|
||||
<para>We have several ways of dealing with it. One would be
|
||||
to <emphasis>reverse</emphasis> the values in our code:
|
||||
</para>
|
||||
to <emphasis>reverse</emphasis> the values in our
|
||||
code:</para>
|
||||
|
||||
<programlisting>sa.sin_family = AF_INET;
|
||||
sa.sin_port = 13 << 8;
|
||||
sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) | 192;</programlisting>
|
||||
|
||||
<para>This will <emphasis>trick</emphasis> our compiler
|
||||
into storing the data in the <emphasis>network byte
|
||||
order</emphasis>. In some cases, this is exactly the way
|
||||
to do it (e.g., when programming in assembly
|
||||
<para>This will <emphasis>trick</emphasis> our compiler into
|
||||
storing the data in the <emphasis>network byte
|
||||
order</emphasis>. In some cases, this is exactly the
|
||||
way to do it (e.g., when programming in assembly
|
||||
language). In most cases, however, it can cause a
|
||||
problem.</para>
|
||||
|
||||
|
@ -862,10 +861,10 @@ sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) |
|
|||
|
||||
<para>You have since forgotten that you had forced all of
|
||||
your constants to the opposite of the <emphasis>host
|
||||
order</emphasis>. You spend some quality time tearing out
|
||||
your hair, calling the names of all gods you ever heard
|
||||
of (and some you made up), hitting your monitor with a
|
||||
nerf bat, and performing all the other traditional
|
||||
order</emphasis>. You spend some quality time tearing
|
||||
out your hair, calling the names of all gods you ever
|
||||
heard of (and some you made up), hitting your monitor with
|
||||
a nerf bat, and performing all the other traditional
|
||||
ceremonies of trying to figure out why something that has
|
||||
worked so well is suddenly not working at all.</para>
|
||||
|
||||
|
@ -876,10 +875,10 @@ sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) |
|
|||
problem. Someone else has created the &man.htons.3; and
|
||||
&man.htonl.3; C functions to convert a
|
||||
<varname>short</varname> and <varname>long</varname>
|
||||
respectively from the <emphasis>host byte
|
||||
order</emphasis> to the <emphasis>network byte
|
||||
order</emphasis>, and the &man.ntohs.3; and &man.ntohl.3;
|
||||
C functions to go the other way.</para>
|
||||
respectively from the <emphasis>host byte order</emphasis>
|
||||
to the <emphasis>network byte order</emphasis>, and the
|
||||
&man.ntohs.3; and &man.ntohl.3; C functions to go the
|
||||
other way.</para>
|
||||
|
||||
<para>On <emphasis><acronym>MSB</acronym>-first</emphasis>
|
||||
systems these functions do nothing. On
|
||||
|
@ -887,11 +886,9 @@ sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) |
|
|||
they convert values to the proper order.</para>
|
||||
|
||||
<para>So, regardless of what system your software is
|
||||
compiled on, your data will end up in the correct order
|
||||
if you use these functions.</para>
|
||||
|
||||
compiled on, your data will end up in the correct order if
|
||||
you use these functions.</para>
|
||||
</sect4>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3 xml:id="sockets-client-functions">
|
||||
|
@ -913,9 +910,7 @@ sa.sin_addr.s_addr = (((((18 << 8) | 244) << 8) | 43) << 8) |
|
|||
connect it to a specific port on a remote system. It uses
|
||||
&man.connect.2;:</para>
|
||||
|
||||
<programlisting>
|
||||
int connect(int s, const struct sockaddr *name, socklen_t namelen);
|
||||
</programlisting>
|
||||
<programlisting>int connect(int s, const struct sockaddr *name, socklen_t namelen);</programlisting>
|
||||
|
||||
<para>The <varname>s</varname> argument is the socket, i.e.,
|
||||
the value returned by the <function>socket</function>
|
||||
|
@ -938,18 +933,17 @@ int connect(int s, const struct sockaddr *name, socklen_t namelen);
|
|||
listening at the specified port. Or it may outright
|
||||
<emphasis>refuse</emphasis> any request for specific
|
||||
code.</para>
|
||||
|
||||
</sect4>
|
||||
|
||||
<sect4 xml:id="sockets-first-client">
|
||||
<title>Our First Client</title>
|
||||
|
||||
<para>We now know enough to write a very simple client, one
|
||||
that will get current time from <systemitem class="ipaddress">192.43.244.18</systemitem> and print it to
|
||||
<filename>stdout</filename>.</para>
|
||||
that will get current time from <systemitem
|
||||
class="ipaddress">192.43.244.18</systemitem> and print
|
||||
it to <filename>stdout</filename>.</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* daytime.c
|
||||
*
|
||||
* Programmed by G. Adam Stanislav
|
||||
|
@ -987,8 +981,7 @@ int main() {
|
|||
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
}</programlisting>
|
||||
|
||||
<para>Go ahead, enter it in your editor, save it as
|
||||
<filename>daytime.c</filename>, then compile and run
|
||||
|
@ -1003,21 +996,19 @@ int main() {
|
|||
<para>In this case, the date was June 19, 2001, the time was
|
||||
02:29:25 <acronym>UTC</acronym>. Naturally, your results
|
||||
will vary.</para>
|
||||
|
||||
</sect4>
|
||||
|
||||
</sect3>
|
||||
|
||||
<sect3 xml:id="sockets-server-functions">
|
||||
<title>Server Functions</title>
|
||||
|
||||
<para>The typical server does not initiate the
|
||||
connection. Instead, it waits for a client to call it and
|
||||
request services. It does not know when the client will
|
||||
call, nor how many clients will call. It may be just sitting
|
||||
there, waiting patiently, one moment, The next moment, it
|
||||
can find itself swamped with requests from a number of
|
||||
clients, all calling in at the same time.</para>
|
||||
<para>The typical server does not initiate the connection.
|
||||
Instead, it waits for a client to call it and request
|
||||
services. It does not know when the client will call, nor
|
||||
how many clients will call. It may be just sitting there,
|
||||
waiting patiently, one moment, The next moment, it can find
|
||||
itself swamped with requests from a number of clients, all
|
||||
calling in at the same time.</para>
|
||||
|
||||
<para>The sockets interface offers three basic functions to
|
||||
handle this.</para>
|
||||
|
@ -1036,22 +1027,18 @@ int main() {
|
|||
specific extension. We use &man.bind.2; to tell sockets
|
||||
which port we want to serve.</para>
|
||||
|
||||
<programlisting>
|
||||
int bind(int s, const struct sockaddr *addr, socklen_t addrlen);
|
||||
</programlisting>
|
||||
<programlisting>int bind(int s, const struct sockaddr *addr, socklen_t addrlen);</programlisting>
|
||||
|
||||
<para>Beside specifying the port in <varname>addr</varname>,
|
||||
the server may include its <acronym>IP</acronym>
|
||||
address. However, it can just use the symbolic constant
|
||||
the server may include its <acronym>IP</acronym> address.
|
||||
However, it can just use the symbolic constant
|
||||
<symbol>INADDR_ANY</symbol> to indicate it will serve all
|
||||
requests to the specified port regardless of what its
|
||||
<acronym>IP</acronym> address is. This symbol, along with
|
||||
several similar ones, is declared in
|
||||
<filename>netinet/in.h</filename></para>
|
||||
|
||||
<programlisting>
|
||||
#define INADDR_ANY (u_int32_t)0x00000000
|
||||
</programlisting>
|
||||
<programlisting>#define INADDR_ANY (u_int32_t)0x00000000</programlisting>
|
||||
|
||||
<para>Suppose we were writing a server for the
|
||||
<emphasis>daytime</emphasis> protocol over
|
||||
|
@ -1089,23 +1076,21 @@ int bind(int s, const struct sockaddr *addr, socklen_t addrlen);
|
|||
<para>To continue our office phone analogy, after you have
|
||||
told the phone central operator what extension you will be
|
||||
at, you now walk into your office, and make sure your own
|
||||
phone is plugged in and the ringer is turned on. Plus, you
|
||||
make sure your call waiting is activated, so you can hear
|
||||
the phone ring even while you are talking to someone.</para>
|
||||
phone is plugged in and the ringer is turned on. Plus,
|
||||
you make sure your call waiting is activated, so you can
|
||||
hear the phone ring even while you are talking to
|
||||
someone.</para>
|
||||
|
||||
<para>The server ensures all of that with the &man.listen.2;
|
||||
function.</para>
|
||||
|
||||
<programlisting>
|
||||
int listen(int s, int backlog);
|
||||
</programlisting>
|
||||
<programlisting>int listen(int s, int backlog);</programlisting>
|
||||
|
||||
<para>In here, the <varname>backlog</varname> variable tells
|
||||
sockets how many incoming requests to accept while you are
|
||||
busy processing the last request. In other words, it
|
||||
determines the maximum size of the queue of pending
|
||||
connections.</para>
|
||||
|
||||
</sect4>
|
||||
|
||||
<sect4 xml:id="sockets-accept">
|
||||
|
@ -1119,9 +1104,7 @@ int listen(int s, int backlog);
|
|||
<para>The server accepts the connection by using the
|
||||
&man.accept.2; function.</para>
|
||||
|
||||
<programlisting>
|
||||
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
||||
</programlisting>
|
||||
<programlisting>int accept(int s, struct sockaddr *addr, socklen_t *addrlen);</programlisting>
|
||||
|
||||
<para>Note that this time <varname>addrlen</varname> is a
|
||||
pointer. This is necessary because in this case it is the
|
||||
|
@ -1138,11 +1121,10 @@ int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
|||
variable we passed to <function>listen</function>?) until
|
||||
we <function>close</function> it.</para>
|
||||
|
||||
<para>Now, the new socket is meant only for
|
||||
communications. It is fully connected. We cannot pass it
|
||||
to <function>listen</function> again, trying to accept
|
||||
<para>Now, the new socket is meant only for communications.
|
||||
It is fully connected. We cannot pass it to
|
||||
<function>listen</function> again, trying to accept
|
||||
additional connections.</para>
|
||||
|
||||
</sect4>
|
||||
|
||||
<sect4 xml:id="sockets-first-server">
|
||||
|
@ -1163,8 +1145,7 @@ int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
|
|||
starts an endless loop, which accepts a connection, serves
|
||||
it, and eventually closes its socket.</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* daytimed - a port 13 server
|
||||
*
|
||||
* Programmed by G. Adam Stanislav
|
||||
|
@ -1251,18 +1232,15 @@ int main() {
|
|||
|
||||
fclose(client);
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
}</programlisting>
|
||||
|
||||
<para>We start by creating a socket. Then we fill out the
|
||||
<varname>sockaddr_in</varname> structure in
|
||||
<varname>sa</varname>. Note the conditional use of
|
||||
<symbol>INADDR_ANY</symbol>:</para>
|
||||
|
||||
<programlisting>
|
||||
if (INADDR_ANY)
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
</programlisting>
|
||||
<programlisting>if (INADDR_ANY)
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);</programlisting>
|
||||
|
||||
<para>Its value is <constant>0</constant>. Since we have
|
||||
just used <function>bzero</function> on the entire
|
||||
|
@ -1291,32 +1269,39 @@ int main() {
|
|||
its backlog to <constant>4</constant>. It does not need a
|
||||
large value here because <emphasis>daytime</emphasis> is
|
||||
not a protocol many clients request all the time, and
|
||||
because it can process each request instantly anyway.</para>
|
||||
because it can process each request instantly
|
||||
anyway.</para>
|
||||
|
||||
<para>Finally, the daemon starts an endless loop, which
|
||||
performs the following steps:</para>
|
||||
|
||||
<procedure>
|
||||
<step><para> Call <function>accept</function>. It waits
|
||||
here until a client contacts it. At that point, it
|
||||
<step>
|
||||
<para>Call <function>accept</function>. It waits here
|
||||
until a client contacts it. At that point, it
|
||||
receives a new socket, <varname>c</varname>, which it
|
||||
can use to communicate with this particular client.
|
||||
</para></step>
|
||||
can use to communicate with this particular
|
||||
client.</para>
|
||||
</step>
|
||||
|
||||
<step><para>It uses the C function
|
||||
<function>fdopen</function> to turn the socket from a
|
||||
low-level <emphasis>file descriptor</emphasis> to a
|
||||
C-style <varname>FILE</varname> pointer. This will allow
|
||||
the use of <function>fprintf</function> later on.
|
||||
</para></step>
|
||||
|
||||
<step><para>It checks the time, and prints it in the
|
||||
<emphasis><acronym>ISO</acronym> 8601</emphasis> format
|
||||
to the <varname>client</varname> <quote>file</quote>. It
|
||||
then uses <function>fclose</function> to close the
|
||||
file. That will automatically close the socket as well.
|
||||
</para></step>
|
||||
<step>
|
||||
<para>It uses the C function <function>fdopen</function>
|
||||
to turn the socket from a low-level <emphasis>file
|
||||
descriptor</emphasis> to a C-style
|
||||
<varname>FILE</varname> pointer. This will allow the
|
||||
use of <function>fprintf</function> later
|
||||
on.</para>
|
||||
</step>
|
||||
|
||||
<step>
|
||||
<para>It checks the time, and prints it in the
|
||||
<emphasis><acronym>ISO</acronym> 8601</emphasis>
|
||||
format to the <varname>client</varname>
|
||||
<quote>file</quote>. It then uses
|
||||
<function>fclose</function> to close the file. That
|
||||
will automatically close the socket as
|
||||
well.</para>
|
||||
</step>
|
||||
</procedure>
|
||||
|
||||
<para>We can <emphasis>generalize</emphasis> this, and use
|
||||
|
@ -1364,12 +1349,13 @@ int main() {
|
|||
<para>This flowchart is good for <emphasis>sequential
|
||||
servers</emphasis>, i.e., servers that can serve one
|
||||
client at a time, just as we were able to with our
|
||||
<emphasis>daytime</emphasis> server. This is only possible
|
||||
whenever there is no real <quote>conversation</quote>
|
||||
going on between the client and the server: As soon as the
|
||||
server detects a connection to the client, it sends out
|
||||
some data and closes the connection. The entire operation
|
||||
may take nanoseconds, and it is finished.</para>
|
||||
<emphasis>daytime</emphasis> server. This is only
|
||||
possible whenever there is no real
|
||||
<quote>conversation</quote> going on between the client
|
||||
and the server: As soon as the server detects a connection
|
||||
to the client, it sends out some data and closes the
|
||||
connection. The entire operation may take nanoseconds,
|
||||
and it is finished.</para>
|
||||
|
||||
<para>The advantage of this flowchart is that, except for
|
||||
the brief moment after the parent
|
||||
|
@ -1395,8 +1381,8 @@ int main() {
|
|||
|
||||
<para>Not all protocols are that simple. Many receive a
|
||||
request from the client, reply to it, then receive another
|
||||
request from the same client. Because of that, they do not
|
||||
know in advance how long they will be serving the
|
||||
request from the same client. Because of that, they do
|
||||
not know in advance how long they will be serving the
|
||||
client. Such servers usually start a new process for each
|
||||
client. While the new process is serving its client, the
|
||||
daemon can continue listening for more connections.</para>
|
||||
|
@ -1430,9 +1416,9 @@ bind: Permission denied
|
|||
bind: Address already in use
|
||||
&prompt.root;</screen>
|
||||
|
||||
<para>Every port can only be bound by one program at a
|
||||
time. Our first attempt was indeed successful: It started
|
||||
the child daemon and returned quietly. It is still running
|
||||
<para>Every port can only be bound by one program at a time.
|
||||
Our first attempt was indeed successful: It started the
|
||||
child daemon and returned quietly. It is still running
|
||||
and will continue to run until you either kill it, or any
|
||||
of its system calls fail, or you reboot the system.</para>
|
||||
|
||||
|
@ -1458,8 +1444,8 @@ Connection closed by foreign host.
|
|||
|
||||
<para>If you have access to another &unix; system via
|
||||
<application>telnet</application>, you can use it to test
|
||||
accessing the server remotely. My computer does not have a
|
||||
static <acronym>IP</acronym> address, so this is what I
|
||||
accessing the server remotely. My computer does not have
|
||||
a static <acronym>IP</acronym> address, so this is what I
|
||||
did:</para>
|
||||
|
||||
<screen>&prompt.user; <userinput>who</userinput>
|
||||
|
@ -1475,8 +1461,8 @@ Escape character is '^]'.
|
|||
Connection closed by foreign host.
|
||||
&prompt.user;</screen>
|
||||
|
||||
<para>Again, it worked. Will it work using the domain name?
|
||||
</para>
|
||||
<para>Again, it worked. Will it work using the domain
|
||||
name?</para>
|
||||
|
||||
<screen>&prompt.user; <userinput>telnet r47.bfm.org 13</userinput>
|
||||
|
||||
|
@ -1489,17 +1475,13 @@ Connection closed by foreign host.
|
|||
|
||||
<para>By the way, <application>telnet</application> prints
|
||||
the <emphasis>Connection closed by foreign host</emphasis>
|
||||
message after our daemon has closed the socket. This shows
|
||||
us that, indeed, using
|
||||
message after our daemon has closed the socket. This
|
||||
shows us that, indeed, using
|
||||
<function>fclose(client);</function> in our code works as
|
||||
advertised.</para>
|
||||
|
||||
</sect4>
|
||||
|
||||
</sect3>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 xml:id="sockets-helper-functions">
|
||||
|
@ -1511,21 +1493,18 @@ Connection closed by foreign host.
|
|||
<acronym>IP</acronym> address. But we do not always know the
|
||||
<acronym>IP</acronym> address. Even if we do, our software is
|
||||
more flexible if it allows the user to enter the
|
||||
<acronym>IP</acronym> address, or even the domain name.
|
||||
</para>
|
||||
<acronym>IP</acronym> address, or even the domain name.</para>
|
||||
|
||||
<sect2 xml:id="sockets-gethostbyname">
|
||||
<title><function>gethostbyname</function></title>
|
||||
|
||||
<para>While there is no way to pass the domain name directly to
|
||||
any of the sockets functions, the FreeBSD C library comes with
|
||||
the &man.gethostbyname.3; and &man.gethostbyname2.3; functions,
|
||||
declared in <filename>netdb.h</filename>.</para>
|
||||
the &man.gethostbyname.3; and &man.gethostbyname2.3;
|
||||
functions, declared in <filename>netdb.h</filename>.</para>
|
||||
|
||||
<programlisting>
|
||||
struct hostent * gethostbyname(const char *name);
|
||||
struct hostent * gethostbyname2(const char *name, int af);
|
||||
</programlisting>
|
||||
<programlisting>struct hostent * gethostbyname(const char *name);
|
||||
struct hostent * gethostbyname2(const char *name, int af);</programlisting>
|
||||
|
||||
<para>Both return a pointer to the <varname>hostent</varname>
|
||||
structure, with much information about the domain. For our
|
||||
|
@ -1538,8 +1517,7 @@ struct hostent * gethostbyname2(const char *name, int af);
|
|||
much more useful—version of our
|
||||
<application>daytime</application> program:</para>
|
||||
|
||||
<programlisting>
|
||||
/*
|
||||
<programlisting>/*
|
||||
* daytime.c
|
||||
*
|
||||
* Programmed by G. Adam Stanislav
|
||||
|
@ -1589,24 +1567,26 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
close(s);
|
||||
return 0;
|
||||
}
|
||||
</programlisting>
|
||||
}</programlisting>
|
||||
|
||||
<para>We now can type a domain name (or an <acronym>IP</acronym>
|
||||
address, it works both ways) on the command line, and the
|
||||
program will try to connect to its
|
||||
<emphasis>daytime</emphasis> server. Otherwise, it will still
|
||||
default to <systemitem class="fqdomainname">time.nist.gov</systemitem>. However, even in
|
||||
this case we will use <function>gethostbyname</function>
|
||||
rather than hard coding <systemitem class="ipaddress">192.43.244.18</systemitem>. That way, even if its
|
||||
<acronym>IP</acronym> address changes in the future, we will
|
||||
still find it.</para>
|
||||
default to <systemitem
|
||||
class="fqdomainname">time.nist.gov</systemitem>. However,
|
||||
even in this case we will use
|
||||
<function>gethostbyname</function> rather than hard coding
|
||||
<systemitem class="ipaddress">192.43.244.18</systemitem>.
|
||||
That way, even if its <acronym>IP</acronym> address changes in
|
||||
the future, we will still find it.</para>
|
||||
|
||||
<para>Since it takes virtually no time to get the time from your
|
||||
local server, you could run <application>daytime</application>
|
||||
twice in a row: First to get the time from <systemitem class="fqdomainname">time.nist.gov</systemitem>, the second time from
|
||||
your own system. You can then compare the results and see how
|
||||
exact your system clock is:</para>
|
||||
twice in a row: First to get the time from <systemitem
|
||||
class="fqdomainname">time.nist.gov</systemitem>, the second
|
||||
time from your own system. You can then compare the results
|
||||
and see how exact your system clock is:</para>
|
||||
|
||||
<screen>&prompt.user; <userinput>daytime ; daytime localhost</userinput>
|
||||
|
||||
|
@ -1617,7 +1597,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
<para>As you can see, my system was two seconds ahead of the
|
||||
<acronym>NIST</acronym> time.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 xml:id="sockets-getservbyname">
|
||||
|
@ -1628,9 +1607,7 @@ int main(int argc, char *argv[]) {
|
|||
<filename>netdb.h</filename> comes in very handy in those
|
||||
cases:</para>
|
||||
|
||||
<programlisting>
|
||||
struct servent * getservbyname(const char *name, const char *proto);
|
||||
</programlisting>
|
||||
<programlisting>struct servent * getservbyname(const char *name, const char *proto);</programlisting>
|
||||
|
||||
<para>The <varname>servent</varname> structure contains the
|
||||
<varname>s_port</varname>, which contains the proper port,
|
||||
|
@ -1640,29 +1617,25 @@ struct servent * getservbyname(const char *name, const char *proto);
|
|||
<emphasis>daytime</emphasis> service, we could have found it
|
||||
this way:</para>
|
||||
|
||||
<programlisting>
|
||||
struct servent *se;
|
||||
<programlisting>struct servent *se;
|
||||
...
|
||||
if ((se = getservbyname("daytime", "tcp")) == NULL {
|
||||
fprintf(stderr, "Cannot determine which port to use.\n");
|
||||
return 7;
|
||||
}
|
||||
sa.sin_port = se->s_port;
|
||||
</programlisting>
|
||||
sa.sin_port = se->s_port;</programlisting>
|
||||
|
||||
<para>You usually do know the port. But if you are developing a
|
||||
new protocol, you may be testing it on an unofficial
|
||||
port. Some day, you will register the protocol and its port
|
||||
(if nowhere else, at least in your
|
||||
new protocol, you may be testing it on an unofficial port.
|
||||
Some day, you will register the protocol and its port (if
|
||||
nowhere else, at least in your
|
||||
<filename>/etc/services</filename>, which is where
|
||||
<function>getservbyname</function> looks). Instead of
|
||||
returning an error in the above code, you just use the
|
||||
temporary port number. Once you have listed the protocol in
|
||||
<filename>/etc/services</filename>, your software will find
|
||||
its port without you having to rewrite the code.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 xml:id="sockets-concurrent-servers">
|
||||
|
@ -1724,13 +1697,13 @@ struct servent * getservbyname(const char *name, const char *proto);
|
|||
|
||||
<para>We moved the <emphasis>serve</emphasis> from the
|
||||
<emphasis>daemon process</emphasis> to its own <emphasis>server
|
||||
process</emphasis>. However, because each child process inherits
|
||||
all open files (and a socket is treated just like a file), the
|
||||
new process inherits not only the <emphasis><quote>accepted
|
||||
handle,</quote></emphasis> i.e., the socket returned by the
|
||||
<function>accept</function> call, but also the <emphasis>top
|
||||
socket</emphasis>, i.e., the one opened by the top process right
|
||||
at the beginning.</para>
|
||||
process</emphasis>. However, because each child process
|
||||
inherits all open files (and a socket is treated just like a
|
||||
file), the new process inherits not only the
|
||||
<emphasis><quote>accepted handle,</quote></emphasis> i.e., the
|
||||
socket returned by the <function>accept</function> call, but
|
||||
also the <emphasis>top socket</emphasis>, i.e., the one opened
|
||||
by the top process right at the beginning.</para>
|
||||
|
||||
<para>However, the <emphasis>server process</emphasis> does not
|
||||
need this socket and should <function>close</function> it
|
||||
|
@ -1744,8 +1717,7 @@ struct servent * getservbyname(const char *name, const char *proto);
|
|||
<para>After the <emphasis>server process</emphasis> is done
|
||||
serving, it should close the <emphasis>accepted
|
||||
socket</emphasis>. Instead of returning to
|
||||
<function>accept</function>, it now exits.
|
||||
</para>
|
||||
<function>accept</function>, it now exits.</para>
|
||||
|
||||
<para>Under &unix;, a process does not really
|
||||
<emphasis>exit</emphasis>. Instead, it
|
||||
|
@ -1766,13 +1738,11 @@ struct servent * getservbyname(const char *name, const char *proto);
|
|||
release the system resources they are taking up.</para>
|
||||
|
||||
<para>That is why our flowchart now contains a <emphasis>process
|
||||
signals</emphasis> box, which is not connected to any other box.
|
||||
By the way, many servers also process <symbol>SIGHUP</symbol>,
|
||||
and typically interpret as the signal from the superuser that
|
||||
they should reread their configuration files. This allows us to
|
||||
change settings without having to kill and restart these
|
||||
servers.</para>
|
||||
|
||||
signals</emphasis> box, which is not connected to any other
|
||||
box. By the way, many servers also process
|
||||
<symbol>SIGHUP</symbol>, and typically interpret as the signal
|
||||
from the superuser that they should reread their configuration
|
||||
files. This allows us to change settings without having to kill
|
||||
and restart these servers.</para>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
|
Loading…
Reference in a new issue