⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 structs.html

📁 Beej的socket教材
💻 HTML
字号:
<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">&#13;    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">&#13;    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">&#13;    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">&#13;    // 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>&#13;<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">&#60;&#60;</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">&#13;    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">&#13;    #include &#60;sys/socket.h&#62;    #include &#60;netinet/in.h&#62;    #include &#60;arpa/inet.h&#62;    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">&#13;<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">&#13;    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", &#38;(my_addr.sin_addr));    memset(&#38;(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">&#13;    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">&#13;    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">&#13;    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">&nbsp;</TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">System Calls or Bust</TD></TR></TABLE></DIV></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -