• Top
  • Reply

My Network Programming Cheat Sheet

This cheat sheet highlights the steps required to get a C++ Client/Server program running in TCP and UDP protocols. This article provides a handy pseudo code client/server using network sockets. The cheat sheet itself leaves out the finer details of network programming which are important. I recommend you read the Beej's guide to network programming first and then continue below.

Some of the major functions we use in socket programing listed below with their helpful short descriptions.

  • socket - Create a socket
  • bind - Bind a socket to a port
  • listen - Listen for addresses (Blocking)
  • accept- Accept data from client on a new socket file
  • send - Send bytes to the client, in the child process.
  • recv - Receive bytes from the client, in the child process.
  • sendto - Explicit version of send()
  • recvfrom - Explicit version of recv()

What do you mean by Explicit?

sendto and recvfrom explicitly define who to send the data to, and where the data was received. These function are most commonly used for UDP protocols however they can be used for TCP (connected sockets) as well.

A simple step guide to networking with sockets can fall within the following:

  • Set up the sockets and listen if you are a server, otherwise connect to the server
  • Block using accept or recvfrom, depending on TCP or UDP
  • Read incoming data from client, and respond back. In some cases this will be reversed, where you send the data and then wait for a response.

For advanced usage the above can be expanded to say

  • For each read accepted client fork or thread before dealing with the client, so the server can continue to listen to other clients.

TCP

These are established connections and require you to bind, listen or connect (connect if you are a client, bind and listen otherwise) before you can transfer data.

Server

Setup the protocol then wait and listen to connections from client, and transmit on each connection. Note the steps below is for one connection at a time.

# Create address structure
address a = { IP4, TCP, 2047, localhost }

# Create a new socket, bind it to a port and start listening
socket fd = socket(a)
bind(fd, a)
listen(fd)

while True:
    # Accept a new connection, accept is blocking and returns when
    # client connects. By accepting we create a temporary socket to
    # talk to the client
    socket temp = accept(fd)

    # Send data, to the client and print the message returned by
    # the client
    message m = recv(temp)
    send(temp, "client said:" + m)

    close(temp)

close(fd)

The address type can be quantified as:

address { type, protocol, port, ipaddress };

Client

Instead of binding to a port and listening for clients we instead connect to the server and start transmitting and/or receiving.

# Create address structure
address a = { IP4, TCP, 2047, "serveraddress.com" }

# Create a new socket and connect to the server on this socket
socket fd = socket(a)
connect(fd);

# While the user keeps typing
while <STDIN> as in:

    # Send to the server whatever the user typed
    send(fd, in)

    # Receive a response from the server and print it
    message m = recv(fd);
    print (m)

close(fd);

Advanced Server

What if the request a client asked for takes time? You don't want other clients to wait for the server to finish with one client at a time. Wouldn't it be better to try to deal with other clients while the server is processing one client?

This is simply achieved by using fork() post accept(), so the parent can get back at accepting other clients, while the child forked process can deal with one client at a time. I prefer using threads instead of forking for this kind of thing but we gotta walk before we run.

# Create address structure
address a = { IP4, TCP, 2047, localhost }

# Create a new socket, bind it to a port and start listening
socket fd = socket(a)
bind(fd, a)
listen(fd)

while True:
    # Accept a new connection, accept is blocking and returns when
    # client connects. By accepting we create a temporary socket to
    # talk to the client
    socket temp = accept(fd)

   # Let the program create a child process to deal with the connection
   if fork() == 0:
        # Send data, to the client and print the message returned by 
        # the client
        message m = recv(temp)
        send(temp, "client said:" + m)
        close(temp)

 close(fd)

UDP

Unlike TCP protocol, the client and server does not care for established connections and due to this the code changes ever so slightly. Some of our handshake code disappears and instead of using send() and recv() we use sendto() and recvfrom(). One noticeable difference is that sendto() explicitly requires the address to which to send data. Where as recvfrom() takes in an address parameter which holds the address of the client the message was received from.

Unlike TCP, UDP fires data to IP addresses given a port without caring for its arrival. It is best to use UDP when you want faster communication but don't care about delivery or order. As an example I use UDP to transfer video or send robots instructions on their next move.

Client

No connection required, and the protocol must be changed to UDP

# Create address structure
address a = { IP4, UDP, 2047, "serveraddress.com" }

# Create a new socket and connect to the server on this socket
socket fd = socket(a)

# While the user keeps typing
while <STDIN> as in:

    # Send to the server whatever the user typed
    sendto(fd, in, a)

close(fd)

Server

Here we will receive using recvfrom() and setting the from variable which is of type address.

# Create address structure
address a = { IP4, UDP, 2047, "serveraddress.com" }

# Create a new socket and connect to the server on this socket
socket fd = socket(a)

# While the user keeps typing
while True:

    # Listen for packets on socket and update the from with the 
    # address from which the packets were recieved
    address from
    message m = recvfrom(fd, from)

    # Send the sender back the message recieved
    sendto(fd, "gotmessage: " + m, from)

close(fd)

Caveat

  • send() won't necessarily send all packet data, to come around this check the number of bytes sent and if less than the total bytes call another send with the new position, repeat if you come up short again.
  • Packaging built in types can be unportable, use a designated packaging and unpackaging method. For example a double may not be represented in the same format on the client and server, so you can't just pass a double's byte values across the line and hope they convert correctly. Use library to package built-in types or create your own protocol that understands builtin types in a known manner.

Improvements

  • Use select() or pselect() so your server can perform actions if there are no connection to the server. Preferably use pselect, it allows signals to interrupt.
  • For alot of concurrent connections use libevent instead of p/select. It is a hell of alot faster.
  • For full working examples use the Beej's guide to align yourself. (See resources below).

Resources

  • Beej's guide to network programming, Every man women child should read this should they wish to communicate using linux sockets.
  • man pages for socket, bind, listen, accept, connect, recv, send, recvfrom, sendto
  • pthread API useful if you are thinking of using threads instead of fork

By

8th Jan 2014
© 2011 Shahmir Javaid - http://shahmirj.com/blog/36


Back to Top
All content is © copyrighted, unless stated otherwise.
Subscribe, @shahmirj, Shahmir Javaid+