C - TCP/IP - Writing and reading on a socket

This tutorial will help us to understand how to write and read on a client side and display the result on the server side.

So, for that, we have to create a server and a client.
The client will be the nc tool (or telnet if you prefer, this is the same for this example).

Let's see this with an example.

Writing and reading on a socket

server.c

/* server.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>


#include "h.h"

void my_socket(t_s *s)
{
    s->name    = "TCP";
    s->domain    = AF_INET;
    s->type    = SOCK_STREAM;
    s->pe        = getprotobyname(s->name);
    s->fd        = socket(s->domain, s->type, s->pe->p_proto);
    check_error(s->fd, -1);
}

void my_bind(t_s *s, int port)
{
    s->sockfd                = s->fd;
    s->addr.sin_family        = s->domain;
    s->addr.sin_addr.s_addr = INADDR_ANY;
    s->addr.sin_port        = htons(port);
    s->addrlen                = sizeof(s->addr);
    s->bindValue            = bind(s->fd, (const struct sockaddr *)&s->addr, s->addrlen);
    check_error(s->bindValue, -1);
}

void my_listen(t_s *s, int backlog)
{
    s->listenValue = listen(s->sockfd, backlog);
    check_error(s->listenValue, -1);
}

void my_accept(t_s *s)
{
    s->addrlenAccept = sizeof(s->addrAccept);
    s->acceptValue = accept(s->sockfd, (struct sockaddr *)&s->addrAccept, &s->addrlenAccept);
    check_error(s->acceptValue, -1);
    s->acceptAddr = inet_ntoa(s->addrAccept.sin_addr);
    check_null(s->acceptAddr);
}

void my_write(t_s *s)
{
    const char *message = "Hello, please enter your name: ";
    s->writeValue = write(s->acceptValue, message, strlen(message));
    check_error(s->writeValue, -1);
}

void my_read(t_s *s)
{
    bzero(s->buf,256);
    s->readValue = read(s->acceptValue, s->buf, BUF_SIZE);
    check_error(s->readValue, -1);
}

int main(int ac, char *av[])
{
    t_s s;

    check_arg(ac, ARG_SIZE);
    my_socket(&s);
    my_bind(&s, atoi(av[1]));
    my_listen(&s, BACKLOG);
    my_accept(&s);
    my_write(&s);
    my_read(&s);
    debug(&s);
    check_error(close(s.acceptValue), -1);
    check_error(close(s.fd), -1);
    return 0;
}


debug.c

/* debug.c */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#include "h.h"

void debug(t_s *s)
{
    printf("\n===== socket() =====\n");
    printf("domain = %d\n", s->domain);
    printf("type = %d\n", s->type);
    printf("fd = %d\n", s->fd);
    printf("name = %s\n", s->name);
    printf("p_proto = %d\n", s->pe->p_proto);

    printf("\n===== bind() =====\n");
    printf("sockfd = %d\n", s->sockfd);
    printf("sin_family = %d\n", s->addr.sin_family);
    printf("sin_addr.s_addr = %d\n", s->addr.sin_addr.s_addr);
    printf("sin_port = %d\n", s->addr.sin_port);
    printf("addrlen = %d\n", s->addrlen);
    printf("bindValue = %d\n", s->bindValue);

    printf("\n===== listen() =====\n");
    printf("listenValue = %d\n", s->listenValue);

    printf("\n===== accept() =====\n");
    printf("addrlenAccept = %d\n", s->addrlenAccept);
    printf("acceptValue = %d\n", s->acceptValue);
    printf("s->acceptAddr = %s\n", s->acceptAddr);

    printf("\n===== write() & read() =====\n");
    printf("s->buf = %s\n\n", s->buf);
}

void check_error(int test, int error)
{
    if (test == error)
    {
        fprintf(stderr, "ERROR: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
}

void check_null(char *test)
{
    if (test == NULL)
    {
        fprintf(stderr, "ERROR: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
}

void check_arg(int ac, int number)
{
    if (ac < number)
    {
        printf("Usage: ./server [PORT]\n");
        exit(EXIT_FAILURE);
    }
}

h.h

#ifndef H_H_
#define H_H_

#include <netdb.h>

/**
 * Define
 */
#define ARG_SIZE 2
#define BACKLOG 10
#define BUF_SIZE 255

/**
 * Structure
 */
typedef struct mystruct
{
    /* socket */
    int domain;
    int type;
    int fd;
    char *name;
    struct protoent *pe;

    /* bind */
    int sockfd;
    socklen_t addrlen;
    struct sockaddr_in addr;
    int bindValue;

    /* listen */
    int listenValue;

    /* accept */
    int acceptValue;
    socklen_t addrlenAccept;
    struct sockaddr_in addrAccept;
    char *acceptAddr;

    /* read & write */
    int readValue;
    int writeValue;
    char buf[256];
} t_s;

/**
 * Prototype
 */
void debug(t_s *);
void check_error(int, int);
void check_null(char *);
void check_arg(int, int);

#endif /* H_H_ */

Compiling

$ gcc server.c debug.c -o server ; ./server 5000

Now that the server is launched, it's waiting a connection from a client on the port 5000.

For that we need to start another shell (it will be our client side).
We have to specified the address and the port.
As we are in local (on our system), we can use the localhost alias or if it doen't work, its real value : 127.0.0.1.

So let's type with nc:

$ nc localhost 5000

The following text appears on the client side:

Hello, please enter your name:

Type your name, for example: John Smith.
Then press ENTER.

Result

At the end of the display, we can see your name.

===== socket() =====
domain = 2
type = 1
fd = 3
name = TCP
p_proto = 6

===== bind() =====
sockfd = 3
sin_family = 2
sin_addr.s_addr = 0
sin_port = 35347
addrlen = 16
bindValue = 0

===== listen() =====
listenValue = 0

===== accept() =====
addrlenAccept = 16
acceptValue = 4
s->acceptAddr = 127.0.0.1

===== write() & read() =====
s->buf = John Smith

Good job! kiss

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.