?? syscalls.html
字號:
<HTML><HEAD><TITLE>System Calls or Bust</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="structs and Data Handling"HREF="structs.html"><LINKREL="NEXT"TITLE="Client-Server Background"HREF="clientserver.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="structs.html">Prev</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="clientserver.html">Next</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="sect1"><H1CLASS="sect1"><ANAME="syscalls">4. System Calls or Bust</A></H1><P>This is the section where we get into the system calls that allowyou to access the network functionality of a Unix box. When you callone of these functions, the kernel takes over and does all the work foryou automagically.</P><P>The place most people get stuck around here is what order to callthese things in. In that, the <BCLASS="command">man</B> pages are no use,as you've probably discovered. Well, to help with that dreadfulsituation, I've tried to lay out the system calls in the followingsections in <EM>exactly</EM> (approximately) the same orderthat you'll need to call them in your programs.</P><P>That, coupled with a few pieces of sample code here and there,some milk and cookies (which I fear you will have to supply yourself),and some raw guts and courage, and you'll be beaming data around theInternet like the Son of Jon Postel!</P><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="socket">4.1. <TTCLASS="function">socket()</TT>--Get the File Descriptor!</A></H2><P>I guess I can put it off no longer--I have to talk about the<TTCLASS="function">socket()</TT> system call. Here's the breakdown:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); </PRE></TD></TR></TABLE><P>But what are these arguments? First,<TTCLASS="parameter"><I>domain</I></TT> should be set to"<TTCLASS="constant">AF_INET</TT>", just like in the <TTCLASS="type">structsockaddr_in</TT> (above.) Next, the <TTCLASS="parameter"><I>type</I></TT>argument tells the kernel what kind of socket this is:<TTCLASS="constant">SOCK_STREAM</TT> or <TTCLASS="constant">SOCK_DGRAM</TT>.Finally, just set <TTCLASS="parameter"><I>protocol</I></TT> to"<TTCLASS="constant">0</TT>" to have <TTCLASS="function">socket()</TT> choosethe correct protocol based on the <TTCLASS="parameter"><I>type</I></TT>. (Notes:there are many more <TTCLASS="parameter"><I>domain</I></TT>s than I've listed.There are many more <TTCLASS="parameter"><I>type</I></TT>s than I've listed. Seethe <TTCLASS="function">socket()</TT> man page. Also, there's a "better"way to get the <TTCLASS="parameter"><I>protocol</I></TT>. See the<TTCLASS="function">getprotobyname()</TT> man page.)</P><P><TTCLASS="function">socket()</TT> simply returns to you a socketdescriptor that you can use in later system calls, or<TTCLASS="constant">-1</TT> on error. The global variable<TTCLASS="parameter"><I>errno</I></TT> is set to the error's value (see the<TTCLASS="function">perror()</TT> man page.)</P><P>In some documentation, you'll see mention of a mystical"<TTCLASS="constant">PF_INET</TT>". This is a weird etherial beast that israrely seen in nature, but I might as well clarify it a bit here. Oncea long time ago, it was thought that maybe a address family (what the"AF" in "<TTCLASS="constant">AF_INET</TT>" stands for) might support severalprotocols that were referenced by their protocol family (what the "PF" in"<TTCLASS="constant">PF_INET</TT>" stands for). That didn't happen. Ohwell. So the correct thing to do is to use <TTCLASS="constant">AF_INET</TT>in your <TTCLASS="type">struct sockaddr_in</TT> and <TTCLASS="constant">PF_INET</TT>in your call to <TTCLASS="function">socket()</TT>. But practicallyspeaking, you can use <TTCLASS="constant">AF_INET</TT> everywhere. And,since that's what W. Richard Stevens does in his book, that's what I'lldo here.</P><P>Fine, fine, fine, but what good is this socket? The answer isthat it's really no good by itself, and you need to read on and makemore system calls for it to make any sense.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="bind">4.2. <TTCLASS="function">bind()</TT>--What port am I on?</A></H2><P>Once you have a socket, you might have to associate that socketwith a port on your local machine. (This is commonly done if you'regoing to <TTCLASS="function">listen()</TT> for incoming connections on aspecific port--MUDs do this when they tell you to "telnet to x.y.z port6969".) The port number is used by the kernel to match an incomingpacket to a certain process's socket descriptor. If you're going toonly be doing a <TTCLASS="function">connect()</TT>, this may be unnecessary.Read it anyway, just for kicks.</P><P>Here is the synopsis for the <TTCLASS="function">bind()</TT> systemcall:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is the socket file descriptorreturned by <TTCLASS="function">socket()</TT>.<TTCLASS="parameter"><I>my_addr</I></TT> is a pointer to a <TTCLASS="type">structsockaddr</TT> that contains information about your address, namely,port and IP address. <TTCLASS="parameter"><I>addrlen</I></TT> can be set to<TTCLASS="computeroutput">sizeof(struct sockaddr)</TT>.</P><P>Whew. That's a bit to absorb in one chunk. Let's have anexample:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define MYPORT 3490 main() { int sockfd; struct sockaddr_in my_addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); // do some error checking! my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons(MYPORT); // short, network byte order my_addr.sin_addr.s_addr = inet_addr("10.12.110.57"); memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct // don't forget your error checking for bind(): bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); . . . </PRE></TD></TR></TABLE><P>There are a few things to notice here:<TTCLASS="parameter"><I>my_addr.sin_port</I></TT> is in Network Byte Order. So is<TTCLASS="parameter"><I>my_addr.sin_addr.s_addr</I></TT>. Another thing to watchout for is that the header files might differ from system to system. Tobe sure, you should check your local <BCLASS="command">man</B>pages.</P><P>Lastly, on the topic of <TTCLASS="function">bind()</TT>, Ishould mention that some of the process of getting your own IP addressand/or port can can be automated:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> my_addr.sin_port = 0; // choose an unused port at random my_addr.sin_addr.s_addr = INADDR_ANY; // use my IP address </PRE></TD></TR></TABLE><P>See, by setting <TTCLASS="parameter"><I>my_addr.sin_port</I></TT> to zero,you are telling <TTCLASS="function">bind()</TT> to choose the port for you.Likewise, by setting <TTCLASS="parameter"><I>my_addr.sin_addr.s_addr</I></TT> to<TTCLASS="constant">INADDR_ANY</TT>, you are telling it to automaticallyfill in the IP address of the machine the process is running on.</P><P>If you are into noticing little things, you might have seen that Ididn't put <TTCLASS="constant">INADDR_ANY</TT> into Network Byte Order!Naughty me. However, I have inside info:<TTCLASS="constant">INADDR_ANY</TT> is really zero! Zero still has zero onbits even if you rearrange the bytes. However, purists will point outthat there could be a parallel dimension where<TTCLASS="constant">INADDR_ANY</TT> is, say, 12 and that my code won't workthere. That's ok with me:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> my_addr.sin_port = htons(0); // choose an unused port at random my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // use my IP address </PRE></TD></TR></TABLE><P>Now we're so portable you probably wouldn't believe it. I justwanted to point that out, since most of the code you come across won'tbother running <TTCLASS="constant">INADDR_ANY</TT> through<TTCLASS="function">htonl()</TT>.</P><P><TTCLASS="function">bind()</TT> also returns <TTCLASS="constant">-1</TT>on error and sets <TTCLASS="parameter"><I>errno</I></TT> to the error'svalue.</P><P>Another thing to watch out for when calling<TTCLASS="function">bind()</TT>: don't go underboard with your port numbers.All ports below 1024 are RESERVED (unless you're the superuser)! Youcan have any port number above that, right up to 65535 (provided theyaren't already being used by another program.)</P><P>Sometimes, you might notice, you try to rerun a server and<TTCLASS="function">bind()</TT> fails, claiming "Address already in use."What does that mean? Well, a bit a of socket that was connected isstill hanging around in the kernel, and it's hogging the port. You caneither wait for it to clear (a minute or so), or add code to yourprogram allowing it to reuse the port, like this:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int yes=1; //char yes='1'; // Solaris people use this // lose the pesky "Address already in use" error message if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) { perror("setsockopt"); exit(1); } </PRE></TD></TR></TABLE><P>One small extra final note about <TTCLASS="function">bind()</TT>:there are times when you won't absolutely have to call it. If you are<TTCLASS="function">connect()</TT>ing to a remote machine and you don'tcare what your local port is (as is the case with<BCLASS="command">telnet</B> where you only care about the remote port),you can simply call <TTCLASS="function">connect()</TT>, it'll check to seeif the socket is unbound, and will <TTCLASS="function">bind()</TT> it to anunused local port if necessary.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="connect">4.3. <TTCLASS="function">connect()</TT>--Hey, you!</A></H2><P>Let's just pretend for a few minutes that you're a telnetapplication. Your user commands you (just like in the movie<EM>TRON</EM>) to get a socket file descriptor. You complyand call <TTCLASS="function">socket()</TT>. Next, the usertells you to connect to "<TTCLASS="computeroutput">10.12.110.57</TT>"on port "<TTCLASS="computeroutput">23</TT>" (the standard telnetport.) Yow! What do you do now?</P><P>Lucky for you, program, you're now perusing the section on<TTCLASS="function">connect()</TT>--how to connect to a remote host. Soread furiously onward! No time to lose!</P><P>The <TTCLASS="function">connect()</TT> call is as follows:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is our friendly neighborhood socketfile descriptor, as returned by the <TTCLASS="function">socket()</TT> call,<TTCLASS="parameter"><I>serv_addr</I></TT> is a <TTCLASS="type">struct sockaddr</TT>containing the destination port and IP address, and<TTCLASS="parameter"><I>addrlen</I></TT> can be set to<TTCLASS="computeroutput">sizeof(struct sockaddr)</TT>.</P><P>Isn't this starting to make more sense? Let's have anexample:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define DEST_IP "10.12.110.57" #define DEST_PORT 23 main() { int sockfd; struct sockaddr_in dest_addr; // will hold the destination addr sockfd = socket(AF_INET, SOCK_STREAM, 0); // do some error checking! dest_addr.sin_family = AF_INET; // host byte order dest_addr.sin_port = htons(DEST_PORT); // short, network byte order dest_addr.sin_addr.s_addr = inet_addr(DEST_IP); memset(&(dest_addr.sin_zero), '\0', 8); // zero the rest of the struct // don't forget to error check the connect()! connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); . . . </PRE></TD></TR></TABLE><P>Again, be sure to check the return value from<TTCLASS="function">connect()</TT>--it'll return <TTCLASS="constant">-1</TT> onerror and set the variable <TTCLASS="parameter"><I>errno</I></TT>.</P><P>Also, notice that we didn't call <TTCLASS="function">bind()</TT>.Basically, we don't care about our local port number; we only care wherewe're going (the remote port). The kernel will choose a local port forus, and the site we connect to will automatically get this informationfrom us. No worries.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="listen">4.4. <TTCLASS="function">listen()</TT>--Will somebody please callme?</A></H2><P>Ok, time for a change of pace. What if you don't want to connect
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -