Creating a Simple Socket Server and Client in C
This example introduces the POSIX socket interface through one simple IPv4 connection: create a socket, bind it, listen, accept a client, exchange a message, and close the connection. It uses TCP as its transport, but the focus is the socket lifecycle and the system calls shared by many network programs.
Socket Server
Save this as socket_server.c. It listens only on the port selected at compile time and handles one client before exiting:
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 1080
static int send_all(int fd, const char *data, size_t length)
{
size_t sent = 0;
while (sent < length) {
ssize_t result = send(fd, data + sent, length - sent, 0);
if (result < 0) {
if (errno == EINTR) continue;
return -1;
}
sent += (size_t)result;
}
return 0;
}
int main(void)
{
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
return EXIT_FAILURE;
}
int reuse = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
&reuse, sizeof(reuse)) < 0) {
perror("setsockopt");
close(server_fd);
return EXIT_FAILURE;
}
struct sockaddr_in address = {0};
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind");
close(server_fd);
return EXIT_FAILURE;
}
if (listen(server_fd, 1) < 0) {
perror("listen");
close(server_fd);
return EXIT_FAILURE;
}
printf("Listening on 127.0.0.1:%d\n", PORT);
struct sockaddr_in client_address = {0};
socklen_t client_length = sizeof(client_address);
int client_fd = accept(server_fd, (struct sockaddr *)&client_address,
&client_length);
if (client_fd < 0) {
perror("accept");
close(server_fd);
return EXIT_FAILURE;
}
char buffer[1024];
ssize_t received = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (received < 0) {
perror("recv");
close(client_fd);
close(server_fd);
return EXIT_FAILURE;
}
buffer[received] = '\0';
printf("Client sent %zd bytes: %s\n", received, buffer);
const char reply[] = "Hello from the TCP server\n";
if (send_all(client_fd, reply, strlen(reply)) < 0) {
perror("send");
}
close(client_fd);
close(server_fd);
return EXIT_SUCCESS;
}
Socket Client
Save this as socket_client.c. It connects to loopback, sends a short message, and prints the response:
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 1080
static int send_all(int fd, const char *data, size_t length)
{
size_t sent = 0;
while (sent < length) {
ssize_t result = send(fd, data + sent, length - sent, 0);
if (result < 0) {
if (errno == EINTR) continue;
return -1;
}
sent += (size_t)result;
}
return 0;
}
int main(void)
{
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror("socket");
return EXIT_FAILURE;
}
struct sockaddr_in server_address = {0};
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
if (inet_pton(AF_INET, "127.0.0.1",
&server_address.sin_addr) != 1) {
fprintf(stderr, "Invalid server address\n");
close(socket_fd);
return EXIT_FAILURE;
}
if (connect(socket_fd, (struct sockaddr *)&server_address,
sizeof(server_address)) < 0) {
perror("connect");
close(socket_fd);
return EXIT_FAILURE;
}
const char message[] = "Hello from the TCP client";
if (send_all(socket_fd, message, strlen(message)) < 0) {
perror("send");
close(socket_fd);
return EXIT_FAILURE;
}
char buffer[1024];
ssize_t received = recv(socket_fd, buffer, sizeof(buffer) - 1, 0);
if (received < 0) {
perror("recv");
close(socket_fd);
return EXIT_FAILURE;
}
buffer[received] = '\0';
printf("Server replied with %zd bytes: %s", received, buffer);
close(socket_fd);
return EXIT_SUCCESS;
}
Compile and Run
Compile both programs with warnings enabled:
cc -std=c11 -Wall -Wextra -pedantic -o socket_server socket_server.c
cc -std=c11 -Wall -Wextra -pedantic -o socket_client socket_client.c
Start the server in one terminal, then run the client in another:
./socket_server
./socket_client
The server accepts one connection and exits. Run it again for another test.
Continue: Once this lifecycle is familiar, read TCP Is a Byte Stream: Message Framing in C to handle partial reads, partial writes, and application message boundaries correctly.
What This Example Does Not Solve
- TCP is a byte stream, so one
recvcall is not guaranteed to return one complete application message. - The example handles one client and has no timeout, authentication, encryption, or access control.
- Binding to loopback keeps this experiment local. Binding to all interfaces requires an intentional firewall and threat model.
dispelled