?? structs.html
字號(hào):
<HTML><HEAD><TITLE>structs and Data Handling</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.70"><LINKREL="HOME"TITLE="Beej's Guide to Network Programming"HREF="index.html"><LINKREL="PREVIOUS"TITLE="What is a socket?"HREF="theory.html"><LINKREL="NEXT"TITLE="System Calls or Bust"HREF="syscalls.html"><METAHTTP-EQUIV="Content-Type"CONTENT="text/html; charset=utf-8"></HEAD><BODYCLASS="sect1"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">Beej's Guide to Network Programming</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="theory.html">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="syscalls.html">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="sect1"><H1CLASS="sect1"><ANAME="structs">3. <TTCLASS="type">struct</TT>s and Data Handling</A></H1><P>Well, we're finally here. It's time to talk about programming.In this section, I'll cover various data types used by the socketsinterface, since some of them are a real bear to figure out.</P><P>First the easy one: a socket descriptor. A socket descriptor isthe following type:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int </PRE></TD></TR></TABLE><P>Just a regular <TTCLASS="type">int</TT>.</P><P>Things get weird from here, so just read through and bear with me.Know this: there are two byte orderings: most significant byte(sometimes called an "octet") first, or least significant byte first.The former is called "Network Byte Order". Some machines store theirnumbers internally in Network Byte Order, some don't. When I saysomething has to be in Network Byte Order, you have to call a function(such as <TTCLASS="function">htons()</TT>) to change it from "Host ByteOrder". If I don't say "Network Byte Order", then you must leave thevalue in Host Byte Order.</P><P>(For the curious, "Network Byte Order" is also know as "Big-EndianByte Order".)</P><P>My First Struct<SUP>TM</SUP>--<TTCLASS="type">structsockaddr</TT>. This structure holds socket address information formany types of sockets:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address }; </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sa_family</I></TT> can be a variety of things, butit'll be <TTCLASS="constant">AF_INET</TT> for everything we do in thisdocument. <TTCLASS="parameter"><I>sa_data</I></TT> contains a destination addressand port number for the socket. This is rather unwieldy since you don'twant to tediously pack the address in the <TTCLASS="parameter"><I>sa_data</I></TT>by hand.</P><P>To deal with <TTCLASS="type">struct sockaddr</TT>, programmers created aparallel structure: <TTCLASS="type">struct sockaddr_in</TT> ("in" for"Internet".)</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> struct sockaddr_in { short int sin_family; // Address family unsigned short int sin_port; // Port number struct in_addr sin_addr; // Internet address unsigned char sin_zero[8]; // Same size as struct sockaddr }; </PRE></TD></TR></TABLE><P>This structure makes it easy to reference elements of the socketaddress. Note that <TTCLASS="parameter"><I>sin_zero</I></TT> (which is includedto pad the structure to the length of a <TTCLASS="type">struct sockaddr</TT>)should be set to all zeros with the function<TTCLASS="function">memset()</TT>. Also, and this is the<EM>important</EM> bit, a pointer to a <TTCLASS="type">structsockaddr_in</TT> can be cast to a pointer to a <TTCLASS="type">structsockaddr</TT> and vice-versa. So even though<TTCLASS="function">socket()</TT> wants a <TTCLASS="type">struct sockaddr*</TT>, youcan still use a <TTCLASS="type">struct sockaddr_in</TT> and cast it at the lastminute! Also, notice that <TTCLASS="parameter"><I>sin_family</I></TT> correspondsto <TTCLASS="parameter"><I>sa_family</I></TT> in a <TTCLASS="type">struct sockaddr</TT>and should be set to "<TTCLASS="constant">AF_INET</TT>". Finally, the<TTCLASS="parameter"><I>sin_port</I></TT> and <TTCLASS="parameter"><I>sin_addr</I></TT> mustbe in <EM>Network Byte Order</EM>!</P><P>"But," you object, "how can the entire structure,<TTCLASS="type">struct in_addr sin_addr</TT>, be in NetworkByte Order?" This question requires careful examination of thestructure <TTCLASS="type">struct in_addr</TT>, one of theworst unions alive:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> // Internet address (a structure for historical reasons) struct in_addr { unsigned long s_addr; // that's a 32-bit long, or 4 bytes }; </PRE></TD></TR></TABLE><P>Well, it <EM>used</EM> to be a union, but now thosedays seem to be gone. Good riddance. So if you have declared<TTCLASS="parameter"><I>ina</I></TT> to be of type <TTCLASS="type">structsockaddr_in</TT>, then <TTCLASS="parameter"><I>ina.sin_addr.s_addr</I></TT>references the 4-byte IP address (in Network Byte Order). Note thateven if your system still uses the God-awful union for <TTCLASS="type">structin_addr</TT>, you can still reference the 4-byte IP address in exactlythe same way as I did above (this due to<TTCLASS="computeroutput">#define</TT>s.)</P><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="convert">3.1. Convert the Natives!</A></H2><P>We've now been lead right into the next section. There's been toomuch talk about this Network to Host Byte Order conversion--now is thetime for action!</P><P>All righty. There are two types that you can convert:<TTCLASS="type">short</TT> (two bytes) and <TTCLASS="type">long</TT> (four bytes).These functions work for the <TTCLASS="type">unsigned</TT> variations as well.Say you want to convert a <TTCLASS="type">short</TT> from Host Byte Order toNetwork Byte Order. Start with "h" for "host", follow it with "to",then "n" for "network", and "s" for "short": h-to-n-s, or<TTCLASS="function">htons()</TT> (read: "Host to Network Short").</P><P>It's almost too easy...</P><P>You can use every combination if "n", "h", "s", and "l" you want,not counting the really stupid ones. For example, there is NOT a<TTCLASS="function">stolh()</TT> ("Short to Long Host") function--not at thisparty, anyway. But there are:</P><P> <P></P><UL><LI><P><TTCLASS="function">htons()</TT> -- "Host to Network Short"</P></LI><LI><P><TTCLASS="function">htonl()</TT> -- "Host to Network Long"</P></LI><LI><P><TTCLASS="function">ntohs()</TT> -- "Network to Host Short"</P></LI><LI><P><TTCLASS="function">ntohl()</TT> -- "Network to Host Long"</P></LI></UL></P><P>Now, you may think you're wising up to this. You might think,"What do I do if I have to change byte order on a <TTCLASS="type">char</TT>?"Then you might think, "Uh, never mind." You might also think that sinceyour 68000 machine already uses network byte order, you don't have tocall <TTCLASS="function">htonl()</TT> on your IP addresses. You would beright, <EM>BUT</EM> if you try to port to a machine that hasreverse network byte order, your program will fail. Be portable! Thisis a Unix world! (As much as Bill Gates would like to think otherwise.)Remember: put your bytes in Network Byte Order before you put them onthe network.</P><P>A final point: why do <TTCLASS="parameter"><I>sin_addr</I></TT> and<TTCLASS="parameter"><I>sin_port</I></TT> need to be in Network Byte Order in a<TTCLASS="type">struct sockaddr_in</TT>, but <TTCLASS="parameter"><I>sin_family</I></TT>does not? The answer: <TTCLASS="parameter"><I>sin_addr</I></TT> and<TTCLASS="parameter"><I>sin_port</I></TT> get encapsulated in the packet at the IPand UDP layers, respectively. Thus, they must be in Network Byte Order.However, the <TTCLASS="parameter"><I>sin_family</I></TT> field is only used by thekernel to determine what type of address the structure contains, so itmust be in Host Byte Order. Also, since<TTCLASS="parameter"><I>sin_family</I></TT> does <EM>not</EM> get sentout on the network, it can be in Host Byte Order.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="ipaddr">3.2. IP Addresses and How to Deal With Them</A></H2><P>Fortunately for you, there are a bunch of functions that allowyou to manipulate IP addresses. No need to figure them out by hand andstuff them in a <TTCLASS="type">long</TT> with the<TTCLASS="computeroutput"><<</TT> operator.</P><P>First, let's say you have a <TTCLASS="type">struct sockaddr_in ina</TT>,and you have an IP address"<TTCLASS="computeroutput">10.12.110.57</TT>" that you want to storeinto it. The function you want to use,<TTCLASS="function">inet_addr()</TT>, converts an IP address innumbers-and-dots notation into an unsigned long. The assignment can bemade as follows:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> ina.sin_addr.s_addr = inet_addr("10.12.110.57"); </PRE></TD></TR></TABLE><P>Notice that <TTCLASS="function">inet_addr()</TT> returns the addressin Network Byte Order already--you don't have to call<TTCLASS="function">htonl()</TT>. Swell!</P><P>Now, the above code snippet isn't very robust because there is noerror checking. See, <TTCLASS="function">inet_addr()</TT> returns<TTCLASS="constant">-1</TT> on error. Remember binary numbers?<TTCLASS="constant">(unsigned)-1</TT> just happens to correspond to the IPaddress <TTCLASS="computeroutput">255.255.255.255</TT>! That's thebroadcast address! Wrongo. Remember to do your error checkingproperly.</P><P>Actually, there's a cleaner interface you can use instead of<TTCLASS="function">inet_addr()</TT>: it's called<TTCLASS="function">inet_aton()</TT> ("aton" means "ascii to network"):</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp); </PRE></TD></TR></TABLE><P>And here's a sample usage, while packing a <TTCLASS="type">structsockaddr_in</TT> (this example will make more sense to you when youget to the sections on <AHREF="syscalls.html#bind"> <TTCLASS="function">bind()</TT></A> and <AHREF="syscalls.html#connect"><TTCLASS="function">connect()</TT></A>.)</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> struct sockaddr_in my_addr; my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons(MYPORT); // short, network byte order inet_aton("10.12.110.57", &(my_addr.sin_addr)); memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct </PRE></TD></TR></TABLE><P><TTCLASS="function">inet_aton()</TT>, <EM>unlike practicallyevery other socket-related function</EM>, returns non-zero onsuccess, and zero on failure. And the address is passed back in<TTCLASS="parameter"><I>inp</I></TT>.</P><P>Unfortunately, not all platforms implement<TTCLASS="function">inet_aton()</TT> so, although its use is preferred, theolder more common <TTCLASS="function">inet_addr()</TT> is used in thisguide.</P><P>All right, now you can convert string IP addresses to their binaryrepresentations. What about the other way around? What if you have a<TTCLASS="type">struct in_addr</TT> and you want to print it in numbers-and-dotsnotation? In this case, you'll want to use the function<TTCLASS="function">inet_ntoa()</TT> ("ntoa" means "network to ascii") likethis:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> printf("%s", inet_ntoa(ina.sin_addr)); </PRE></TD></TR></TABLE><P>That will print the IP address. Note that<TTCLASS="function">inet_ntoa()</TT> takes a <TTCLASS="type">struct in_addr</TT> asan argument, not a <TTCLASS="type">long</TT>. Also notice that it returns apointer to a char. This points to a statically stored char array within<TTCLASS="function">inet_ntoa()</TT> so that each time you call<TTCLASS="function">inet_ntoa()</TT> it will overwrite the last IP addressyou asked for. For example:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> char *a1, *a2; . . a1 = inet_ntoa(ina1.sin_addr); // this is 192.168.4.14 a2 = inet_ntoa(ina2.sin_addr); // this is 10.12.110.57 printf("address 1: %s\n",a1); printf("address 2: %s\n",a2); </PRE></TD></TR></TABLE><P>will print:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> address 1: 10.12.110.57 address 2: 10.12.110.57 </PRE></TD></TR></TABLE><P>If you need to save the address, <TTCLASS="function">strcpy()</TT> itto your own character array.</P><P>That's all on this topic for now. Later, you'll learn to converta string like "whitehouse.gov" into its corresponding IP address (see<AHREF="syscalls.html#dns">DNS</A>, below.)</P></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="theory.html">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="syscalls.html">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">What is a socket?</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"> </TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">System Calls or Bust</TD></TR></TABLE></DIV></BODY></HTML>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -