Welcome to /Bubbles/Logs

Heres the latest post!

BubbleChat & Network programming with Sockets PT 1.1 2024-04-14

WHAT HAPPENED TO PT 2?

This follow-up had a lot happen with it. Originally I was writing out the second part it was in C# and following directly where I had left off, but due to a mistake on my part while distro hopping, I had re-imaged the wrong M.2 drive that I use to dual boot, Which in turn wiped the posts I had prepared around that time. How did that happen? I'm not sure I guess that windows and Linux disagree on which of the identical drives should be labeled drive 0. That's been my guess since it was quite the surprise when it happened. Regardless it was a lot of work to try to rewrite it due to how much was deleted. Cut to now. I am working with C more lately in my projects and I was messing with sockets and I still have ideas for BTP that I wanted to implement so what better way to continue on than following me along the journey of learning how C goes about socket programming, than having me argue with code I wrote months ago trying to piece everything together.

I am not going to be really covering too much in this part. This is just to get some struct and function definitions out there, and I need to talk about what I plan on doing for the project, since this is the first project I am documenting as I go.

Warning

I am learning C. I am not proficient with C in any regard. I am using my projects to teach myself and improve with C. My code is most likely not going to be the most secure. I am trying to learn how to manage issues such as strlen() or scanf() whether thats avoid them entirely or check data before passing to the functions. I am writing C to the best of my ability, so just don't take any of my code as production grade software. Think of it as something small you'd host on a LAN at home, or to experiment with how things work.

Now that I got that out of the way, lets get into it.

Data Structures

C has a lot of data structures to know. But for networking at the moment from what I have seen we need to know of

struct addrinfo {
    int ai_flags;
    int ai_family;
    int ai_socktype;
    int ai_protocol;
    socklen_t ai_addrlen;
    struct sockaddr *ai_addr;
    char *ai_canonname;
    struct addrinfo *ai_next;
};

struct sockaddr {
    sa_family_t sa_family;
    char sa_data[];
};

// IPv4
struct sockaddr_in {
    short sin_family;
    unsigned short sin_port;
    struct in_addr sin_addr;
    char sin_zero[8];
};

// IPv6
struct sockaddr_in6 {
    u_int16_t sin6_family;
    u_int16_t sin6_port;
    u_int32_t sin6_flowinfo;
    struct in6_addr sin6_addr;
    u_int32_t sin6_scope_id;
};

struct sockaddr_storage {
    sa_family_t  ss_family;
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

I'm going to be honest for the most part I get whats happening but there are some fields of these structs that I just at the moment do not understand. Most of my confusion is surrounded by addrinfo.ai_next just because I am unsure what the context of its usage is.

other than that the rest of the syntax looks really similar to what you would see in C#, Java, or Python with the exception of using the data structures so much.

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


int socket(int domain, int type, int protocol);

int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);

int listen(int sockfd, int backlog);

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// TCP
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

// UDP
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 
    const struct sockaddr *dest_addr, socklen_t addrlen);

// TCP
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

// UDP
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
        struct sockaddr *src_addr, socklen_t *addrlen);

Since I am inexperienced with C currently, there is more for me to learn and I am excited about that, but there seemingly is more to the socket API in C such as direct access to RAW sockets, a lot of data structures, and different options for handling multiple client connections using multiple threads or using the following functions.

/* select()  allows  a  program to monitor multiple file descriptors, waiting until one or more of
the file descriptors become "ready" for some class of I/O operation */
int select(int nfds, fd_set *readfds, fd_set *writefds, 
                fd_set *exceptfds, struct timeval *timeout);


/*  fork()  creates  a new process by duplicating the calling process. The new process is referred
to as the child process.  The calling process is referred to as the parent process. */
pid_t fork(void);

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
/* poll()  performs  a similar task to select(2): it waits for one of a set of 
file descriptors to become ready to perform I/O. */


// alternatively to poll, on Linux based systems you get access to epoll
#include <sys/epoll.h>

/*  epoll API full MAN page description
The epoll API performs a similar task to poll(2): monitoring
multiple file descriptors to see if I/O is possible on any of
them.  The epoll API can be used either as an edge-triggered or a
level-triggered interface and scales well to large numbers of
watched file descriptors.
*/

A lot of what Ive seen with C's socket experience is just a lower level look at whats happening. I am really pushing for this because I am not only trying to understand networking on a lower level but programming as a whole. A lot of my goals rely on low level knowledge, and honestly there's no better way than to remove the safety rails so I can learn what the computer is really doing under the hood.

The server is written in C alright, what about the client? Well given some thought I really want to focus on the server's development so I am going to be writing the client in python since realistically the server will be doing everything and the client just does the same in reverse most of the time. I also have never really used sockets across different languages and I kinda want to show that it really doesn't matter which language you pick its the logic you throw into your connection and packet handling. I'm not saying I wont make the C client but for now to speed up development I will just be using Python. It helps too since Python borderline reads like pseudo-code.

I wrote this echo client (code snippet below) in Python in order to test basic I/O functionality such as sending and receiving on the socket, along with connecting with multiple clients.

import socket
import threading

# SERVER DATA CONSTANT VARIABLES
HOST = "127.0.0.1"
PORT = 12345

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

def send(s):
    while True:
        s.send(bytes(input("> "), encoding="ascii"))

def rec(s):
    while True:
        data = s.recv(1024)
        print(str(data))

# Put send and receive on their own threads
t = threading.Thread(target=send, args=(s,))
t2 = threading.Thread(target=rec, args=(s,))

t.start()
t2.start()

The code is straight forward but just to clarify, the usage of threads is so we don't need to send any data in order to get an update from the server. It would suck if we needed to send a message to receive another wouldn't it?

I plan on going further with this version of the BTP than the C# version. Here's another list of things I thought of adding.

Now that everything is covered I will get to it. Continuing this has been on my mind with every post I write so I am excited to go through documenting my journey with C and Networking.

Thanks for reading!