CSE 522S: Studio 14

Linux Sockets


Sockets are a fundamental abstraction for many types of computer communication- both inside a single machine and over networks. They are the basic primitives used to connect machines together, and they are used to implement all kinds of networked communication. Despite this, they are surprisingly simple to use, as they encapsulate and hide virtually all of the technical details need to accomplish such communication, and are accessed (like many other things in Linux) just like you were reading or writing a local file or pipe.

In this studio, you will:

  1. Use sockets to send data between processes within a system
  2. Use sockets to recieve data from the Internet

Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete.

As you work through these exercises, please record your answers, and when finished email your results to dferry@email.wustl.edu with the phrase Sockets Studio in the subject line.

Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.


Required Exercises

  1. As the answer to the first exercise, list the names of the people who worked together on this studio.

  2. The first goal of this studio is to establish a local connection between two processes on your Raspberry Pi. We'll write two programs, a server and a client. First we will write the server, whose role is to create the communications channel and to listen for incoming connections. A good example of how to construct a basic server can be found in man 2 bind under the section labeled EXAMPLE.

    Your server should perform the following actions:

    1. First, create a socket with the system call socket(). To create a local connection use the domain AF_LOCAL (or equivalently AF_UNIX), the connection type SOCK_STREAM, and protocol zero.
    2. Second, create the communications channel with the bind() system call. This associates your socket from step one with a machine-visible address. In the case of AF_LOCAL, this address is a path in the file system. Directions on how to specify a local address in this way can be found at man 7 unix.
    3. Now, having created the channel, your program needs to declare that it is going to listen for incoming connections with the listen() system call. Sockets only allow one process to connect at a time, so the second parameter determines how many connection requests can wait in line before they are rejected.
    4. Use the accept() system call to accept a connection over the socket interface. If no connection is immediately available then this system call will, by default, cause your program block until another program attempts to connect.
    5. Finally, use the system call unlink() in order to destroy the socket and connection before the program returns.

    When your program successfully returns from accept() the connection has been established. You may read and write data over the channel with the lower level read() and write() system calls, or you can instantiate a stream interface with the fdopen function. When doing so, be sure to use the socket descriptor returned by the call to accept(), not the one created by the initial call to socket().

    Note: Many things can go wrong with socket-based communication. As usual, you should always check function return codes to detect errors. Recall that most functions allow you to print a descriptive error statement with a line of code such as: printf("Program error, reason: %s\n", strerror(errno)). This will require including the headers string.h and errno.h.

  3. Now we will write a client program. We will make use of the previously created communications channel. In particular:

    1. Create a communications endpoint with a call to the socket() system call.
    2. Establish a connection with the connect() system call. This requires using the same socket address struct as the call to bind().

    At this point, barring any errors, your communcations channel is ready to be used. Send some messages to the server program and print them to standard output in order to validate the functionality. Copy and paste the server output as the answer to this exercise. Note that, like with pipes, a call to read an empty socket will block until data is available.

  4. If you haven't already, refactor your server with an event loop so that it will stay alive through multiple connection attempts. This requires that your server make multiple calls to accept(), but not to listen() or bind(). Print a message each time a new connection is established and run your client program several times with the same server to validate this behavior. Copy and paste the server output as the answer to this exercise.

  5. Rather than having your server stay alive forever, have your program terminate when it recieves the message "quit". Use the function strncmp to test for string equality. The similar function strcmp is difficult to use with large buffers.

    Copy and paste terminal output demonstrating this functionality

  6. The primary use of sockets is to manage networked connections between remote computers. The final exercise of this studio will have you connect to a Raspberry Pi at the instructor's house and receive a message.

    Use your client program as a template, but we will modify it to use an internet connection rather than a local connection. You will connect to port 17000 at IP address 24.107.185.147.

    1. Include the files netinet/ip.h and arpa/inet.h.
    2. Modify your call to socket() to specify an internet connection type with AF_INET.
    3. Create a struct of type sockaddr_in to specify the internet address. This struct is documented at man 7 ip. Set the sin_family field to AF_INET, the sin_port field to htons(port_num), where htons() is a function that translates between numeric representations on your computer and the network. Third, use the function inet_aton() to translate a c-style string containing the target IP address and store it at sin_addr using the second argument.
    4. Finally, make the connection using connect() After opening the connection, read the message from the socket and print it to standard out. This server will refuse any data you send to it.

    As the answer to this question, copy and paste the output from your terminal. If you cannot connect for whatever reason, give a descriptive error message using strerror() and errno.


    Things to Turn In: