?? syscalls.html
字號:
to a remote host. Say, just for kicks, that you want to wait forincoming connections and handle them in some way. The process is twostep: first you <TTCLASS="function">listen()</TT>, then you<TTCLASS="function">accept()</TT> (see below.)</P><P>The listen call is fairly simple, but requires a bit ofexplanation:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int listen(int sockfd, int backlog); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is the usual socket file descriptorfrom the <TTCLASS="function">socket()</TT> system call.<TTCLASS="parameter"><I>backlog</I></TT> is the number of connections allowed onthe incoming queue. What does that mean? Well, incoming connectionsare going to wait in this queue until you <TTCLASS="function">accept()</TT>them (see below) and this is the limit on how many can queue up. Mostsystems silently limit this number to about 20; you can probably getaway with setting it to <TTCLASS="constant">5</TT> or<TTCLASS="constant">10</TT>.</P><P>Again, as per usual, <TTCLASS="function">listen()</TT> returns<TTCLASS="constant">-1</TT> and sets <TTCLASS="parameter"><I>errno</I></TT> onerror.</P><P>Well, as you can probably imagine, we need to call<TTCLASS="function">bind()</TT> before we call <TTCLASS="function">listen()</TT>or the kernel will have us listening on a random port. Bleah! So ifyou're going to be listening for incoming connections, the sequence ofsystem calls you'll make is:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> socket(); bind(); listen(); /* accept() goes here */ </PRE></TD></TR></TABLE><P>I'll just leave that in the place of sample code, since it'sfairly self-explanatory. (The code in the <TTCLASS="function">accept()</TT>section, below, is more complete.) The really tricky part of this wholesha-bang is the call to <TTCLASS="function">accept()</TT>.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="accept">4.5. <TTCLASS="function">accept()</TT>--"Thank you for calling port3490."</A></H2><P>Get ready--the <TTCLASS="function">accept()</TT> call is kinda weird!What's going to happen is this: someone far far away will try to<TTCLASS="function">connect()</TT> to your machine on a port that you are<TTCLASS="function">listen()</TT>ing on. Their connection will be queuedup waiting to be <TTCLASS="function">accept()</TT>ed. You call<TTCLASS="function">accept()</TT> and you tell it to get the pendingconnection. It'll return to you a <EM>brand new socket filedescriptor</EM> to use for this single connection! That's right,suddenly you have <EM>two socket file descriptors</EM> forthe price of one! The original one is still listening on your port andthe newly created one is finally ready to <TTCLASS="function">send()</TT>and <TTCLASS="function">recv()</TT>. We're there! </P><P>The call is as follows:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> #include <sys/socket.h> int accept(int sockfd, void *addr, int *addrlen); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is the<TTCLASS="function">listen()</TT>ing socket descriptor. Easy enough.<TTCLASS="parameter"><I>addr</I></TT> will usually be a pointer to a local<TTCLASS="type">struct sockaddr_in</TT>. This is where the information aboutthe incoming connection will go (and with it you can determine whichhost is calling you from which port). <TTCLASS="parameter"><I>addrlen</I></TT> isa local integer variable that should be set to<TTCLASS="computeroutput">sizeof(struct sockaddr_in)</TT> before itsaddress is passed to <TTCLASS="function">accept()</TT>. Accept will not putmore than that many bytes into <TTCLASS="parameter"><I>addr</I></TT>. If it putsfewer in, it'll change the value of <TTCLASS="parameter"><I>addrlen</I></TT> toreflect that.</P><P>Guess what? <TTCLASS="function">accept()</TT> returns<TTCLASS="constant">-1</TT> and sets <TTCLASS="parameter"><I>errno</I></TT> if anerror occurs. Betcha didn't figure that.</P><P>Like before, this is a bunch to absorb in one chunk, so here's asample code fragment for your perusal:</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 // the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold main() { int sockfd, new_fd; // listen on sock_fd, new connection on new_fd struct sockaddr_in my_addr; // my address information struct sockaddr_in their_addr; // connector's address information int sin_size; 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 = INADDR_ANY; // auto-fill with my IP memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct // don't forget your error checking for these calls: bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); listen(sockfd, BACKLOG); sin_size = sizeof(struct sockaddr_in); new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); . . . </PRE></TD></TR></TABLE><P>Again, note that we will use the socket descriptor<TTCLASS="parameter"><I>new_fd</I></TT> for all <TTCLASS="function">send()</TT> and<TTCLASS="function">recv()</TT> calls. If you're only getting one singleconnection ever, you can <TTCLASS="function">close()</TT> the listening<TTCLASS="parameter"><I>sockfd</I></TT> in order to prevent more incomingconnections on the same port, if you so desire.</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="sendrecv">4.6. <TTCLASS="function">send()</TT> and <TTCLASS="function">recv()</TT>--Talkto me, baby!</A></H2><P>These two functions are for communicating over stream sockets orconnected datagram sockets. If you want to use regular unconnecteddatagram sockets, you'll need to see the section on <AHREF="syscalls.html#sendtorecv"><TTCLASS="function">sendto()</TT> and<TTCLASS="function">recvfrom()</TT></A>, below.</P><P>The <TTCLASS="function">send()</TT> call:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int send(int sockfd, const void *msg, int len, int flags); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is the socket descriptor you want tosend data to (whether it's the one returned by<TTCLASS="function">socket()</TT> or the one you got with<TTCLASS="function">accept()</TT>.) <TTCLASS="parameter"><I>msg</I></TT> is a pointerto the data you want to send, and <TTCLASS="parameter"><I>len</I></TT> is thelength of that data in bytes. Just set <TTCLASS="parameter"><I>flags</I></TT> to<TTCLASS="constant">0</TT>. (See the <TTCLASS="function">send()</TT> man pagefor more information concerning flags.)</P><P>Some sample code might be:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> char *msg = "Beej was here!"; int len, bytes_sent; . . len = strlen(msg); bytes_sent = send(sockfd, msg, len, 0); . . . </PRE></TD></TR></TABLE><P><TTCLASS="function">send()</TT> returns the number of bytes actuallysent out--<EM>this might be less than the number you told it tosend!</EM> See, sometimes you tell it to send a whole gob of dataand it just can't handle it. It'll fire off as much of the data as itcan, and trust you to send the rest later. Remember, if the valuereturned by <TTCLASS="function">send()</TT> doesn't match the value in<TTCLASS="parameter"><I>len</I></TT>, it's up to you to send the rest of thestring. The good news is this: if the packet is small (less than 1K orso) it will <EM>probably</EM> manage to send the whole thingall in one go. Again, <TTCLASS="constant">-1</TT> is returned on error, and<TTCLASS="parameter"><I>errno</I></TT> is set to the error number.</P><P>The <TTCLASS="function">recv()</TT> call is similar in manyrespects:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int recv(int sockfd, void *buf, int len, unsigned int flags); </PRE></TD></TR></TABLE><P><TTCLASS="parameter"><I>sockfd</I></TT> is the socket descriptor to readfrom, <TTCLASS="parameter"><I>buf</I></TT> is the buffer to read the informationinto, <TTCLASS="parameter"><I>len</I></TT> is the maximum length of the buffer,and <TTCLASS="parameter"><I>flags</I></TT> can again be set to<TTCLASS="constant">0</TT>. (See the <TTCLASS="function">recv()</TT> man pagefor flag information.)</P><P><TTCLASS="function">recv()</TT> returns the number of bytes actuallyread into the buffer, or <TTCLASS="constant">-1</TT> on error (with<TTCLASS="parameter"><I>errno</I></TT> set, accordingly.)</P><P>Wait! <TTCLASS="function">recv()</TT> can return<TTCLASS="constant">0</TT>. This can mean only one thing: the remote sidehas closed the connection on you! A return value of<TTCLASS="constant">0</TT> is <TTCLASS="function">recv()</TT>'s way of lettingyou know this has occurred.</P><P>There, that was easy, wasn't it? You can now pass data back andforth on stream sockets! Whee! You're a Unix NetworkProgrammer!</P></DIV><DIVCLASS="sect2"><H2CLASS="sect2"><ANAME="sendtorecv">4.7. <TTCLASS="function">sendto()</TT> and<TTCLASS="function">recvfrom()</TT>--Talk to me, DGRAM-style</A></H2><P>"This is all fine and dandy," I hear you saying, "but where doesthis leave me with unconnected datagram sockets?" No problemo, amigo.We have just the thing.</P><P>Since datagram sockets aren't connected to a remote host, guess whichpiece of information we need to give before we send a packet? That'sright! The destination address! Here's the scoop:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen); </PRE></TD></TR></TABLE><P>As you can see, this call is basically the same as the call to<TTCLASS="function">send()</TT> with the addition of two other pieces ofinformation. <TTCLASS="parameter"><I>to</I></TT> is a pointer to a <TTCLASS="type">structsockaddr</TT> (which you'll probably have as a <TTCLASS="type">structsockaddr_in</TT> and cast it at the last minute) which contains thedestination IP address and port. <TTCLASS="parameter"><I>tolen</I></TT> cansimply be set to <TTCLASS="computeroutput">sizeof(structsockaddr)</TT>.</P><P>Just like with <TTCLASS="function">send()</TT>,<TTCLASS="function">sendto()</TT> returns the number of bytes actually sent(which, again, might be less than the number of bytes you told it tosend!), or <TTCLASS="constant">-1</TT> on error.</P><P>Equally similar are <TTCLASS="function">recv()</TT> and<TTCLASS="function">recvfrom()</TT>. The synopsis of<TTCLASS="function">recvfrom()</TT> is:</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="programlisting"> int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen); </PRE></TD></TR></TABLE><P>Again, this is just like <TTCLASS="function">recv()</TT> with theaddition of a couple fields. <TTCLASS="parameter"><I>from</I></TT> is a pointerto a local <TTCLASS="type">struct sockaddr</TT> that will be filled with the IPaddress and port of the originating machine.<TTCLASS="parameter"><I>fromlen</I></TT> is a pointer to a local <TTCLASS="type">int</TT>that should be initialized to <TTCLASS="type">sizeof(struct sockaddr)</TT>.When the function returns, <TTCLASS="parameter"><I>fromlen</I></TT> will containthe length of the address actually stored in<TTCLASS="parameter"><I>from</I></TT>.</P><P><TTCLASS="function">recvfrom()</TT> returns the number of bytesreceived, or <TTCLASS="constant">-1</TT> on error (with<TTCLASS="parameter"><I>errno</I></TT> set accordingly.)</P><P>Remember, if you <TTCLASS="function">connect()</TT> a datagram socket,you can then simply use <TTCLASS="function">send()</TT> and<TTCLASS="function">recv()</TT> for all your transactions. The socketitself is still a datagram socket and the packets still use UDP, but thesocket interface will automatically add the destination and sourceinformation for you.</P
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -