Topic : Using Internet Sockets
Author : Beej
Page : << Previous 6  Next >>
Go to page :


Whee! You're a Unix Network Programmer!

4.7. sendto() and recvfrom()--Talk to me, DGRAM-style
"This is all fine and dandy," I hear you saying, "but where does this leave me with unconnected datagram sockets?" No problemo, amigo. We have just the thing.

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 struct sockaddr (which you'll probably have as a struct sockaddr_in and cast it at the last minute) which contains the destination IP address and port. tolen can simply be set to sizeof(struct sockaddr).

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 struct sockaddr that will be filled with the IP address and port of the originating machine. fromlen is a pointer to a local int that should be initialized to sizeof(struct sockaddr). When the function returns, fromlen will contain the length of the address actually stored in from.

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.

4.8. close() and shutdown()--Get outta my face!
Whew! You've been send()ing and recv()ing data all day long, and you've had it. You're ready to close the connection on your socket descriptor. This is easy. You can just use the regular Unix file descriptor close() function:

    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:


0 -- Further receives are disallowed

1 -- Further sends are disallowed

2 -- Further sends and receives are disallowed (like close())

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.)

It's important to note that shutdown() doesn't actually close the file descriptor--it just changes its usability. To free a socket descriptor, you need to use close().

Nothing to it.

4.9. getpeername()--Who are you?
This function is so easy.

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 struct sockaddr (or a struct sockaddr_in) that will hold the information about the other side of the connection, and addrlen is a pointer to an int, that should be initialized to sizeof(struct sockaddr).

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.)

4.10. gethostname()--Who am I?
Even easier than getpeername() is the function gethostname(). It returns the name of the computer that your program is running on. The name can then be used by gethostbyname(), below, to determine the IP address of your local machine.

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.

4.11. DNS--You say "whitehouse.gov", I say "198.137.240.92"
In case you don't know what DNS is, it stands for "Domain Name Service". In a nutshell, you tell it what the human-readable address is for a site, and it'll give you the IP address (so you can use it with bind(), connect(), sendto(), or whatever you need it for.) This way, when someone enters:

    $ telnet whitehouse.gov


telnet can find out that it needs to connect() to "198.137.240.92".

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, the layout of which is as follows:

    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 struct hostent:


h_name -- Official name of the host.

h_aliases -- A NULL-terminated array of alternate names for the host.

h_addrtype -- The type of address being returned; usually AF_INET.

h_length -- The length of the address in bytes.

h_addr_list -- A zero-terminated array of network addresses for the host. Host addresses are in Network Byte Order.

h_addr -- The first address in h_addr_list.

gethostbyname() returns a pointer to the filled struct hostent, or NULL on error. (But errno is not set--h_errno is set instead. See herror(), below.)

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.

Here's an example program:

    /*
    ** getip.c -- a hostname lookup demo
    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.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

Page : << Previous 6  Next >>