πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English

Simple TCP Client & Server in C

Build the minimal programs that exchange text over a TCP socket β€” server side and client side.

What is client & server?

Network programs play one of two roles: the server waits for connections, and the client reaches out to connect. We'll start by running both on the same computer and getting them to talk.
Client
echo_client
⇄
TCP:12345
Server
echo_server

What we'll build (an echo server)

Why echo first? It's the shortest program that proves both send and receive work β€” a classic first program for network beginners.

API flow (both sides)

Server and client use slightly different calls. It helps to learn them side by side.
Server side
  1. socket() β€” create socket
  2. bind() β€” attach to a port
  3. listen() β€” start listening
  4. accept() β€” accept a connection
  5. recv() / send()
  6. close()
Client side
  1. socket() β€” create socket
  2. connect() β€” reach the server
  3. send() / recv()
  4. close()
Key idea: bind/listen/accept are server-only; connect is client-only. Everything else is shared.

Server program (echo_server.c)

A minimal server that echoes back whatever the client sends. It handles one client, then exits.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 12345
#define BUF_SIZE 1024

int main(void) {
    // 1. create socket
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) { perror("socket"); return 1; }

    // (recommended) allow quick reuse of the port
    int yes = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    // 2. prepare address to listen on
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);  // any local IP
    addr.sin_port = htons(PORT);

    // 3. bind β€” attach the socket to the port
    if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind"); return 1;
    }

    // 4. listen β€” start accepting connections
    if (listen(listen_fd, 1) < 0) {
        perror("listen"); return 1;
    }
    printf("Listening on port %d...\n", PORT);

    // 5. accept one client
    struct sockaddr_in client_addr;
    socklen_t clen = sizeof(client_addr);
    int conn_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &clen);
    if (conn_fd < 0) { perror("accept"); return 1; }
    printf("Client connected: %s\n", inet_ntoa(client_addr.sin_addr));

    // 6. echo loop β€” read what arrives, send it back
    char buf[BUF_SIZE];
    ssize_t n;
    while ((n = recv(conn_fd, buf, BUF_SIZE - 1, 0)) > 0) {
        buf[n] = '\0';
        printf("received: %s", buf);
        send(conn_fd, buf, n, 0);  // send the same bytes back
    }

    // 7. clean up
    close(conn_fd);
    close(listen_fd);
    printf("Connection closed\n");
    return 0;
}
INADDR_ANY means "accept on any of my local IPs" β€” fine for development. To restrict to localhost only, use inet_addr("127.0.0.1").

Client program (echo_client.c)

Reads one line from stdin, sends it to the server, then prints the echoed reply.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 12345
#define BUF_SIZE 1024

int main(void) {
    // 1. create socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { perror("socket"); return 1; }

    // 2. target address: localhost:12345
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // 3. connect β€” reach the server
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("connect"); return 1;
    }
    printf("Connected. Type a message then Enter:\n");

    // 4. read one line from stdin and send it
    char msg[BUF_SIZE];
    if (fgets(msg, BUF_SIZE, stdin) == NULL) return 1;
    send(sockfd, msg, strlen(msg), 0);

    // 5. receive the reply
    char buf[BUF_SIZE];
    ssize_t n = recv(sockfd, buf, BUF_SIZE - 1, 0);
    if (n > 0) {
        buf[n] = '\0';
        printf("Server replied: %s", buf);
    }

    // 6. close
    close(sockfd);
    return 0;
}
Note: we're connecting to 127.0.0.1 (localhost), so run the server on the same machine. To reach a different machine, use its IP address.

Build & run

Open two terminals. Start the server in one and the client in the other.

Terminal 1 (server)

$ gcc echo_server.c -o echo_server $ ./echo_server Listening on port 12345... Client connected: 127.0.0.1 received: hello Connection closed

Terminal 2 (client)

$ gcc echo_client.c -o echo_client $ ./echo_client Connected. Type a message then Enter: hello Server replied: hello
Troubleshooting:
β€’ bind: Address already in use β€” another process still holds the port. Wait a moment, or pick a different port.
β€’ connect: Connection refused β€” the server isn't running, or the port/IP is wrong.
β€’ Connects but no reply β€” make sure you started the client after the server reached accept.

Challenges

Challenge 1: Uppercase server
Instead of echoing, uppercase the received string before sending it back. Use toupper from <ctype.h>.
Challenge 2: Repeated exchanges
Modify both sides to keep exchanging messages until the client sends "quit".
Challenge 3: Multiple clients
After accept, call fork() so each client is served by a child process while the parent loops back to accept.
Challenge 4: Port as argument
Let the server accept the port as a command-line argument: ./echo_server 8080. Read it from argv[1]. See the argc/argv lesson.
Next: Build an HTTP client β†’

Review Quiz

Check your understanding of this lesson!

Q1. Which combination of functions does a TCP server use to listen for connections?

bind() / listen() / accept()
send() / recv()
open() / close()

bind attaches the socket to an address/port, listen puts it in listening mode, and accept completes an incoming connection.

Q2. Which function does a client use to connect to a server?

connect()
bind()
accept()

After creating a socket, the client calls connect to reach the server. bind and accept are server-side calls.

Q3. What's the name of the socket-API struct that stores IP address and port together?

struct sockaddr_in
struct ip_addr
struct host

For IPv4 it's sockaddr_in. IPv6 uses sockaddr_in6, and sockaddr is the generic type.

Share this article
Share on X