Networking - C Sockets in Linux

    Hello my friends it's a me again! Today we will get into "real" networking starting off with Sockets in Linux. We will again use C Languages for all our Coding and I will start out with some Theory getting also into the 2 different kinds of Sockets we are able to use (TCP and UDP). Then we will get into the functions that the socket.h library gives us and first explain how we use them and lastly get into how we create a TCP and UDP Socket in steps and implement it next time. Lastly we will get into other libraries and functions/datatypes that are useful. So, without further do let's get started!

Sockets:

    Sockets are communication mechanisms that let a client communicate with a server and vise versa through a local connection or the Internet. They differentiate from Pipes, cause here the Client and Server have a different role clearly. The sockets offer an API that lets us access the lower level of the network. The sockets sit on the Transport Layer of the OSI Network Model. You can check it out here if you don't have heard of it before.

Protocols:

    Inside of this Transport Layer we can use 2 protocols: TCP or UDP. TCP is a stream protocol and UDP is a datagram protocol. This means that TCP creates a "real" connection between the server and client (connection-oriented) and guarantees the transport of packets between them. UDP on the other hand uses datagrams and doesn't create a connection (connectionless) and simply sends the packets from one source to another without guaranteeing the transport of those datagrams.

So, we can create 2 types of Sockets:

  • Stream Sockets (TCP) -> That give us a connection between the client and server and ensure that the packets will not be lost.
  • Datagram Sockets (UDP) -> That don't create a connection and send datagrams without ensuring their correct reception

TCP vs UDP:

    Seeing that a UDP Socket is unsafe for sending packets you might think that those Sockets are bad and useless, but, you have to think also about some other things. For example, when we are in a local LAN network the datagrams will have a much better performance, cause resending packets doesn't take so much time. In the other hand TCP Sockets will take much more time, because we will need to setup a connection first. So, when we are in a local network or want to simply stream data to many different receivers without having the need of correct reception (for example livestreams) a UDP sockets work better than a TCP socket. TCP is great when we want to ensure correct reception and are in a bigger network.


Socket Library:

    In Linux we use the sys/socket.h library to create, manage and use POSIX Sockets. We will also need the sys/types.h library for some datatypes. The way we set up Clients and Server differentiate a little bit. We also have changes in the parameters and functions used depending on the protocol (TCP or UDP) that we will use. To make it easier for me to explain I will first explain all the functions and what they do and then get into how we create a TCP or UDP, Client or Server Socket.

The functions that we need are:

  • socket(int domain, int type, int protocol) -> that returns a socket descriptor for a specified with parameters usage, where we will use AF_INET for the domain (that means Internet) and SOCK_STREAM (TCP) or SOCK_DGRAM (UDP) for the type and 0 (default) for the protocol
  • bind(int socket, const struct sockaddr *address, socklen_t address_len) -> that binds the socket to the specific port in the address
  • listen(int socket, int backlog) -> that makes the socket listen for incoming connections with a queue of size backlog (not needed in UDP)
  • connect(int socket, const struct sockaddr *address, socklen_t address_len) -> that connect to an specified server
  • accept(int socket, struct sockaddr *address, socklen_t *address_len) -> that accepts an incoming client connection 
  • send(int socket, const void *message, size_t length, int flags) -> that sends a message to the connected address (connect() or accept() must be done previously and is mostly used in TCP)
  • sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) -> that sends a message to the specified address (used in UDP)
  • recv(int socket, void *buffer, size_t length, int flags) -> that receives a message from the connected addresst (connect() or accept() must be done previously and is mostly used in TCP)
  • recvfrom(int socket, void *buffer, size_t length, int flags, const struct sockaddr *address, socklen_t *address_len) -> that receives a message from the specified address (used in UDP)
  • shutdown(int socket, int flag) -> to close a socket in a specified way using the flag
  • close(int socket) -> to close the socket descriptor (that is from the stdio.h library)

Steps for setup:

Let's now get into how we create and use each kind of Client and Server!

A TCP Client needs to do the following steps:

  • Create a socket using socket()
  • Bind to a specific Port using bind() (optionally)
  • Connect to the Server using connect()
  • Send and receive packets using send() and recv()
  • Close the socket after finishing using close() or shutdown()

A TCP Server needs to do the following steps:

  • Create a socket using socket()
  • Bind to a specific Port using bind()
  • Listen for incoming connections using listen()
  • Accept a incoming connection using accept()
  • Receive and send packets using recv() and send()
  • Close the socket after finishing using close() or shutdown()

A UDP Client needs to do the following steps:

  • Create a socket using socket()
  • Send and receive datagrams to and from a specified address using sendto() and recvfrom()
  • Close the socket after finishing using close() or shutdown()

A UDP Server needs to do the following steps:

  • Create a socket using socket()
  • Bind to a specific Port using bind()
  • Receive and send datagrams from and to a specified address using recvfrom() and sendto()
  • Close the socket after finishing using close() or shutdown()


You can see that TCP needs a lot more steps than UDP does!


Datatypes and other useful functions:

    The function definitions I gave you previously had some wierd new datatypes/structs that we will now take a look at. The datatypes socklen_t and size_t are unsigned integers. The struct called sock_addr is mostly not used directly and we have other ways of getting this address using some functions.

sock_addr looks like this:

struct sockaddr {
    unsigned short sa_family; // address family
    char sa_data[14]; // 14 bytes of protocol address
};


    The other sock_addr "like" struct is called sockaddr_in and it contains another struct called in_addr. We use it instead of sock_addr, cause it helps us specify the port and ip address more easily.

sockaddr_in looks like this:

struct sockaddr_in {
    u_short sin_family; // type of address
    u_short sin_port; // protocol port number
    struct in_addr sin_addr; // IPv4 address
    char sin_zero[8]; // unused (set to zero)
};


in_addr looks like this:

struct in_addr { // IPv4 Internet address
    uint32_t s_addr; // Α 32-bit int (4 bytes)
};


    To make our life easier and get the host/server easier we can use the hostent or servent struct and functions from the netdb.h library. I will not get into how we use them today, but you can read about hostent here and servent here.


    Because some machines are big endian and others little endian we will have to use some conversion functions from the netinet.h library. Using these we can convert between host and network format.

Those functions are:

  • htons(hostshort) -> that converts shorts (16-bit) from host to network format
  • htonl(hostlong) -> that converts longs (32-bit) from host to network format
  • ntohs(netshort) -> that converts shorts (16-bit) from network to host format
  • ntohl(netlong) -> that converts longs (32-bit) from network to host format 

    We will use these functions to set up the port number when binding to a port or connecting to a server and also when sending/receiving to send in network format and receive in host format.


And this is actually it for today!

Next time we will get into basic Client-Server implementation and communication!

Bye!

H2
H3
H4
3 columns
2 columns
1 column
3 Comments