<!--order=4-->


<h1>Creating a Simple TCP Client and Server in C</h1>
<p>This example demonstrates the basic lifecycle of one IPv4 TCP connection: create a socket, bind it, listen, accept a client, exchange a message, and close the connection. The original filename called this a SOCKS5 server, but it is not a SOCKS5 proxy. It does not send the SOCKS5 greeting or implement a CONNECT request.</p>

<aside>
  <strong>Important:</strong> Run this on a trusted test network or on loopback. This is a learning example, not a production server, proxy, or security boundary.
</aside>

<h2>TCP Server</h2>
<p>Save this as <code>tcp_server.c</code>. It listens only on the port selected at compile time and handles one client before exiting:</p>
<pre><code class="language-c">#include &lt;arpa/inet.h&gt;
#include &lt;errno.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;unistd.h&gt;

#define PORT 1080

static int send_all(int fd, const char *data, size_t length)
{
    size_t sent = 0;

    while (sent &lt; length) {
        ssize_t result = send(fd, data + sent, length - sent, 0);
        if (result &lt; 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 &lt; 0) {
        perror("socket");
        return EXIT_FAILURE;
    }

    int reuse = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR,
                   &amp;reuse, sizeof(reuse)) &lt; 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 *)&amp;address, sizeof(address)) &lt; 0) {
        perror("bind");
        close(server_fd);
        return EXIT_FAILURE;
    }

    if (listen(server_fd, 1) &lt; 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 *)&amp;client_address,
                           &amp;client_length);
    if (client_fd &lt; 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 &lt; 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)) &lt; 0) {
        perror("send");
    }

    close(client_fd);
    close(server_fd);
    return EXIT_SUCCESS;
}</code></pre>

<h2>TCP Client</h2>
<p>Save this as <code>tcp_client.c</code>. It connects to loopback, sends a short message, and prints the response:</p>
<pre><code class="language-c">#include &lt;arpa/inet.h&gt;
#include &lt;errno.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;unistd.h&gt;

#define PORT 1080

static int send_all(int fd, const char *data, size_t length)
{
    size_t sent = 0;

    while (sent &lt; length) {
        ssize_t result = send(fd, data + sent, length - sent, 0);
        if (result &lt; 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 &lt; 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",
                  &amp;server_address.sin_addr) != 1) {
        fprintf(stderr, "Invalid server address\n");
        close(socket_fd);
        return EXIT_FAILURE;
    }

    if (connect(socket_fd, (struct sockaddr *)&amp;server_address,
                sizeof(server_address)) &lt; 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)) &lt; 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 &lt; 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;
}</code></pre>

<h2>Compile and Run</h2>
<p>Compile both programs with warnings enabled:</p>
<pre><code class="language-sh">cc -std=c11 -Wall -Wextra -pedantic -o tcp_server tcp_server.c
cc -std=c11 -Wall -Wextra -pedantic -o tcp_client tcp_client.c</code></pre>
<p>Start the server in one terminal, then run the client in another:</p>
<pre><code class="language-sh">./tcp_server
./tcp_client</code></pre>
<p>The server accepts one connection and exits. Run it again for another test.</p>

<h2>What This Example Does Not Solve</h2>
<ul>
  <li>TCP is a byte stream, so one <code>recv</code> call is not guaranteed to return one complete application message.</li>
  <li>The example handles one client and has no timeout, authentication, encryption, or access control.</li>
  <li>A real SOCKS5 proxy must implement RFC 1928 negotiation, optional authentication, CONNECT or other commands, and bidirectional forwarding.</li>
  <li>Binding to loopback keeps this experiment local. Binding to all interfaces requires an intentional firewall and threat model.</li>
</ul>

<h2>References</h2>
<ul>
  <li><a href="https://man7.org/linux/man-pages/man2/socket.2.html" target="_blank" rel="noopener">Linux socket(2) manual page ↗</a></li>
  <li><a href="https://man7.org/linux/man-pages/man2/accept.2.html" target="_blank" rel="noopener">Linux accept(2) manual page ↗</a></li>
  <li><a href="https://www.rfc-editor.org/rfc/rfc1928" target="_blank" rel="noopener">RFC 1928 — SOCKS Protocol Version 5 ↗</a></li>
</ul>