client.pl 0100644 0002257 0000215 00000001042 06664312305 0012775 0 ustar 00cho diglib 0000040 0000073 #! /usr/bin/perl -w
# client1.pl - a simple client
use strict;
use Socket;
# initialize host and port
my $host = shift || 'server.onsight.com';
my $port = shift || 7890;
my $proto = getprotobyname('tcp');
# get the port address
my $iaddr = inet_aton($host);
my $paddr = sockaddr_in($port, $iaddr);
# create the socket, connect to the port
socket(SOCKET, PF_INET, SOCK_STREAM, $proto)
or die "socket: $!";
connect(SOCKET, $paddr) or die "connect: $!";
while (
Beej's Guide to Network Programming
Using Internet Sockets
Version 1.5.4 (17-May-1998)
[http://www.ecst.csuchico.edu/~beej/guide/net]
Intro
Hey! Socket programming got you down? Is this stuff just a little too
difficult to figure out from the man pages? You want to do
cool Internet programming, but you don't have time to wade through a gob
of structs trying to figure out if you have to call
bind() before you connect(), etc., etc.
Well, guess what! I've already done this nasty business, and I'm dying to share the information with everyone! You've come to the right place. This document should give the average competent C programmer the edge s/he needs to get a grip on this networking noise.
Hopefully, though, it'll be just enough for those man pages to start making sense... :-)
What?
Ok--you may have heard some Unix hacker state, "Jeez, everything in Unix is a file!" What that person may have been talking about is the fact that when Unix programs do any sort of I/O, they do it by reading or writing to a file descriptor. A file descriptor is simply an integer associated with an open file. But (and here's the catch), that file can be a network connection, a FIFO, a pipe, a terminal, a real on-the-disk file, or just about anything else. Everything in Unix is a file! So when you want to communicate with another program over the Internet you're gonna do it through a file descriptor, you'd better believe it.
"Where do I get this file descriptor for network communication, Mr. Smarty-Pants?" is probably the last question on your mind right now, but I'm going to answer it anyway: You make a call to the socket() system routine. It returns the socket descriptor, and you communicate through it using the specialized send() and recv() ("man send", "man recv") socket calls.
"But, hey!" you might be exclaiming right about now. "If it's a file descriptor, why in the hell can't I just use the normal read() and write() calls to communicate through the socket?" The short answer is, "You can!" The longer answer is, "You can, but send() and recv() offer much greater control over your data transmission."
What next? How about this: there are all kinds of sockets. There are DARPA Internet addresses (Internet Sockets), path names on a local node (Unix Sockets), CCITT X.25 addresses (X.25 Sockets that you can safely ignore), and probably many others depending on which Unix flavor you run. This document deals only with the first: Internet Sockets.
All right, already. What are the two types? One is "Stream Sockets"; the other is "Datagram Sockets", which may hereafter be referred to as "SOCK_STREAM" and "SOCK_DGRAM", respectively. Datagram sockets are sometimes called "connectionless sockets" (though they can be connect()'d if you really want. See connect(), below.
Stream sockets are reliable two-way connected communication streams. If you output two items into the socket in the order "1, 2", they will arrive in the order "1, 2" at the opposite end. They will also be error free. Any errors you do encounter are figments of your own deranged mind, and are not to be discussed here.
What uses stream sockets? Well, you may have heard of the telnet application, yes? It uses stream sockets. All the characters you type need to arrive in the same order you type them, right? Also, WWW browsers use the HTTP protocol which uses stream sockets to get pages. Indeed, if you telnet to a WWW site on port 80, and type "GET pagename", it'll dump the HTML back at you!
How do stream sockets achieve this high level of data transmission quality? They use a protocol called "The Transmission Control Protocol", otherwise known as "TCP" (see RFC-793 for extremely detailed info on TCP.) TCP makes sure your data arrives sequentially and error-free. You may have heard "TCP" before as the better half of "TCP/IP" where "IP" stands for "Internet Protocol" (see RFC-791.) IP deals with Internet routing only.
Cool. What about Datagram sockets? Why are they called connectionless? What is the deal, here, anyway? Why are they unreliable? Well, here are some facts: if you send a datagram, it may arrive. It may arrive out of order. If it arrives, the data within the packet will be error-free.
Datagram sockets also use IP for routing, but they don't use TCP; they use the "User Datagram Protocol", or "UDP" (see RFC-768.)
Why are they connectionless? Well, basically, it's because you don't have to maintain an open connection as you do with stream sockets. You just build a packet, slap an IP header on it with destination information, and send it out. No connection needed. They are generally used for packet-by-packet transfers of information. Sample applications: tftp, bootp, etc.
"Enough!" you may scream. "How do these programs even work if datagrams might get lost?!" Well, my human friend, each has it's own protocol on top of UDP. For example, the tftp protocol says that for each packet that gets sent, the recipient has to send back a packet that says, "I got it!" (an "ACK" packet.) If the sender of the original packet gets no reply in, say, five seconds, he'll re-transmit the packet until he finally gets an ACK. This acknowledgment procedure is very important when implementing SOCK_DGRAM applications.
Hey, kids, it's time to learn about Data Encapsulation! This is very very important. It's so important that you might just learn about it if you take the networks course here at Chico State ;-). Basically, it says this: a packet is born, the packet is wrapped ("encapsulated") in a header (and maybe footer) by the first protocol (say, the TFTP protocol), then the whole thing (TFTP header included) is encapsulated again by the next protocol (say, UDP), then again by the next (IP), then again by the final protocol on the hardware (physical) layer (say, Ethernet).
When another computer receives the packet, the hardware strips the Ethernet header, the kernel strips the IP and UDP headers, the TFTP program strips the TFTP header, and it finally has the data.
Now I can finally talk about the infamous Layered Network Model. This Network Model describes a system of network functionality that has many advantages over other models. For instance, you can write sockets programs that are exactly the same without caring how the data is physically transmitted (serial, thin Ethernet, AUI, whatever) because programs on lower levels deal with it for you. The actual network hardware and topology is transparent to the socket programmer.
Without any further ado, I'll present the layers of the full-blown model. Remember this for network class exams:
The Physical Layer is the hardware (serial, Ethernet, etc.). The Application Layer is just about as far from the physical layer as you can imagine--it's the place where users interact with the network.
Now, this model is so general you could probably use it as an automobile repair guide if you really wanted to. A layered model more consistent with Unix might be:
At this point in time, you can probably see how these layers correspond to the encapsulation of the original data.
See how much work there is in building a simple packet? Jeez! And you have to type in the packet headers yourself using "cat"! Just kidding. All you have to do for stream sockets is send() the data out. All you have to do for datagram sockets is encapsulate the packet in the method of your choosing and sendto() it out. The kernel builds the Transport Layer and Internet Layer on for you and the hardware does the Network Access Layer. Ah, modern technology.
So ends our brief foray into network theory. Oh yes, I forgot to tell you everything I wanted to say about routing: nothing! That's right, I'm not going to talk about it at all. The router strips the packet to the IP header, consults its routing table, blah blah blah. Check out the IP RFC if you really really care. If you never learn about it, well, you'll live.
First the easy one: a socket descriptor. A socket descriptor is the following type:
intJust a regular int.
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 their numbers internally in Network Byte Order, some don't. When I say something has to be in NBO, you have to call a function (such as htons()) to change it from "Host Byte Order". If I don't say "NBO", then you must leave the value in Host Byte Order.
My First Struct(TM)--
struct sockaddr { unsigned short sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };sa_family can be a variety of things, but it'll be "AF_INET" for everything we do in this document. sa_data contains a destination address and port number for the socket. This is rather unwieldy.
To deal with
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 */ };This structure makes it easy to reference elements of the socket address. Note that sin_zero (which is included to pad the structure to the length of a
"But," you object, "how can the entire structure,
/* Internet address (a structure for historical reasons) */ struct in_addr { unsigned long s_addr; };Well, it used to be a union, but now those days seem to be gone. Good riddance. So if you have declared "ina" to be of type
All righty. There are two types that you can convert: short (two bytes) and long (four bytes). These functions work for the unsigned variations as well. Say you want to convert a short from Host Byte Order to Network Byte Order. Start with "h" for "host", follow it with "to", then "n" for "network", and "s" for "short": h-to-n-s, or htons() (read: "Host to Network Short").
It's almost too easy...
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 stolh() ("Short to Long Host") function--not at this party, anyway. But there are:
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 char?" Then you might think, "Uh, never mind." You might also think that since your 68000 machine already uses network byte order, you don't have to call htonl() on your IP addresses. You would be right, BUT if you try to port to a machine that has reverse network byte order, your program will fail. Be portable! This is a Unix world! Remember: put your bytes in Network Order before you put them on the network.
A final point: why do sin_addr and sin_port need to be
in Network Byte Order in a
First, let's say you have a
ina.sin_addr.s_addr = inet_addr("132.241.5.10");Notice that inet_addr() returns the address in Network Byte Order already--you don't have to call htonl(). Swell!
Now, the above code snippet isn't very robust because there is no error checking. See, inet_addr() returns -1 on error. Remember binary numbers? (unsigned)-1 just happens to correspond to the IP address 255.255.255.255! That's the broadcast address! Wrongo. Remember to do your error checking properly.
All right, now you can convert string IP addresses to longs.
What about the other way around? What if you have a
printf("%s",inet_ntoa(ina.sin_addr));That will print the IP address. Note that inet_ntoa() takes a
char *a1, *a2; . . a1 = inet_ntoa(ina1.sin_addr); /* this is 198.92.129.1 */ a2 = inet_ntoa(ina2.sin_addr); /* this is 132.241.5.10 */ printf("address 1: %s\n",a1); printf("address 2: %s\n",a2);will print:
address 1: 132.241.5.10 address 2: 132.241.5.10If you need to save the address, strcpy() it to your own character array.
That's all on this topic for now. Later, you'll learn to convert a string like "whitehouse.gov" into its corresponding IP address (see DNS, below.)
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);But what are these arguments? First, domain should be set to "AF_INET", just like in the
socket() simply returns to you a socket descriptor that you can use in later system calls, or -1 on error. The global variable errno is set to the error's value (see the perror() man page.)
Here is the synopsis for the bind() system call:
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen);sockfd is the socket file descriptor returned by socket(). my_addr is a pointer to a
Whew. That's a bit to absorb in one chunk. Let's have an example:
#include <string.h> #include <sys/types.h> #include <sys/socket.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("132.241.5.10"); bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ /* don't forget your error checking for bind(): */ bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); . . .There are a few things to notice here. my_addr.sin_port is in Network Byte Order. So is my_addr.sin_addr.s_addr. Another thing to watch out for is that the header files might differ from system to system. To be sure, you should check your local man pages.
Lastly, on the topic of bind(), I should mention that some of the process of getting your own IP address and/or port can can be automated:
my_addr.sin_port = 0; /* choose an unused port at random */ my_addr.sin_addr.s_addr = INADDR_ANY; /* use my IP address */See, by setting my_addr.sin_port to zero, you are telling bind() to choose the port for you. Likewise, by setting my_addr.sin_addr.s_addr to INADDR_ANY, you are telling it to automatically fill in the IP address of the machine the process is running on.
If you are into noticing little things, you might have seen that I didn't put INADDR_ANY into Network Byte Order! Naughty me. However, I have inside info: INADDR_ANY is really zero! Zero still has zero on bits even if you rearrange the bytes. However, purists will point out that there could be a parallel dimension where INADDR_ANY is, say, 12 and that my code won't work there. That's ok with me:
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 */Now we're so portable you probably wouldn't believe it. I just wanted to point that out, since most of the code you come across won't bother running INADDR_ANY through htonl().
bind() also returns -1 on error and sets errno to the error's value.
Another thing to watch out for when calling bind(): don't go underboard with your port numbers. All ports below 1024 are RESERVED! You can have any port number above that, right up to 65535 (provided they aren't already being used by another program.)
One small extra final note about bind(): there are times when you won't absolutely have to call it. If you are connect()'ing to a remote machine and you don't care what your local port is (as is the case with telnet), you can simply call connect(), it'll check to see if the socket is unbound, and will bind() it to an unused local port.
Lucky for you, program, you're now perusing the section on connect()--how to connect to a remote host. You read furiously onward, not wanting to disappoint your user...
The connect() call is as follows:
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);sockfd is our friendly neighborhood socket file descriptor, as returned by the socket() call, serv_addr is a
Isn't this starting to make more sense? Let's have an example:
#include <string.h> #include <sys/types.h> #include <sys/socket.h> #define DEST_IP "132.241.5.10" #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); bzero(&(dest_addr.sin_zero), 8); /* zero the rest of the struct */ /* don't forget to error check the connect()! */ connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)); . . .
Again, be sure to check the return value from connect()--it'll return -1 on error and set the variable errno.
Also, notice that we didn't call bind(). Basically, we don't care about our local port number; we only care where we're going. The kernel will choose a local port for us, and the site we connect to will automatically get this information from us. No worries.
The listen call is fairly simple, but requires a bit of explanation:
int listen(int sockfd, int backlog);sockfd is the usual socket file descriptor from the socket() system call. backlog is the number of connections allowed on the incoming queue. What does that mean? Well, incoming connections are going to wait in this queue until you accept() them (see below) and this is the limit on how many can queue up. Most systems silently limit this number to about 20; you can probably get away with setting it to 5 or 10.
Again, as per usual, listen() returns -1 and sets errno on error.
Well, as you can probably imagine, we need to call bind() before we call listen() or the kernel will have us listening on a random port. Bleah! So if you're going to be listening for incoming connections, the sequence of system calls you'll make is:
socket(); bind(); listen(); /* accept() goes here */I'll just leave that in the place of sample code, since it's fairly self-explanatory. (The code in the accept() section, below, is more complete.) The really tricky part of this whole sha-bang is the call to accept().
The call is as follows:
#include <sys/socket.h> int accept(int sockfd, void *addr, int *addrlen);sockfd is the listen()'ing socket descriptor. Easy enough. addr will usually be a pointer to a local struct sockaddr_in. This is where the information about the incoming connection will go (and you can determine which host is calling you from which port). addrlen is a local integer variable that should be set to
Guess what? accept() returns -1 and sets errno if an error occurs. Betcha didn't figure that.
Like before, this is a bunch to absorb in one chunk, so here's a sample code fragment for your perusal:
#include <string.h> #include <sys/types.h> #include <sys/socket.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 */ bzero(&(my_addr.sin_zero), 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, &their_addr, &sin_size); . . .Again, note that we will use the socket descriptor new_fd for all send() and recv() calls. If you're only getting one single connection ever, you can close() the original sockfd in order to prevent more incoming connections on the same port, if you so desire.
The send() call:
int send(int sockfd, const void *msg, int len, int flags);sockfd is the socket descriptor you want to send data to (whether it's the one returned by socket() or the one you got with accept().) msg is a pointer to the data you want to send, and len is the length of that data in bytes. Just set flags to 0. (See the send() man page for more information concerning flags.)
Some sample code might be:
char *msg = "Beej was here!"; int len, bytes_sent; . . len = strlen(msg); bytes_sent = send(sockfd, msg, len, 0); . . .send() returns the number of bytes actually sent out--this might be less than the number you told it to send! See, sometimes you tell it to send a whole gob of data and it just can't handle it. It'll fire off as much of the data as it can, and trust you to send the rest later. Remember, if the value returned by send() doesn't match doesn't match the value in len, it's up to you to send the rest of the string. The good news is this: if the packet is small (less than 1K or so) it will probably manage to send the whole thing all in one go. Again, -1 is returned on error, and errno is set to the error number.
The recv() call is similar in many respects:
int recv(int sockfd, void *buf, int len, unsigned int flags);sockfd is the socket descriptor to read from, buf is the buffer to read the information into, len is the maximum length of the buffer, and flags can again be set to 0. (See the recv() man page for flag information.)
recv() returns the number of bytes actually read into the buffer, or -1 on error (with errno set, accordingly.)
There, that was easy, wasn't it? You can now pass data back and forth on stream sockets! Whee! You're a Unix Network Programmer!
Since datagram sockets aren't connected to a remote host, guess which piece of information we need to give before we send a packet? That's right! The destination address! Here's the scoop:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);As you can see, this call is basically the same as the call to send() with the addition of two other pieces of information. to is a pointer to a
Just like with send(), sendto() returns the number of bytes actually sent (which, again, might be less than the number of bytes you told it to send!), or -1 on error.
Equally similar are recv() and recvfrom(). The synopsis of recvfrom() is:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);Again, this is just like recv() with the addition of a couple fields. from is a pointer to a local
recvfrom() returns the number of bytes received, or -1 on error (with errno set accordingly.)
Remember, if you connect() a datagram socket, you can then simply use send() and recv() for all your transactions. The socket itself is still a datagram socket and the packets still use UDP, but the socket interface will automatically add the destination and source information for you.
close(sockfd);This will prevent any more reads and writes to the socket. Anyone attempting to read or write the socket on the remote end will receive an error.
Just in case you want a little more control over how the socket closes, you can use the shutdown() function. It allows you to cut off communication in a certain direction, or both ways (just like close() does.) Synopsis:
int shutdown(int sockfd, int how);sockfd is the socket file descriptor you want to shutdown, and how is one of the following:
shutdown() returns 0 on success, and -1 on error (with errno set accordingly.)
If you deign to use shutdown() on unconnected datagram sockets, it will simply make the socket unavailable for further send() and recv() calls (remember that you can use these if you connect() your datagram socket.)
Nothing to it.
It's so easy, I almost didn't give it it's own section. But here it is anyway.
The function getpeername() will tell you who is at the other end of a connected stream socket. The synopsis:
#include <sys/socket.h> int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);sockfd is the descriptor of the connected stream socket, addr is a pointer to a
The function returns -1 on error and sets errno accordingly.
Once you have their address, you can use inet_ntoa() or gethostbyaddr() to print or get more information. No, you can't get their login name. (Ok, ok. If the other computer is running an ident daemon, this is possible. This, however, is beyond the scope of this document. Check out RFC-1413 for more info.)
What could be more fun? I could think of a few things, but they don't pertain to socket programming. Anyway, here's the breakdown:
#include <unistd.h> int gethostname(char *hostname, size_t size);
The arguments are simple: hostname is a pointer to an array of chars that will contain the hostname upon the function's return, and size is the length in bytes of the hostname array.
The function returns 0 on successful completion, and -1 on error, setting errno as usual.
$ telnet whitehouse.govtelnet can find out that it needs to connect() to "198.137.240.100".
But how does it work? You'll be using the function gethostbyname():
#include <netdb.h> struct hostent *gethostbyname(const char *name);As you see, it returns a pointer to a
struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; #define h_addr h_addr_list[0]And here are the descriptions of the fields in the
gethostbyname() returns a pointer to the filled
But how is it used? Sometimes (as we find from reading computer manuals), just spewing the information at the reader is not enough. This function is certainly easier to use than it looks.
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> int main(int argc, char *argv[]) { struct hostent *h; if (argc != 2) { /* error check the command line */ fprintf(stderr,"usage: getip address\n"); exit(1); } if ((h=gethostbyname(argv[1])) == NULL) { /* get the host info */ herror("gethostbyname"); exit(1); } printf("Host name : %s\n", h->h_name); printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr))); return 0; }With gethostbyname(), you can't use perror() to print error message (since errno is not used). Instead, call herror().
It's pretty straightforward. You simply pass the string that contains
the machine name ("whitehouse.gov") to gethostbyname(), and
then grab the information out of the returned
The only possible weirdness might be in the printing of the IP address,
above. h->h_addr is a
The exchange of information between client and server is summarized in Figure 2.
Note that the client-server pair can speak SOCK_STREAM, SOCK_DGRAM, or anything else (as long as they're speaking the same thing.) Some good examples of client-server pairs are telnet/telnetd, ftp/ftpd, or bootp/bootpd. Every time you use ftp, there's a remote program, ftpd, that serves you.
Often, there will only be one server on a machine, and that server will handle multiple clients using fork(). The basic routine is: server will wait for a connection, accept() it, and fork() a child process to handle it. This is what our sample server does in the next section.
$ telnet remotehostname 3490where remotehostname is the name of the machine you're running it on.
The server code: (Note: a trailing backslash on a line means that the line is continued on the next.)
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.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; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } 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 */ bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ == -1) { perror("bind"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } while(1) { /* main accept() loop */ sin_size = sizeof(struct sockaddr_in); if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, \ &sin_size)) == -1) { perror("accept"); continue; } printf("server: got connection from %s\n", \ inet_ntoa(their_addr.sin_addr)); if (!fork()) { /* this is the child process */ if (send(new_fd, "Hello, world!\n", 14, 0) == -1) perror("send"); close(new_fd); exit(0); } close(new_fd); /* parent doesn't need this */ while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ } }In case you're curious, I have the code in one big main() function for (I feel) syntactic clarity. Feel free to split it into smaller functions if it makes you feel better.
You can also get the string from this server by using the client listed in the next section.
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #define PORT 3490 /* the port client will be connecting to */ #define MAXDATASIZE 100 /* max number of bytes we can get at once */ int main(int argc, char *argv[]) { int sockfd, numbytes; char buf[MAXDATASIZE]; struct hostent *he; struct sockaddr_in their_addr; /* connector's address information */ if (argc != 2) { fprintf(stderr,"usage: client hostname\n"); exit(1); } if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ herror("gethostbyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } their_addr.sin_family = AF_INET; /* host byte order */ their_addr.sin_port = htons(PORT); /* short, network byte order */ their_addr.sin_addr = *((struct in_addr *)he->h_addr); bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */ if (connect(sockfd, (struct sockaddr *)&their_addr, \ sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { perror("recv"); exit(1); } buf[numbytes] = '\0'; printf("Received: %s",buf); close(sockfd); return 0; }
Notice that if you don't run the server before you run the client, connect() returns "Connection refused". Very useful.
listener sits on a machine waiting for an incoming packet on port 4950. talker sends a packet to that port, on the specified machine, that contains whatever the user enters on the command line.
Here is the source for listener.c:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #define MYPORT 4950 /* the port users will be connecting to */ #define MAXBUFLEN 100 main() { int sockfd; struct sockaddr_in my_addr; /* my address information */ struct sockaddr_in their_addr; /* connector's address information */ int addr_len, numbytes; char buf[MAXBUFLEN]; if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } 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 */ bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) \ == -1) { perror("bind"); exit(1); } addr_len = sizeof(struct sockaddr); if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN, 0, \ (struct sockaddr *)&their_addr, &addr_len)) == -1) { perror("recvfrom"); exit(1); } printf("got packet from %s\n",inet_ntoa(their_addr.sin_addr)); printf("packet is %d bytes long\n",numbytes); buf[numbytes] = '\0'; printf("packet contains \"%s\"\n",buf); close(sockfd); }Notice that in our call to socket() we're finally using SOCK_DGRAM. Also, note that there's no need to listen() or accept(). This is one of the perks of using unconnected datagram sockets!
Next comes the source for talker.c:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <sys/socket.h> #include <sys/wait.h> #define MYPORT 4950 /* the port users will be connecting to */ int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in their_addr; /* connector's address information */ struct hostent *he; int numbytes; if (argc != 3) { fprintf(stderr,"usage: talker hostname message\n"); exit(1); } if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */ herror("gethostbyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } their_addr.sin_family = AF_INET; /* host byte order */ their_addr.sin_port = htons(MYPORT); /* short, network byte order */ their_addr.sin_addr = *((struct in_addr *)he->h_addr); bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */ if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0, \ (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) == -1) { perror("sendto"); exit(1); } printf("sent %d bytes to %s\n",numbytes,inet_ntoa(their_addr.sin_addr)); close(sockfd); return 0; }And that's all there is to it! Run listener on some machine, then run talker on another. Watch them communicate! Fun G-rated excitement for the entire nuclear family!
Except for one more tiny detail that I've mentioned many times in the past: connected datagram sockets. I need to talk about this here, since we're in the datagram section of the document. Let's say that talker calls connect() and specifies the listener's address. From that point on, talker may only sent to and receive from the address specified by connect(). For this reason, you don't have to use sendto() and recvfrom(); you can simply use send() and recv().
Lots of functions block. accept() blocks. All the recv*() functions block. The reason they can do this is because they're allowed to. When you first create the socket descriptor with socket(), the kernel sets it to blocking. If you don't want a socket to be blocking, you have to make a call to fcntl():
#include <unistd.h> #include <fcntl.h> . . sockfd = socket(AF_INET, SOCK_STREAM, 0); fcntl(sockfd, F_SETFL, O_NONBLOCK); . .
By setting a socket to non-blocking, you can effectively "poll" the socket for information. If you try to read from a non-blocking socket and there's no data there, it's not allowed to block--it will return -1 and errno will be set to EWOULDBLOCK.
Generally speaking, however, this type of polling is a bad idea. If you put your program in a busy-wait looking for data on the socket, you'll suck up CPU time like it was going out of style. A more elegant solution for checking to see if there's data waiting to be read comes in the following section on select().
No problem, you say, just an accept() and a couple of recv()s. Not so fast, buster! What if you're blocking on an accept() call? How are you going to recv() data at the same time? "Use non-blocking sockets!" No way! You don't want to be a CPU hog. What, then?
select() gives you the power to monitor several sockets at the same time. It'll tell you which ones are ready for reading, which are ready for writing, and which sockets have raised exceptions, if you really want to know that.
Without any further ado, I'll offer the synopsis of select():
#include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
The function monitors "sets" of file descriptors; in particular readfds, writefds, and exceptfds. If you want to see if you can read from standard input and some socket descriptor, sockfd, just add the file descriptors 0 and sockfd to the set readfds. The parameter numfds should be set to the values of the highest file descriptor plus one. In this example, it should be set to sockfd+1, since it is assuredly higher than standard input (0).
When select() returns, readfds will be modified to reflect which of the file descriptors you selected is ready for reading. You can test them with the macro FD_ISSET(), below.
Before progressing much further, I'll talk about how to manipulate these sets. Each set is of the type fd_set. The following macros operate on this type:
Finally, what is this weirded out
The struct timeval has the follow fields:
struct timeval { int tv_sec; /* seconds */ int tv_usec; /* microseconds */ };Just set tv_sec to the number of seconds to wait, and set tv_usec to the number of microseconds to wait. Yes, that's microseconds, not milliseconds. There are 1,000 microseconds in a millisecond, and 1,000 milliseconds in a second. Thus, there are 1,000,000 microseconds in a second. Why is it "usec"? The "u" is supposed to look like the Greek letter Mu that we use for "micro". Also, when the function returns, timeout might be updated to show the time still remaining. This depends on what flavor of Unix you're running.
Yay! We have a microsecond resolution timer! Well, don't count on
it. Standard Unix timeslice is 100 milliseconds, so you'll probably
have to wait at least that long, no matter how small you set your
Other things of interest: If you set the fields in your
The following code snippet waits 2.5 seconds for something to appear on standard input:
#include <sys/time.h> #include <sys/types.h> #include <unistd.h> #define STDIN 0 /* file descriptor for standard input */ main() { struct timeval tv; fd_set readfds; tv.tv_sec = 2; tv.tv_usec = 500000; FD_ZERO(&readfds); FD_SET(STDIN, &readfds); /* don't care about writefds and exceptfds: */ select(STDIN+1, &readfds, NULL, NULL, &tv); if (FD_ISSET(STDIN, &readfds)) printf("A key was pressed!\n"); else printf("Timed out.\n"); }If you're on a line buffered terminal, the key you hit should be RETURN or it will time out anyway.
Now, some of you might think this is a great way to wait for data on a datagram socket--and you are right: it might be. Some Unices can use select in this manner, and some can't. You should see what your local man page says on the matter if you want to attempt it.
One final note of interest about select(): if you have a socket that is listen()'ing, you can check to see if there is a new connection by putting that socket's file descriptor in the readfds set.
And that, my friends, is a quick overview of the almighty select() function.
Try the following man pages, for starters:
So, if there are, that's tough for you. I'm sorry if any inaccuracies contained herein have caused you any grief, but you just can't hold me accountable. See, I don't stand behind a single word of this document, legally speaking. This is my warning to you: the whole thing could be a load of crap.
But it's probably not. After all, I've spent many many hours messing with this stuff, and implemented several TCP/IP network utilities for Windows (including Telnet) as summer work. I'm not the sockets god; I'm just some guy.
By the way, if anyone has any constructive (or destructive) criticism about this document, please send mail to beej@ecst.csuchico.edu and I'll try to make an effort to set the record straight.
In case you're wondering why I did this, well, I did it for the money. Hah! No, really, I did it because a lot of people have asked me socket-related questions and when I tell them I've been thinking about putting together a socket page, they say, "cool!" Besides, I feel that all this hard-earned knowledge is going to waste if I can't share it with others. WWW just happens to be the perfect vehicle. I encourage others to provide similar information whenever possible.
Enough of this--back to coding! ;-)
#/usr/local/bin/perl # create a socket $proto = getprotobyname('tcp'); socket(SOCK, PF_INET, SOCK_STREAM, $proto); # connect to the server $iaddr = gethostbyname($name); $port = getservbyname('http', 'tcp'); $sin = sockaddr_in($port, $iaddr); connect(SOCK, $sin); # send request $request = "HEAD / HTTP/1.0\r\nUser-Agent: Poong\r\n\r\n"; syswrite SOCK, $request, length($request); # read result @_ =perl-sock-example.html~ 0100644 0002257 0000215 00000000624 06663140123 0015577 0 ustar 00cho diglib 0000040 0000073; print @_;
# create a socket $proto = getprotobyname('tcp'); socket(SOCK, PF_INET, SOCK_STREAM, $proto); # connect to the server $iaddr = gethostbyname($name); $port = getservbyname('http', 'tcp'); $sin = sockaddr_in($port, $iaddr); connect(SOCK, $sin); # send request $request = "HEAD / HTTP/1.0\r\nUser-Agent: Poong\r\n\r\n"; syswrite SOCK, $request, length($request); # read result @_ =server.pl 0100644 0002257 0000215 00000002140 06664312256 0013032 0 ustar 00cho diglib 0000040 0000073 !/usr/bin/perl -w # server1.pl - a simple server use strict; use Socket; # use port 7890 as default my $port = shift || 7890; my $proto = getprotobyname('tcp'); # create a socket, make it reusable socket(SERVER, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsock: $!"; # grab a port on this machine my $paddr = sockaddr_in($port, INADDR_ANY); # bind to a port, then listen bind(SERVER, $paddr) or die "bind: $!"; listen(SERVER, SOMAXCONN) or die "listen: $!"; print "SERVER started on port $port\n"; # for each connection... my $client_addr; while ($client_addr = accept(CLIENT, SERVER)) { # find out who connected my ($client_port, $client_ip) = sockaddr_in($client_addr); my $client_ipnum = inet_ntoa($client_ip); my $client_host = gethostbyaddr($client_ip, AF_INET); # tell who connected print "got a connection from: $client_host", " [$client_ipnum]\n"; # send them a message, close connection print CLIENT "Hello from the server: ", close CLIENT; } shell.html 0100644 0002257 0000215 00000044227 06606167275 0013205 0 ustar 00cho diglib 0000040 0000073;
find . -name file -printand you'd rather type a simple command, say
sfind fileCreate a shell script
% cd ~/bin % emacs sfind % page sfind find . -name $1 -print % chmod a+x sfind % rehash % cd /usr/local/bin % sfind tcsh ./shells/tcsh
%chmod a+x sfind
#!/bin/shFrom the man page for exec(2):
"On the first line of an interpreter script, following the "#!", is the name of a program which should be used to interpret the contents of the file. For instance, if the first line contains "#! /bin/sh", then the con- tents of the file are executed as a shell script."
You can get away without this, but you shouldn't. All good scripts state the interpretor explicitly. Long ago there was just one (the Bourne Shell) but these days there are many interpretors -- Csh, Ksh, Bash, and others.
PATH=/usr/ucb:/usr/bin:/bin; export PATHA PATH specification is recommended -- often times a script will fail for some people because they have a different or incomplete search path.
The Bourne Shell does not export environment variables to children unless explicitly instructed to do so by using the export command.
if [ $# -ne 3 ]; then echo 1>&2 Usage: $0 19 Oct 91 exit 127 fiThis script requires three arguments and gripes accordingly.
# is the year out of range for me? if [ $year -lt 1901 -o $year -gt 2099 ]; then echo 1>&2 Year \"$year\" out of range exit 127 fi etc... # All done, exit ok exit 0A non-zero exit status indicates an error condition of some sort while a zero exit status indicates things worked as expected.
On BSD systems there's been an attempt to categorize some of the more common exit status codes. See /usr/include/sysexits.h.
The conditional construct is:
if command; then command fiFor example,
if tty -s; then echo Enter text end with \^D fiYour code should be written with the expectation that others will use it. Making sure you return a meaningful exit status will help.
# is the year out of range for me? if [ $year -lt 1901 -o $year -gt 2099 ]; then echo 1>&2 Year \"$year\" out of my range exit 127 fi etc... # ok, you have the number of days since Jan 1, ... case `expr $days % 7` in 0) echo Mon;; 1) echo Tue;; etc...Error messages should appear on stderr not on stdout! Output should appear on stdout. As for input/output dialogue:
# give the fellow a chance to quit if tty -s ; then echo This will remove all files in $* since ... echo $n Ok to procede? $c; read ans case "$ans" in n*|N*) echo File purge abandoned; exit 0 ;; esac RM="rm -rfi" else RM="rm -rf" fiNote: this code behaves differently if there's a user to communicate with (ie. if the standard input is a tty rather than a pipe, or file, or etc. See tty(1)).
Substitute values for variable and perform task:
for variable in word ... do command doneFor example:
for i in `cat $LOGS` do mv $i $i.$TODAY cp /dev/null $i chmod 664 $i doneAlternatively you may see:
for variable in word ...; do command; done
Switch to statements depending on pattern match
case word in [ pattern [ | pattern ... ] ) command ;; ] ... esacFor example:
case "$year" in [0-9][0-9]) year=19${year} years=`expr $year - 1901` ;; [0-9][0-9][0-9][0-9]) years=`expr $year - 1901` ;; *) echo 1>&2 Year \"$year\" out of range ... exit 127 ;; esac
Test exit status of command and branch
if command then command [ else command ] fiFor example:
if [ $# -ne 3 ]; then echo 1>&2 Usage: $0 19 Oct 91 exit 127 fiAlternatively you may see:
if command; then command; [ else command; ] fi
Repeat task while command returns good exit status.
{while | until} command do command doneFor example:
# for each argument mentioned, purge that directory while [ $# -ge 1 ]; do _purge $1 shift doneAlternatively you may see:
while command; do command; done
Variables are sequences of letters, digits, or underscores beginning with a letter or underscore. To get the contents of a variable you must prepend the name with a $.
Numeric variables (eg. like $1, etc.) are positional vari- ables for argument communication.
Assign a value to a variable by variable=value. For example:
PATH=/usr/ucb:/usr/bin:/bin; export PATHor
TODAY=`(set \`date\`; echo $1)`
Variables are not exported to children unless explicitly marked.
# We MUST have a DISPLAY environment variable if [ "$DISPLAY" = "" ]; then if tty -s ; then echo "DISPLAY (`hostname`:0.0)? \c"; read DISPLAY fi if [ "$DISPLAY" = "" ]; then DISPLAY=`hostname`:0.0 fi export DISPLAY fiLikewise, for variables like the PRINTER which you want hon- ored by lpr(1). From a user's .profile:
PRINTER=PostScript; export PRINTERNote: that the Cshell exports all environment variables.
Use $variable (or, if necessary, ${variable}) to reference the value.
# Most user's have a /bin of their own if [ "$USER" != "root" ]; then PATH=$HOME/bin:$PATH else PATH=/etc:/usr/etc:$PATH fiThe braces are required for concatenation constructs.
$p_01The value of the variable "p_01".
${p}_01The value of the variable "p" with "_01" pasted onto the end.
${variable-word}If the variable has been set, use it's value, else use word.
POSTSCRIPT=${POSTSCRIPT-PostScript}; export POSTSCRIPT ${variable:-word}If the variable has been set and is not null, use it's value, else use word.
These are useful constructions for honoring the user envi- ronment. Ie. the user of the script can override variable assignments. Cf. programs like lpr(1) honor the PRINTER environment variable, you can do the same trick with your shell scripts.
${variable:?word}If variable is set use it's value, else print out word and exit. Useful for bailing out.
Command line arguments to shell scripts are positional vari- ables:
$0, $1, ...The command and arguments. With $0 the command and the rest the arguments.
$#The number of arguments.
$*, $@All the arguments as a blank separated string. Watch out for "$*" vs. "$@".
shiftShift the postional variables down one and decrement number of arguments.
set arg arg ...Set the positional variables to the argument list.
Command line parsing uses shift:
# parse argument list while [ $# -ge 1 ]; do case $1 in process arguments... esac shift doneA use of the set command:
# figure out what day it is TODAY=`(set \`date\`; echo $1)` cd $SPOOL for i in `cat $LOGS` do mv $i $i.$TODAY cp /dev/null $i chmod 664 $i done
$$Current process id. This is very useful for constructing temporary files.
tmp=/tmp/cal0$$ trap "rm -f $tmp /tmp/cal1$$ /tmp/cal2$$" trap exit 1 2 13 15 /usr/lib/calprog >$tmp $?The exit status of the last command.
$command # Run target file if no errors and ... if [ $? -eq 0 ] then etc... fi
Special characters to terminate words:
; & ( ) | ^ < > new-line space tabThese are for command sequences, background jobs, etc. To quote any of these use a backslash (\) or bracket with quote marks ("" or '').
Single Quotes
Within single quotes all characters are quoted -- including the backslash. The result is one word.
grep :${gid}: /etc/group | awk -F: '{print $1}'Double Quotes
Within double quotes you have variable subsitution (ie. the dollar sign is interpreted) but no file name generation (ie. * and ? are quoted). The result is one word.
if [ ! "${parent}" ]; then parent=${people}/${group}/${user} fiBack Quotes
Back quotes mean run the command and substitute the output.
if [ "`echo -n`" = "-n" ]; then n="" c="\c" else n="-n" c="" fiand
TODAY=`(set \`date\`; echo $1)`
Functions are a powerful feature that aren't used often enough. Syntax is
name () { commands }For example:
# Purge a directory _purge() { # there had better be a directory if [ ! -d $1 ]; then echo $1: No such directory 1>&2 return fi etc... }Within a function the positional parmeters $0, $1, etc. are the arguments to the function (not the arguments to the script).
Within a function use return instead of exit.
Functions are good for encapsulations. You can pipe, redi- rect input, etc. to functions. For example:
# deal with a file, add people one at a time do_file() { while parse_one etc... } etc... # take standard input (or a specified file) and do it. if [ "$1" != "" ]; then cat $1 | do_file else do_file fi
You can execute shell scripts from within shell scripts. A couple of choices:
sh command
This runs the shell script as a separate shell. For example, on Sun machines in /etc/rc:
sh /etc/rc.local. command
This runs the shell script from within the current shell script. For example:
# Read in configuration information . /etc/hostconfigWhat are the virtues of each? What's the difference? The second form is useful for configuration files where environment variable are set for the script. For example:
for HOST in $HOSTS; do # is there a config file for this host? if [ -r ${BACKUPHOME}/${HOST} ]; then . ${BACKUPHOME}/${HOST} fi etc...Using configuration files in this manner makes it possible to write scripts that are automatically tailored for differ- ent situations.
The most powerful command is test(1).
if test expression; then etc...and (note the matching bracket argument)
if [ expression ]; then etc...On System V machines this is a builtin (check out the com- mand /bin/test).
On BSD systems (like the Suns) compare the command /usr/bin/test with /usr/bin/[.
Useful expressions are:
test { -w, -r, -x, -s, ... } filenameis file writeable, readable, executeable, empty, etc?
test n1 { -eq, -ne, -gt, ... } n2are numbers equal, not equal, greater than, etc.?
test s1 { =, != } s2Are strings the same or different?
test cond1 { -o, -a } cond2Binary or; binary and; use ! for unary negation.
For example
if [ $year -lt 1901 -o $year -gt 2099 ]; then echo 1>&2 Year \"$year\" out of range exit 127 fiLearn this command inside out! It does a lot for you.
The test command provides limited string matching tests. A more powerful trick is to match strings with the case switch.
# parse argument list while [ $# -ge 1 ]; do case $1 in -c*) rate=`echo $1 | cut -c3-`;; -c) shift; rate=$1 ;; -p*) prefix=`echo $1 | cut -c3-`;; -p) shift; prefix=$1 ;; -*) echo $Usage; exit 1 ;; *) disks=$*; break ;; esac shift doneOf course getopt would work much better.
On BSD systems to get a prompt you'd say:
echo -n Ok to procede?; read ansOn SysV systems you'd say:
echo Ok to procede? \c; read ansIn an effort to produce portable code we've been using:
# figure out what kind of echo to use if [ "`echo -n`" = "-n" ]; then n=""; c="\c" else n="-n"; c="" fi etc... echo $n Ok to procede? $c; read ans
The Unix tradition is that programs should execute as qui- etly as possible. Especially for pipelines, cron jobs, etc.
User prompts aren't required if there's no user.
# If there's a person out there, prod him a bit. if tty -s; then echo Enter text end with \^D fiThe tradition also extends to output.
# If the output is to a terminal, be verbose if tty -s <&1; then verbose=true else verbose=false fiBeware: just because stdin is a tty that doesn't mean that stdout is too. User prompts should be directed to the user terminal.
# If there's a person out there, prod him a bit. if tty -s; then echo Enter text end with \^D >&0 fiHave you ever had a program stop waiting for keyboard input when the output is directed elsewhere?
We're familiar with redirecting input. For example:
# take standard input (or a specified file) and do it. if [ "$1" != "" ]; then cat $1 | do_file else do_file fialternatively, redirection from a file:
# take standard input (or a specified file) and do it. if [ "$1" != "" ]; then do_file < $1 else do_file fiYou can also construct files on the fly.
rmail bsmtp <Note: that variables are expanded in the input.rcpt to: data from: <$1@newshost.uwo.ca> to: Subject: Signon $2 subscribe $2 Usenet Feeder at UWO . quit EOF
One of the more common things you'll need to do is parse strings. Some tricks
TIME=`date | cut -c12-19` TIME=`date | sed 's/.* .* .* \(.*\) .* .*/\1/'` TIME=`date | awk '{print $4}'` TIME=`set \`date\`; echo $4` TIME=`date | (read u v w x y z; echo $x)`With some care, redefining the input field separators can help.
#!/bin/sh # convert IP number to in-addr.arpa name name() { set `IFS=".";echo $1` echo $4.$3.$2.$1.in-addr.arpa } if [ $# -ne 1 ]; then echo 1>&2 Usage: bynum IP-address exit 127 fi add=`name $1` nslookup < < EOF | grep "$add" | sed 's/.*= //' set type=any $add EOF
The shell has a number of flags that make debugging easier:
sh -n command
Read the shell script but don't execute the commands. IE. check syntax.
sh -x command
Display commands and arguments as they're executed. In a lot of my shell scripts you'll see
# Uncomment the next line for testing # set -x
PRE> The value of the variable "p_01".
${p}_01The value of the variable "p" with "_01" pasted onto the end.
${variable-word}If the variable has been set, use it's value, else use word.
POSTSCRIPT=${POSTSCRIPT-PostScript}; export POSTSCRIPT ${variable:-word}If the variable unix-socket-faq-1.html 0100644 0002257 0000215 00000014342 06633403311 0015225 0 ustar 00cho diglib 0000040 0000073
Changed on May 21/1998
This FAQ is maintained by Vic Metcalfe ( vic@acm.org), with lots of assistance from Andrew Gierth ( andrew@erlenstar.demon.co.uk). I am depending on the true wizards to fill in the details, and correct my (no doubt) plentiful mistakes. The code examples in this FAQ are written to be easy to follow and understand. It is up to the reader to make them as efficient as required. I started this faq because after reading comp.unix.programmer for a short time, it became evident that a FAQ for sockets was needed.
The FAQ is available at the following locations:
news.answers, comp.answers, comp.unix.answers, comp.unix.programmer
http://www.ibrado.com/sock-faq http://kipper.york.ac.uk/~vic/sock-faq http://www.ntua.gr/sock-faq
Please email me if you would like to correct or clarify an answer. I would also like to hear from you if you would like me to add a question to the list. I may not be able to answer it, but I can add it in the hopes that someone else will submit an answer. Every hour I seem to be getting even busier, so if I am slow to respond to your email, please be patient. If more than a week passes you may want to send me another one as I often put messages aside for later and then forget about them. I'll have to work on dealing with my mail better, but until then feel free to pester me a little bit.
This FAQ is for C programmers in the Unix environment. It is not intended for WinSock programmers, or for Perl, Java, etc. I have nothing against Windows or Perl, but I had to limit the scope of the FAQ for the first draft. In the future, I would really like to provide examples for Perl, Java, and maybe others. For now though I will concentrate on correctness and completeness for C.
This version of the FAQ will only cover sockets of the AF_INET family, since this is their most common use. Coverage of other types of sockets may be added later.
Sockets are just like "worm holes" in science fiction. When things go
into one end, they (should) come out of the other. Different kinds of
sockets have different properties. Sockets are either connection-oriented
or connectionless. Connection-oriented sockets allow for data to flow
back and forth as needed, while connectionless sockets (also known as
datagram sockets) allow only one message at a time to be transmitted,
without an open connection. There are also different socket families.
The two most common are AF_INET
for internet connections, and
AF_UNIX
for
unix IPC (interprocess communication). As stated earlier, this FAQ deals
only with AF_INET
sockets.
The implementation is left up to the vendor of your particular unix, but
from the point of view of the programmer, connection-oriented sockets work
a lot like files, or pipes. The most noticeable difference, once you have
your file descriptor is that read()
or write()
calls may actually
read or
write fewer bytes than requested. If this happens, then you will have to
make a second call for the rest of the data. There are examples of this
in the
source code that accompanies the faq.
Here is a list of the places I know to get source code for network programming books. It is very short, so please mail me with any others you know of.
Title: Unix Network Programming
Author: W. Richard Stevens
(
rstevens@noao.edu)
Publisher: Prentice Hall, Inc.
ISBN: 0-13-949876-1
URL:
http://www.noao.edu/~rstevens
Title: Power Programming with RPC
Author: John Bloomer
Publisher: O'Reilly & Associates, Inc.
ISBN: 0-937175-77-3
URL:
ftp://ftp.uu.net/published/oreilly/nutshell/rpc/rpc.tar.Z
Recommended by:
Lokmanm Merican (lokmanm#pop4.jaring.my@199.1.1.88)
Title: UNIX PROGRAM DEVELOPMENT for IBM PC'S Including OSF/Motif
Author: Thomas Yager
Publisher: Addison Wesley, 1991
ISBN: 0-201-57727-5
I keep a copy of the resources I know of on my socks page on the web. I don't remember where I got most of these items, but some day I'll check out their sources, and provide ftp information here. For now, you can get them at http://www.ibrado.com/sock-faq.
There is a good TCP/IP FAQ maintained by George Neville-Neil ( gnn@wrs.com) which can be found at http://www.visi.com/~khayes/tcpipfaq.html