Socket
- Tags
- networking comp-arch
Is a persistent connection between 2 hosts (possibly the same host) through which data can be passed over a network. It is the network analogy to a file-descriptor or pipes. See socket.
The protocol for networking with UNIX uses the sys/socket.h
libraries. This library
has bindings for several well known protocols including IPv4 and IPv6 alongside UDP
and more. Sockets are declared with both a socket address and a port.
A socket is a file descriptor. You can use it alongside the read
and write
system
calls and also any of the derivatives of these that accept file-descriptors (example
fprintf
).
Procedure
Sockets are the base construct used for both creating a server and a client.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#define port "5336"
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo)); // Zero out hints struct
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int s = getaddrinfo(NULL, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(1);
}
// Create a new socket
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
The process of creating a socket for server is:
- Create a socket.
- Bind the socket onto a port on the local machine.
- Specify the queue size for the socket through listen.
- Accept a connection from a client. This blocks until one comes.
Close the listening socket (this won't close any existing connections).
Note: You should ideally call shutdown prior to close to indicate you don't need to read any more data or write any more data or both. This gives both sides of the connection a chance to close it.
Warn: If you fork after creating a socket all processes need to close the socket before the resource can be reused. Shutting down a socket will shut it down across all processes because the underlying file description is the same.
Note: There are a few differences between a server socket and a client socket. The core difference being a server socket remains open when a peer disconnects. Every accepted connection results in a new file-descriptor used to communicate across that socket and the socket stays open listening for more connections. A server socket waiting for connections is known as a passive socket and one for an established connection is known as an active socket. Warn: A particular server port can have multiple active sockets, with the kernel maintaining a lookup tuple associating unique tuples with active sockets to uniquely identify them, but only one passive socket.
Warn: when closing a socket the port its bound to will not be released until some
time later. In between this duration the port enters a Timed Wait state. To be able
to reuse the port immediately specify SO_REUSPORT
through setsockopt, before binding
to it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#define port "5336"
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo)); // Zero out hints struct
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int s = getaddrinfo(NULL, port, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(1);
}
// Create a new socket
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
// Bind the socket onto an address on the local machine.
if (bind(sock_fd, result->ai_addr, result->ai_addrlen) != 0) {
perror("bind()");
exit(1);
}
// Make the socket listen for incoming connections through binding.
if (listen(sock_fd, port) != 0) {
perror("listen()");
exit(1);
}
struct sockaddr_in *result_addr = (struct sockaddr_in *)result->ai_addr;
printf("Listening on file descriptor %d, port %d\n", sock_fd, ntohs(result_addr->sin_port));
// Accept a connection to the open socket getting a file descriptor for the connection back.
int client_fd = accept(sock_fd, NULL, NULL);
printf("Connection made: client_fd=%d\n", client_fd);
char buffer[1000];
int len = read(client_fd, buffer, sizeof(buffer) - 1);
buffer[len] = '\0';
printf("Read %d chars\n", len);
printf("===\n");
printf("%s\n", buffer);
close(client_fd);
close(sock_fd);
if (connect(sock_fd, result->ai_addr, result->ai_addrlen)) {
printf("Connection with the server failed...\n");
exit(1);
}
printf("Connected to the server...\n");
char buffer[] = "hello world friend";
printf("Sending the message: %s\n", buffer);
write(sock_fd, buffer, sizeof(buffer));
close(sock_fd);