Blocking I/O: On A Network Socket

Opening a Network Connection

  • Even more than a file, network connections are tedious

    • Determine destination address: local/remote? (⟶ do I send it to address on local net, or do I send it to my net’s gateway?)

    • Formulate IP packet

    • Setup DMA on network hardware

    • Wait for interrupt

    • Start transmission timeout

    • … (you get the point) …

  • ⟶ a job for an Operating System

Abstractions

  • What do we read?

    • Bytes from a network connection

    • Addresses depend on network type (TCP/IP is just one out of many)

    • Socket abstraction!

  • How do we read that? In the case of TCP …

    • Don’t want to … blah interrupt, DMA, blah … want none of that

    • Only want to connect to peer/server

    • Only want to read data

Simplicity

  • System call: socket()

  • System call: connect()

  • System call: read()

  • System call: write()

  • System call: close()

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>


int main(void)
{
    // allocate space (in kernel) for a TCP connection. the handle to
    // it is a FILE DESCRIPTOR, again.
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    // error handling
    if (fd == -1) {
        perror("socket()");
        exit(1);
    }

    // connect to peer/server. fill in address structure first.
    {
        int error;

        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(1234);
        inet_aton("127.0.0.1", &addr.sin_addr);

        // this sends out packets via network hardware, does all the
        // tedious timeout handling, DMA, and whatnot.
        error = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
        // error handling
        if (error == -1) {
            perror("connect()");
            exit(1);
        }
    }

    // allocate buffer, and *read* in a loop until done. NOTE THIS IS
    // THE SAME AS WITH FILES: "Everything is a file!".
    char buffer[16];
    while (1) {
        ssize_t nbytes_read = read(fd, buffer, sizeof(buffer));
        if (nbytes_read == 0) // end-of-file (EOF) -> done
            break;
        write(STDOUT_FILENO, buffer, nbytes_read);
    }

    // free resources
    close(fd);
    return 0;
}

Build, Run, Look

$ gcc -o read-network read-network.c

Client

Server (start first)

$ strace ./read-network
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
read(3,

Blocking system call: nothing there to read

$ nc -l -p 1234
... "hallo du!\n", 16)              = 10
write(1, "hallo du!\n", 10hallo du!
)             = 10
read(3,

Wakes up to read “hallo du!\n”, and then blocks again

hallo du!
... "this is a very l", 16)         = 16
write(1, "this is a very l", 16this is a very l)        = 16
read(3, "ong text!!!!!\n", 16)          = 14
write(1, "ong text!!!!!\n", 14ong text!!!!!
)         = 14
read(3,

(Needs two iterations for a very long text)

this is a very long text!!!!!
... "", 16)                         = 0
close(3)                                = 0
^D is end-of file on standard input
^D