22

I would like to know the simplest and most effective way to open and write data to a socket in the C programming language for network programming.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
The.Anti.9
  • 43,474
  • 48
  • 123
  • 161

6 Answers6

27

You're right, using sockets in C has a difficult syntax. Later languages like Java and Python make it a snap by comparison. The best tutorial I've found for doing socket programming in C is Beej's Guide to Network Programming. I recommend you start at the beginning to get a good overview, but if you just need to get some code working now, you can skip ahead to the section titled Client-Server Background.

Good luck!

Lorraine
  • 1,189
  • 14
  • 30
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • 2
    This is silly. Sockets are *defined* as a C API, and "later languages" have to make all those C calls at some level. There are "later libraries" in C that will also do it easily, right up to making, say, an HTTP request instead of mucking around with sockets. You can easily have a client function that will take an IP address in host or dot notation as a `char *` string, a port number as an `int`, and return you a `FILE *` stream denoting the connected circuit, or a null pointer with `errno` set to something useful. – Kaz Dec 18 '13 at 23:54
7

POSIX 7 minimal runnable client server TCP example

Get two computers in a LAN, e.g. your home WiFi network.

Run the server on one computer with:

./server.out

Get the IP of the server computer with ifconfig, e.g. 192.168.0.10.

On the other computer, run:

./client.out 192.168.0.10

Now type lines on the client, and the server will return them incremented by 1 (ROT-1 cypher).

server.c

#define _XOPEN_SOURCE 700

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    int enable = 1;
    int i;
    int newline_found = 0;
    int server_sockfd, client_sockfd;
    socklen_t client_len;
    ssize_t nbytes_read;
    struct sockaddr_in client_address, server_address;
    unsigned short server_port = 12345u;

    if (argc > 1) {
        server_port = strtol(argv[1], NULL, 10);
    }

    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }

    server_sockfd = socket(
        AF_INET,
        SOCK_STREAM,
        protoent->p_proto
        /* 0 */
    );
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(server_port);
    if (bind(
            server_sockfd,
            (struct sockaddr*)&server_address,
            sizeof(server_address)
        ) == -1
    ) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(server_sockfd, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    fprintf(stderr, "listening on port %d\n", server_port);

    while (1) {
        client_len = sizeof(client_address);
        client_sockfd = accept(
            server_sockfd,
            (struct sockaddr*)&client_address,
            &client_len
        );
        while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) {
            printf("received:\n");
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n')
                newline_found;
            for (i = 0; i < nbytes_read - 1; i++)
                buffer[i]++;
            write(client_sockfd, buffer, nbytes_read);
            if (newline_found)
                break;
        }
        close(client_sockfd);
    }
    return EXIT_SUCCESS;
}

client.c

#define _XOPEN_SOURCE 700

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    char *server_hostname = "127.0.0.1";
    char *user_input = NULL;
    in_addr_t in_addr;
    in_addr_t server_addr;
    int sockfd;
    size_t getline_buffer = 0;
    ssize_t nbytes_read, i, user_input_len;
    struct hostent *hostent;
    /* This is the struct used by INet addresses. */
    struct sockaddr_in sockaddr_in;
    unsigned short server_port = 12345;

    if (argc > 1) {
        server_hostname = argv[1];
        if (argc > 2) {
            server_port = strtol(argv[2], NULL, 10);
        }
    }

    /* Get socket. */
    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Prepare sockaddr_in. */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
    if (in_addr == (in_addr_t)-1) {
        fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
        exit(EXIT_FAILURE);
    }
    sockaddr_in.sin_addr.s_addr = in_addr;
    sockaddr_in.sin_family = AF_INET;
    sockaddr_in.sin_port = htons(server_port);

    /* Do the actual connection. */
    if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }
    while (1) {
        fprintf(stderr, "enter string (empty to quit):\n");
        user_input_len = getline(&user_input, &getline_buffer, stdin);
        if (user_input_len == -1) {
            perror("getline");
            exit(EXIT_FAILURE);
        }
        if (user_input_len == 1) {
            close(sockfd);
            break;
        }
        if (write(sockfd, user_input, user_input_len) == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }
        while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) {
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n') {
                fflush(stdout);
                break;
            }
        }
    }
    free(user_input);

    exit(EXIT_SUCCESS);
}

On GitHub with a Makefile. Tested on Ubuntu 15.10.

Message length

The read calls on both client and server run inside while loops.

Like when reading from files, the OS may split up messages arbitrarily to make things faster, e.g. one packet may arrive much earlier than the other.

So the protocol must specify a convention of where messages stop. Common methods include:

  • a header with a length indicator (e.g. HTTP Content-Length)
  • an unique string that terminates messages. Here we use \n.
  • the server closes connection: HTTP allows that https://stackoverflow.com/a/25586633/895245. Limited of course since the next message requires a reconnect.

Next steps

This example is limited because:

  • the server can only handle one client connection at a time
  • communication is synchronized simply. E.g.: on a P2P chat app, the server (other person) could send messages at any time.

Solving those problems requires threading and possibly other calls like poll.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
5

You don't mention what platform you are on, but a copy of Unix Network Programming by Stevens would be a good addition to your bookshelf. Most operating systems implement Berkley Sockets using socket, bind, connect, etc.

Brian C. Lane
  • 4,073
  • 1
  • 24
  • 23
2

Unless you write a network daemon, most networking in C can be done at a higher level than using directly the sockets, by using appropriate libraries.

For instance, if you just want to retrieve a file with HTTP, use Neon or libcurl. It will be simpler, it will be at a higher level and you will have gratis SSL, IPv6, etc.

bortzmeyer
  • 34,164
  • 12
  • 67
  • 91
  • 1
    Several down votes and not one comment to explain why. This tells a lot about the technical level of many SO users... – bortzmeyer Jan 01 '09 at 22:22
  • 11
    More likely it's a battle between people who agree with you and people who think you are not answering the question asked. – David Sykes Jul 07 '10 at 14:34
  • If people downvote because they disagree, they can say so in the comments. Replying with a referral (you asked how to do X but I suggest that you do Y instead) is perfectly acceptable when the requirments are not clear (if the OP had said "my manager [or my teacher] required the use of sockets", things would have been different) – bortzmeyer Apr 03 '13 at 15:05
0

Reading and writing from basic sockets is not any harder than reading and writing normal files (just use recv instead of read and send instead if write). Things get a little trickey when you need to open a socket. The reason for that is because there are many different ways to communicate using sockets (TCP, UDP, etc).

SoapBox
  • 20,457
  • 3
  • 51
  • 87
0

I generally write in C++, but you can find some use in a white paper I wrote "How to Avoid the Top Ten Sockets Programming Errors" - ignore the advice to use the ACE toolkit (since it requires C++) but take note of the socket errors in the paper - they're easy to make and hard to find, especially for a beginner. http://www.riverace.com/sockets10.htm

desertnaut
  • 57,590
  • 26
  • 140
  • 166