Skip to content

How Sockets Work

Conrad edited this page Apr 6, 2024 · 9 revisions

Summary of my findings on researching about sockets

What Are Sockets?

Sockets are the "endpoints" of a 2 way communication channel. We'll be using sockets to handle network communications between the client (e.g., a web browser) and our server. For webserv we will use: TCP (Transmission Control Protocol) sockets, which provide a reliable, more secure way to communicate, ensuring data is sent successfully.

Basic overview of how you use sockets/manipulate them

  1. Create a Socket: Use the socket() function to create a socket.
  2. Bind the Socket (Server-Side): Use the bind() function to bind the socket to an address and port.
  3. Listen on the Socket (Server-Side): Use the listen() function to listen for incoming connections.
  4. Accept Connections (Server-Side): Use the accept() function to accept incoming connections. This is where we would use poll() I think.
  5. Connect to a Server (Client-Side): Use the connect() function to connect to a server. (NOTE: NOT SURE HOW TO SET THIS ONE UP, OR IF WE EVEN NEED TO??)
  6. Send/Receive Data: Use send()/recv() or write()/read() to send and receive data.
  7. Close the Socket: Use the close() function to close the socket when done.

socket()

Creates a new socket

int socket(int domain, int type, int protocol);
  • domain: Specifies the communication domain, AF_INET for IPv4, AF_INET6 for IPv6.
  • type: Specifies the communication semantics, SOCK_STREAM for TCP and SOCK_DGRAM for UDP
  • protocol: Specifies the protocol to be used with the socket. Not entirely sure how this works, but normally only a single protocol exists to support a particular socket, so we can just set this as 0 i think.

Read more here: https://man7.org/linux/man-pages/man2/socket.2.html

Example:

int sockfd = socket(AF_INET, SOCK_STREAM, 0); // returns the socket "file descriptor"

bind()

Binds a socket to an address/port

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd: File descriptor of the socket to be bound.
  • addr: A pointer to a sockaddr (see below) structure representing the address to bind to
  • addrlen: The size of the address structure

Read more here: https://man7.org/linux/man-pages/man2/bind.2.html

What is the sockaddr data structure??
The sockaddr structure is a generic descriptor for any type of socket operation in C/C++. It's used to specify socket addresses in network functions, such as bind(), connect(), accept(). However, ITS A GENERIC STRUCTURE!! We would use sockaddr_in for IPv4 and sockaddr_in6 for IPv6. These include additional fields for their address family. These specific structures can be cast to sockaddr when used with socket functions.

// System definition
struct sockaddr {
    sa_family_t sa_family;
    char        sa_data[14];
}

// For IPv4
struct sockaddr_in {
    short            sin_family;   // Address family (AF_INET for IPv4)
    unsigned short   sin_port;     // Port number in Network Byte Order
    struct in_addr   sin_addr;     // Internet address
    char             sin_zero[8];  // Padding to make structure the same size as sockaddr
};
  • sin_family: Corresponds to sa_family in sockaddr. Always set to AF_INET for IPv4 addresses.
  • sin_port: This is the port number (in network byte order) associated with the socket. We would use hton() which ensures the port number given from config file is in the correct "byte order". No idea what this means I'll be honest, but we would do something like: hton(8080) for example
  • sin_addr: Dont fully understand what this does, but its the IP address for the socket, I'm not sure if we would have to set this.

Example:

struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // bind to any address
address.sin_port = htons(8080); // port number

bind(sockfd, (struct sockaddr *)&address, sizeof(address));

listen()

Marks the socket referred to by sockfd as a passive socket; that is, as a socket that will be used to accept incoming connection requests using accept()

int listen(int sockfd, int backlog);
  • sockfd: The file descriptor of the socket
  • backlog The max length of the queue of pending connections. Basically its the number of connections that can be waiting while the process is handling a particular connection. Not sure what we sent this to??

Read more here: https://man7.org/linux/man-pages/man2/listen.2.html

accept()

Accepts a connection on a socket

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • sockfd: The file descriptor of the LISTENING socket.
  • addr: A pointer to a sockaddr structure. Again - we would use sockaddr_in for IPv4 etc.
  • addrlen: On input, this specifies the amount of space pointed to by addr. On output, it specifies the actual length (in bytes) of the address returned.

Read more here: https://man7.org/linux/man-pages/man2/listen.2.html

Example:

struct sockaddr_in client_address;
socklen_t client_addrlen = sizeof(client_address);
int new_socket = accept(sockfd, (struct sockaddr *)&client_address, &client_addrlen);

connect()

Initiates a connection on a socket

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd: The file descriptor of the socket.
  • addr: A pointer to a sockaddr structure specifying the address to connect to.
  • addrlen: The size of the address structure.

Example:

int sockfd = socket(AF_INET, SOCK_STREAM, 0); // Create a TCP socket
struct sockaddr_in serv_addr;
    
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80); // HTTP port
// Convert IP address to numerical form so that socket can use it
inet_pton(AF_INET, "93.184.216.34", &serv_addr.sin_addr); // example.com IP

 if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    // Error handling
    perror("Connection failed");
    return 1;
}

// Connection is established; can now send/receive data
close(sockfd); // Close the socket when done

send()/recv(), write(), read()

Used to send and receive data. send() and recv() are used with TCP sockets (SOCK_STREAM), and write() and read() can be used interchangeably.

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

ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
  • sockfd/fd: The file descriptor of the socket.
  • buf: The buffer to send data from or receive data into.
  • len/count: The length of the buffer.
  • flags: A set of flags that modify the behavior of the send/recv operations.

Example:

char message[] = "Hello, world!";
send(sockfd, message, sizeof(message), 0);

char buffer[1024];
recv(sockfd, buffer, 1024, 0);

Clone this wiki locally