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


a data structures book and go from there.)

I never said it was easy. Ok, I did say it was easy. And it is; you just need practice and pretty soon it'll come to you naturally. By Excalibur I swear it!





7. Common Questions
Q: Where can I get those header files?
Q: What do I do when bind() reports "Address already in use"?
Q: How do I get a list of open sockets on the system?
Q: How can I view the routing table?
Q: How can I run the client and server programs if I only have one computer? Don't I need a network to write network program?
Q: How can I tell if the remote side has closed connection?
Q: How do I implement a "ping" utility? What is ICMP? Where can I find out more about raw sockets and SOCK_RAW?
Q: How do I build for Windows?
Q: How do I build for Solaris/SunOS? I keep getting linker errors when I try to compile!
Q: Why does select() keep falling out on a signal?
Q: How can I implement a timeout on a call to recv()?
Q: How do I encrypt or compress the data before sending it through the socket?
Q: What is this "PF_INET" I keep seeing? Is it related to AF_INET?
Q: How can I write a server that accepts shell commands from a client and executes them?
Q: I'm sending a slew of data, but when I recv(), it only receives 536 bytes or 1460 bytes at a time. But if I run it on my local machine, it receives all the data at the same time. What's going on?
Q: I'm on a Windows box and I don't have the fork() system call or any kind of struct sigaction. What to do?
Q: Where can I get those header files?

A: If you don't have them on your system already, you probably don't need them. Check the manual for your particular platform. If you're building for Windows, you only need to #include <winsock.h>.

Q: What do I do when bind() reports "Address already in use"?

A: You have to use setsockopt() with the SO_REUSEADDR option on the listening socket. Check out the section on bind() and the section on select() for an example.

Q: How do I get a list of open sockets on the system?

A: Use the netstat. Check the man page for full details, but you should get some good output just typing:

$ netstat



The only trick is determining which socket is associated with which program. :-)

Q: How can I view the routing table?

A: Run the route command (in /sbin on most Linuxes) or the command netstat -r.

Q: How can I run the client and server programs if I only have one computer? Don't I need a network to write network program?

A: Fortunately for you, virtually all machines implement a loopback network "device" that sits in the kernel and pretends to be a network card. (This is the interface listed as "lo" in the routing table.)

Pretend you're logged into a machine named "goat". Run the client in one window and the server in another. Or start the server in the background ("server &") and run the client in the same window. The upshot of the loopback device is that you can either client goat or client localhost (since "localhost" is likely defined in your /etc/hosts file) and you'll have the client talking to the server without a network!

In short, no changes are necessary to any of the code to make it run on a single non-networked machine! Huzzah!

Q: How can I tell if the remote side has closed connection?

A: You can tell because recv() will return 0.

Q: How do I implement a "ping" utility? What is ICMP? Where can I find out more about raw sockets and SOCK_RAW?

A: All your raw sockets questions will be answered in W. Richard Stevens' UNIX Network Programming books. See the books section of this guide.

Q: How do I build for Windows?

A: First, delete Windows and install Linux or BSD. };-). No, actually, just see the section on building for Windows in the introduction.

Q: How do I build for Solaris/SunOS? I keep getting linker errors when I try to compile!

A: The linker errors happen because Sun boxes don't automatically compile in the socket libraries. See the section on building for Solaris/SunOS in the introduction for an example of how to do this.

Q: Why does select() keep falling out on a signal?

A: Signals tend to cause blocked system calls to return -1 with errno set to EINTR. When you set up a signal handler with sigaction(), you can set the flag SA_RESTART, which is supposed to restart the system call after it was interrupted.

Naturally, this doesn't always work.

My favorite solution to this involves a goto statement. You know this irritates your professors to no end, so go for it!

select_restart:
    if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) {
        if (errno == EINTR) {
            // some signal just interrupted us, so restart
            goto select_restart;
        }
        // handle the real error here:
        perror("select");
    }



Sure, you don't need to use goto in this case; you can use other structures to control it. But I think the goto statement is actually cleaner.

Q: How can I implement a timeout on a call to recv()?

A: Use select()! It allows you to specify a timeout parameter for socket descriptors that you're looking to read from. Or, you could wrap the entire functionality in a single function, like this:

#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

int recvtimeout(int s, char *buf, int len, int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;

    // set up the file descriptor set
    FD_ZERO(&fds);
    FD_SET(s, &fds);

    // set up the struct timeval for the timeout
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    // wait until timeout or data received
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // timeout!
    if (n == -1) return -1; // error

    // data must be here, so do a normal recv()
    return recv(s, buf, len, 0);
}

// Sample call to recvtimeout():
    .
    .
    n = recvtimeout(s, buf, sizeof(buf), 10); // 10 second timeout

    if (n == -1) {
        // error occurred
        perror("recvtimeout");
    }
    else if (n == -2) {
        // timeout occurred
    } else {
        // got some data in buf
    }
    .
    .



Notice that recvtimeout() returns -2 in case of a timeout. Why not return 0? Well, if you recall, a return value of 0 on a call to recv() means that the remote side closed the connection. So that return value is already spoken for, and -1 means "error", so I chose -2 as my timeout indicator.

Q: How do I encrypt or compress the data before sending it through the socket?

A: One easy way to do encryption is to use SSL (secure sockets layer), but that's beyond the scope of this guide.

But assuming you want to plug in or implement your own compressor or encryption system, it's just a matter of thinking of your data as running through a sequence of steps between both ends. Each step changes the data in some way.



server reads data from file (or whereever)

server encrypts data (you add this part)

server send()s encrypted data


Now the other way around:



client recv()s encrypted data

client decrypts data (you add this part)

client writes data to file (or whereever)


You can also do compression at the same point that you do the encryption/decryption, above. Or you could do both! Just remember to compress before you encrypt. :)

Just as long as the client properly undoes what the server does, the data will be fine in the end no matter how many intermediate steps you add.

So all you need to do to use my code is to find the place between where the data is read and the data is sent (using send()) over the network, and stick some code in there that does the encryption.

Q: What is this "PF_INET" I keep seeing? Is it related to AF_INET?

A: Yes, yes it is. See the section on socket() for details.

Q: How can I write a server that accepts shell commands from a client and executes them?

A: For simplicity, lets

Page : << Previous 13  Next >>