Skip to content

Instantly share code, notes, and snippets.

@Descent098
Last active November 16, 2022 00:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Descent098/783f68e1e3943e8796a3aaf8a14f8013 to your computer and use it in GitHub Desktop.
Save Descent098/783f68e1e3943e8796a3aaf8a14f8013 to your computer and use it in GitHub Desktop.
Sockets

Example in python can be seen here: https://github.com/Descent098/ezcv-http/tree/main/socket%20testing

Uses ezcv to generate html and then sockets as the http server

Endian formats

Networking data is in big endian, meaning the integers in hex values go from left to right So for example int x = 0xAABBCCDD would be understood as AA, BB, CC, DD (where each 2 letters is a byte value) In some host systems they operate using little endian meaning the order is reversed DD, CC, BB, AA and the values need to be reversed before being sent The network sending format is called Network Byte Order (or big endian), and the host format is called Host Byte Order (can be big or little endian)

Utility Functions

You can use the following functions to convert Host byte order to Network Byte order (big endian): htons() [for shorts], htonl() [for longs]

to convert back use: ntohs() [for shorts], ntohl() [for longs]

To format IP addresses: ASCII dotted to binary: inet_aton() Binary to ASCII dotted: inet_ntoa()

Usage

C implementation background

// This is a view of the structs that will be used later, they're included in the headerfiles
sockaddr{ 				   // Can be used for any networking protocol
unsigned short sa_family;   // The family of addresses the address belongs to (IPV4 [PF_INET], IPV6 etc.)
char sa_data[14]; 		   // The IP address
} 

sock_addr_in{ 			   // Specifically used for internet
short sin_family; 		   // The family of addresses the address belongs to (IPV4 [PF_INET], IPV6 etc.)
unsigned short sin_port;   // Port number
struct in_addr sin_addr;   // IP Address
char sin_zero[8]; 		  // Pad
}

struct in_addr{
	unsigned long s_addr; // 4 bytes long and represents an IP address
}

Socket()

Call is used to create a socket. The socket is what is actually used to do all the network interactions and sends/receives to/from other endpoints.

Variables

domain or family; the type of communication, typically PF_INET which corresponds to IPV4

type; the type of socket, typically SOCK_STREAM for TCP and SOCK_DGRAM for UDP

protocol or proto; sometimes used with specific socket types, but almost always 0

C implementation

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

Returns a socket descriptor on it's success, and returns -1 when there's an error

socket(PF_INET, SOCK_STREAM, 0)

Python Implementation

class socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

fileno is just a way to specify a config file for a socket, if fileno is specified, the values for family, type, and proto are auto-detected from the specified file descriptor.

import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET == IPV4, SOCK_STREAM == TCP

bind()

The step in the process that actually attaches a socket to a specific port and IP address

C implementation

int Bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)

sockfd is the socket descriptor returned by socket()

my_addr = pointer to a valid sockaddr_in struct cast as a sockaddr pointer

addrlen is the length of the sockarrd_in

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080

// Initialize socket
sock = socket(PF_INET, SOCK_STREAM, 0)
    
// Configure socket
struct sockaddr_in sock_config;

sock_config.sin_family = PF_INET; // IPV4 family address
sock_config.sin_port = htons(80); // port 80 or the common http port
sock_config.sin_addr.s_addr = INADDR_ANY;
bzero(&sock_config, 8);

// Bind socket
bind(sock, (struct sockaddr *)sock_config, sizeof(sock_config));

Python Implementation

socket.bind(tuple[str,int])

socket.bind() takes in a tuple of a string and an int, the string is the IP address (must be IPV4 in this case), and the integer is the port in (IP_ADDRESS, PORT) order.

import socket

# Initialize socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET == IPV4, SOCK_STREAM == TCP

# Bind Socket
address = '127.0.0.1' # The IP address to bind to
port = 8080 # The port to bind to
server_socket.bind((address, port))

listen()

Tells the computer to wait for connections on the configured socket

C implementation

int Listen(int sock, int backlog)

sock is the socket returned by socket()

backlog = maximum pending connections queue

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080

// Initialize socket
sock = socket(AF_INET, SOCK_STREAM, 0)
    
// Configure socket
struct sockaddr_in sock_config;

sock_config.sin_family = PF_INET; // IPV4 family address
sock_config.sin_port = htons(PORT); // port 8080 defined above
sock_config.sin_addr.s_addr = INADDR_ANY;
bzero(&sock_config, 8);

// Bind socket
bind(sock, (struct sockaddr *)sock_config, sizeof(sock_config));

// Listen for up to 10 connections
listen(sock, 10) 

Python Implementation

import socket

# Initialize socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET == IPV4, SOCK_STREAM == TCP

# Bind Socket
address = '127.0.0.1' # The IP address to bind to
port = 8080 # The port to bind to
server_socket.bind((address, port))

# Listen on socket for up to 10 conections
server_socket.listen(10)

accept()

Tells the computer to wait for connections on the configured socket

C implementation

int Accept(int socket, (struct sockaddr *)&client, socklen_t *client_len)

socket is the socket integer returned by socket()

client is what will hold the new client's information when accept returns

client_len is the pointer to the size of the client struct

// Setting up the Necessary Structs
sockaddr{ 				   // Can be used for any networking protocol
unsigned short sa_family;   // The family of addresses the address belongs to (IPV4 [PF_INET], IPV6 etc.)
char sa_data[14]; 		   // The IP address
} 

sock_addr_in{ 			   // Specifically used for internet
short sin_family; 		   // The family of addresses the address belongs to (IPV4 [PF_INET], IPV6 etc.)
unsigned short sin_port;   // Port number
struct in_addr sin_addr;   // IP Address
char sin_zero[8]; 		  // Pad
}

struct in_addr{
	unsigned long s_addr; // 4 bytes long and represents an IP address
}

// Initialize socket
sock = socket(PF_INET, SOCK_STREAM, 0)
    
// Configure socket
struct sockaddr_in sock_config;

sock_config.sin_family = PF_INET; // IPV4 family address
sock_config.sin_port = htons(80); // port 80 or the common http port
sock_config.sin_addr.s_addr = INADDR_ANY;
bzero(&sock_config, 8);

// Bind socket
bind(sock, (struct sockaddr *)sock_config, sizeof(sock_config));

// Listen for up to 10 connections
listen(sock, 10) 
    
// Accepting new connections
struct sockaddr_in client;
int len = sizeof(client);
    
Accept(sock, (struct sockaddr *)&client, &len);

Python Implementation

import socket

# Initialize socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET == IPV4, SOCK_STREAM == TCP

# Bind Socket
address = '127.0.0.1' # The IP address to bind to
port = 8080 # The port to bind to
server_socket.bind((address, port))

# Listen on socket for up to 10 conections
server_socket.listen(10)

connection, addr = s.accept()

Connect()

Called by a client to connect to a server/port

C implementation

int Connect(int socket, (struct sockaddr *)&server_addr, socklen_t len)

socket is the socket integer returned by socket()

server_addr is what will hold the server details when connecting

len is the pointer to the size of the server_addr struct

// Initialize socket
sock = socket(PF_INET, SOCK_STREAM, 0)
    
// Configure socket
struct sockaddr_in sock_config;

sock_config.sin_family = PF_INET; // IPV4 family address
sock_config.sin_port = htons(80); // port 80 or the common http port
sock_config.sin_addr.s_addr = INADDR_ANY;
bzero(&sock_config, 8);

// Bind socket
bind(sock, (struct sockaddr *)sock_config, sizeof(sock_config));

// Listen for up to 10 connections
listen(sock, 10) 
    
// Accepting new connections
struct sockaddr_in client;
int len = sizeof(client);
    
Accept(sock, (struct sockaddr *)&client, &len);


// Connecting to a remote server
Connect(sock, (struct sockaddr *)&server_addr, len);

Python Implementation

import socket

# Initialize socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET == IPV4, SOCK_STREAM == TCP

# Bind Socket
address = '127.0.0.1' # The IP address to bind to
port = 8080 # The port to bind to
server_socket.bind((address, port))

# Listen on socket for up to 10 conections
server_socket.listen(10)

connection, addr = s.accept()

working C implementation

Server

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
int main(int argc, char const* argv[])
{
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = { 0 };
    char* hello = "Hello from server";
 
    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0))
        == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
 
    // Forcefully attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET,
                   SO_REUSEADDR | SO_REUSEPORT, &opt,
                   sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
 
    // Forcefully attaching socket to the port 8080
    if (bind(server_fd, (struct sockaddr*)&address,
             sizeof(address))
        < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    if ((new_socket
         = accept(server_fd, (struct sockaddr*)&address,
                  (socklen_t*)&addrlen))
        < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    valread = read(new_socket, buffer, 1024);
    printf("%s\n", buffer);
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    return 0;
}

Client

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
 
int main(int argc, char const* argv[])
{
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char* hello = "Hello from client";
    char buffer[1024] = { 0 };
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }
 
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
 
    // Convert IPv4 and IPv6 addresses from text to binary
    // form
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)
        <= 0) {
        printf(
            "\nInvalid address/ Address not supported \n");
        return -1;
    }
 
    if (connect(sock, (struct sockaddr*)&serv_addr,
                sizeof(serv_addr))
        < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    valread = read(sock, buffer, 1024);
    printf("%s\n", buffer);
    return 0;
}

You cannot use berkley sockets directly in JS. The closest you can get is unix sockets, which work similarly, but stream from out of a file instead of the NIC directly.

This code only works server side with NodeJS, and you must manually remove the file afterwards!

'use strict';

const net = require('net');
const unixSocketServer = net.createServer();

unixSocketServer.listen('/tmp/unixSocket', () => {
  console.log('now listening');
});

unixSocketServer.on('connection', (s) => {
  console.log('got connection!');
  s.write('hello world');
  s.end();
});

code from here

@Descent098
Copy link
Author

Example can be seen here:
https://github.com/Descent098/ezcv-http/tree/main/socket%20testing

Uses ezcv to generate html and then sockets as the http server

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment