Networking and Sockets
So far, your programs have lived alone. They read from files, talk to the keyboard, print to the screen. All by themselves.
But the real power of computers comes from connecting them. The internet is just millions of computers talking to each other.
This lesson teaches you how to write programs that communicate across a network.
What is Networking?
Networking is computers talking to each other. Your web browser talks to web servers. Your game talks to game servers. Your phone talks to your friend’s phone.
Every time you load a website, your computer:
- Sends a message asking for the page
- Waits for a response
- Receives the page data
That’s networking. Messages going back and forth.
IP Addresses: Phone Numbers for Computers
Every computer on a network has an IP address. It’s like a phone number - it tells other computers how to reach you.
An IP address looks like this: 192.168.1.100
Four numbers from 0 to 255, separated by dots.
Some special addresses:
127.0.0.1- “localhost” - your own computer talking to itself192.168.x.x- Usually computers on your home network10.x.x.x- Also local networks
When you type “google.com” in your browser, your computer looks up Google’s IP address (like looking up a phone number) and then connects to it.
Ports: Apartment Numbers
One computer might run many programs that all want to talk on the network. A web server, an email server, a game - all on the same machine.
Ports tell the computer which program should receive each message. Think of them like apartment numbers. The IP address gets you to the building, the port gets you to the right apartment.
Ports are just numbers from 0 to 65535.
Some well-known ports:
- Port 80 - Web servers (HTTP)
- Port 443 - Secure web servers (HTTPS)
- Port 22 - SSH (secure remote login)
- Port 25 - Email
When your browser connects to google.com, it’s really connecting to google.com:443 (the IP address of Google, port 443).
TCP vs UDP
There are two main ways to send data:
TCP (Transmission Control Protocol):
- Like a phone call
- You establish a connection first
- Messages arrive in order
- If something gets lost, it gets resent
- Reliable but slower
UDP (User Datagram Protocol):
- Like sending postcards
- No connection needed
- Messages might arrive out of order
- If something gets lost, it’s gone
- Fast but unreliable
Most networking uses TCP because you usually want reliability. UDP is used for things like video streaming and games where speed matters more than getting every single packet.
We’ll use TCP in this lesson.
Sockets: The Network Interface
A socket is your program’s connection to the network. Think of it like a phone - you use it to make calls and receive calls.
To make a network connection, you:
- Create a socket
- Connect to an address (for clients) or listen for connections (for servers)
- Send and receive data
- Close the socket when done
Including the Right Headers
Network programming needs special headers. On different systems:
Linux/macOS:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>Windows:
#include <winsock2.h>
#include <ws2tcpip.h>
// Link with ws2_32.libWindows also needs initialization code before using sockets. We’ll show cross-platform examples that work on both.
A Simple Client
Here’s a program that connects to a server and sends a message:
// This example is for Linux/macOS
// See the Windows version below
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
// 1. Create a socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("Couldn't create socket");
return 1;
}
// 2. Set up the server address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(8080); // Port 8080
server.sin_addr.s_addr = inet_addr("127.0.0.1"); // localhost
// 3. Connect to the server
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Couldn't connect");
close(sock);
return 1;
}
printf("Connected!\n");
// 4. Send a message
char *message = "Hello from the client!";
send(sock, message, strlen(message), 0);
printf("Sent: %s\n", message);
// 5. Receive a response
char buffer[1024] = {0};
int bytes = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
printf("Received: %s\n", buffer);
}
// 6. Close the socket
close(sock);
return 0;
}Let’s break down the new parts:
socket(AF_INET, SOCK_STREAM, 0)- Create a TCP socket.AF_INETmeans IPv4,SOCK_STREAMmeans TCP.struct sockaddr_in- Holds an IP address and porthtons(8080)- Converts the port number to network format (computers store numbers differently, this makes it consistent)inet_addr("127.0.0.1")- Converts the IP address string to a numberconnect()- Connects to the serversend()- Sends datarecv()- Receives dataclose()- Closes the socket
A Simple Server
A server waits for clients to connect:
// This example is for Linux/macOS
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
// 1. Create a socket
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0) {
perror("Couldn't create socket");
return 1;
}
// Allow reusing the address (helpful during development)
int opt = 1;
setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 2. Set up our address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(8080);
server.sin_addr.s_addr = INADDR_ANY; // Accept connections on any interface
// 3. Bind to the port
if (bind(server_sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Couldn't bind");
close(server_sock);
return 1;
}
// 4. Start listening
listen(server_sock, 5); // Allow up to 5 waiting connections
printf("Server listening on port 8080...\n");
// 5. Accept a connection
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
int client_sock = accept(server_sock, (struct sockaddr *)&client, &client_len);
if (client_sock < 0) {
perror("Couldn't accept");
close(server_sock);
return 1;
}
printf("Client connected!\n");
// 6. Receive a message
char buffer[1024] = {0};
int bytes = recv(client_sock, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
printf("Received: %s\n", buffer);
// 7. Send a response
char *response = "Hello from the server!";
send(client_sock, response, strlen(response), 0);
}
// 8. Close connections
close(client_sock);
close(server_sock);
return 0;
}The server flow is:
- Create a socket
- Bind to a port (claim that port number)
- Listen for connections
- Accept a connection (this creates a new socket just for that client)
- Send/receive data
- Close
Windows Version
Windows uses slightly different functions:
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main(void) {
// Windows needs initialization
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
printf("WSAStartup failed\n");
return 1;
}
// Create socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
printf("Couldn't create socket\n");
WSACleanup();
return 1;
}
// Set up server address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(8080);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
// Connect
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
printf("Couldn't connect\n");
closesocket(sock);
WSACleanup();
return 1;
}
// Send and receive...
// Clean up
closesocket(sock); // Windows uses closesocket, not close
WSACleanup(); // Clean up Winsock
return 0;
}Key differences:
- Need
WSAStartup()at the beginning - Need
WSACleanup()at the end - Use
closesocket()instead ofclose() - Use
SOCKETtype instead ofint
A Simple Chat Program
Let’s put it together into a chat program. Run the server first, then the client:
Server (server.c):
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = INADDR_ANY
};
bind(server_sock, (struct sockaddr *)&server, sizeof(server));
listen(server_sock, 1);
printf("Waiting for connection...\n");
struct sockaddr_in client;
socklen_t len = sizeof(client);
int client_sock = accept(server_sock, (struct sockaddr *)&client, &len);
printf("Client connected!\n\n");
char buffer[1024];
while (1) {
// Wait for client message
memset(buffer, 0, sizeof(buffer));
int bytes = recv(client_sock, buffer, sizeof(buffer) - 1, 0);
if (bytes <= 0) break;
printf("Client: %s\n", buffer);
// Send our response
printf("You: ");
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = 0; // Remove newline
if (strcmp(buffer, "quit") == 0) break;
send(client_sock, buffer, strlen(buffer), 0);
}
close(client_sock);
close(server_sock);
printf("Chat ended.\n");
return 0;
}Client (client.c):
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = inet_addr("127.0.0.1")
};
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("Couldn't connect");
return 1;
}
printf("Connected to server!\n\n");
char buffer[1024];
while (1) {
// Send our message
printf("You: ");
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = 0;
if (strcmp(buffer, "quit") == 0) break;
send(sock, buffer, strlen(buffer), 0);
// Wait for response
memset(buffer, 0, sizeof(buffer));
int bytes = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (bytes <= 0) break;
printf("Server: %s\n", buffer);
}
close(sock);
printf("Chat ended.\n");
return 0;
}To use:
- Compile both:
gcc -o server server.candgcc -o client client.c - Run the server in one terminal:
./server - Run the client in another terminal:
./client - Type messages! Type “quit” to exit.
Error Handling
Real network code needs lots of error checking. Connections drop, servers go away, data gets corrupted.
int bytes = recv(sock, buffer, sizeof(buffer), 0);
if (bytes < 0) {
perror("Error receiving");
} else if (bytes == 0) {
printf("Connection closed by other side\n");
} else {
// Process the data
}Always check return values. Network operations fail more often than file operations.
What You’ve Learned
- IP addresses identify computers
- Ports identify programs on a computer
- TCP gives reliable, ordered delivery
- Sockets are your interface to the network
- Servers listen and accept connections
- Clients connect to servers
send()andrecv()transfer data
The Bigger Picture
The internet is built on these simple ideas. Every website, every online game, every chat app - they all use sockets underneath.
HTTP (the web) is just text messages sent over TCP connections. When your browser loads a page, it’s doing exactly what we did: connect, send a request, receive a response.
Now you know how it works.
Try It Yourself
- Modify the chat program so the server can talk to multiple clients
- Make a program that downloads a webpage (connect to port 80, send an HTTP request)
- Create a simple file transfer program
- Add timestamps to the chat messages
Common Mistakes
- Forgetting to call
htons()on port numbers - Not checking return values (network operations fail!)
- Forgetting Windows needs
WSAStartup()andWSACleanup() - Using
close()on Windows (needclosesocket()) - Assuming data arrives all at once (it might come in chunks)
Next Up
In Part 24, we’ll learn about command line arguments - how to make your programs accept input when they start, like real command line tools.
Enjoyed This?
If this helped something click, subscribe to my YouTube channel. More content like this, same approach - making things stick without insulting your intelligence. It’s free, it helps more people find this stuff, and it tells me what’s worth making more of.