Socket Address Structure

struct sockaddr {
    unsighed short sa_family;
    char sa_data[14];
}

struct sockaddr_in {
    short sin_family;
    unsigned short sin_port; // Port Number
    struct in_addr sin_addr; // IP Address
    char sin_zero[8];
}

struct in_addr {
    unsighed long s_addr; // 4 bytes long
}

Byte Ordering; Big Endian and Little Endian

Each address stores one element of the memory "array". Each element is typically one byte. So it is commonly called byte-addressable. A word means 32 bits data. We can split this into 4 bytes and each byte goes into the one sell of memory. Depending on which direction each byte goes into the memory, there are Big Endian and Little Endian.

Utility Functions

Convert values between host and network byte order:

htons() - short for host to network short
htonl() - short for host to network long
ntohs() - short for network to host short
ntohl() - short for network to host long

Convert IP address format:

inet_aton() - Ascii dotted to binary
inet_ntoa() - Binary to Ascii dotted

syscalls(); library

Socket() - A Connection Endpoint

This creates an endpoint for a network connection

int Socket(int doman, int type, int protocol);
  • domain = PF_INET; For IPv4 communication, domain is always
  • type = SOCK_STREAM(TCP), SOCK_DGRAM(UDP)
  • protocol = 0 (for simplicity)
/*
 * This will create a TCP socket
 * It returns a socket descriptor on success and -1 on an error
 */
socket(PF_INET, SOCK_STREAM, 0);

Bind() - Attaching an IP and Port(My Network) to a Socket

A server process calls bind to attach itself to a specific port and IP address

int Bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
  • sockfd = socket descriptor returned by socket()
  • my_addr = pointer to a valid sockaddr_in structure cast as a sockadd * pointer
  • addrlen = lenghth of the sockaddr_in structure
struct sockaddr_in my;

my.sin_family = PF_INET;
my.sin_port = htons(80);
my.sin_add.s_addr = INADDR_ANY;
bzero(&my, 8);

bind(sock, (struct sockaddr *)&my, sizeof(,my));

Listen() - Wait for a Connection

The server process calls listen to tell the kernel to initialize a wait queue of connections for this socket

int Listen(int sock, int backlog);
  • sock = socket returned by socket()
  • backlog = maximum length of the pending connections queue
// This will allow a maximum of 10 connections to be in pending state
Listen(sock, 10);

Accept() - A New Connection

Accept is called by a Server process to accept new connections from new clients trying to connect to the server

int Accept(int socket, (struct sockaddr *)&client, socklen_t *client_len);
  • socket = the socket in listen state
  • client = will hold the new client's information when accept returns
  • client_len = pointer to size of the client structure
struct sockaddr_in client;
int len = sizeof(client);
Accept(sock, (struct sockaddr *)&client, &len); // empty client struct

Connect() - Connect to a Service

Connect is called by a client to connect to a server port

int Connect(int sock, (struct sockaddr *)&server_addr, socklen_t len);
  • sock = a socket returned by socket()
  • server_addr = a sockaddr_in struct pointer filled with all the remote server details and cast as a sockaddr struct pointer
  • len = size of the server_addr struct
connect(sock, (struct sockaddr *)server_addr, len);

Send / Recv - Data

Send(), Recv(), Read(), Write() etc calls are used to send and receive data. The return value is the number of bytes actually sent/received.

int send(int sock, void *mesg, size_t len, int flags);
int recv(int sock, void *mesg, size_t len, int flags);
  • sock = a connected socket
  • mesg = pointer to a buffer to send/receive data from/in
  • len = size of the message buffer
  • flags = 0 for simplicity
char send_buffer[1024];
char recv_buffer[1024];
int sent_bytes;
int recvd_bytes;

sent_bytes = send(sock, send_buffer, 1024, 0);
recvd_bytes = recv(sock, recv_buffer, 1024, 0);

Close()

int close(int sock);
close(sock);

Server

TCP Server Using Sockets

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>    // socket
#include <sys/socket.h>   // socket
#include <netinet/in.h>   // structure
#include <errno.h>        // perror
#include <string.h>

int main()
{
    int socket, cli;
    struct sockaddr_in server, client;
    unsigned int len; // len should be a positive num
    char mesg[] = "Hello to the world of socket programming!";
    int sent;

    if((sock = socket(AF_INET, SOCK_STREAN, 0)) == -1)
    {
        perror("socket: ");
        exit(-1);
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(10000);
    server.sin_addr.s_addr = INADDR_ANY;
    bzero(&server.sin_zero, 8); // bzero sets all values in a buffer zero

    len = sizeof(struct sockaddr_in);

    if((bind(sock, (struct sockaddr *)&server, len)) == -1)
    {
        perror("bind");
        exit(-1);
    }
    if ((listen(sock, 5)) == -1)
    {
        perror("listen");
        exit(-1);
    }

    while(true)
    {
        // return a client socket descriptor or -1
        if ((cli == accept(sock, (struct sockaddr *)&client, &len)) == -1)
        {
            perror("accept");
            exit(-1);
        }
        sent = send(cli, mesg, strlen(mesg), 0);

        // inet_aton() - Ascii dotted to binary
        printf("Sent %d bytes to client : %s\n", sent, inet_ntoa(client.sin_addr)); 
        close(cli); // server closes the client server
    }    
}

Terminal Command

This will check servers that are running

netstat -atp

Connect server with the port number

telnet localhost 10000

TCP Echo Server Using Sockets

/*
 * Listening on the port number and waiting for a client to connect
 * Once the connection has been established, the client sends messages
 * The server receives it and sends it back to the client
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>    // socket
#include <sys/socket.h>   // socket
#include <netinet/in.h>   // structure
#include <errno.h>        // perror
#include <string.h>
#include <unistd.h>       // sleep
#include <arpa/inet.h>    // inet_addr


#define ERROR    -1
#define MAX_CLIENTS    2
#define MAX_DATA       1024

int main(int argc, char **argv)
{
    struct sockaddr_in server;
    struct sockaddr_in client;
    int sock; // server's socket descriptor
    int new;  // client's socket descriptor
    int socketaddr_len = sizeof(struct sockaddr_in);
    int data_len;
    char data[MAX_DATA];

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == ERROR)
    {
       perror("server socket: ");
       exit(-1);
    }

    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[1])); // port num as an argument; atoi: char to int
    server.sin_addr.s_addr = INADDR_ANY; // this tells your kernel to listen all inferfaces
    bzero(&server.sin_zero, 8);

    if ((bind(sock, (struct sockaddr *)&server, sockaddr_len)) == ERROR)
    {
        perror("bind :");
        exit(-1);
    }

    if ((listen(sock, MAX_CLIENTS)) == ERROR)
    {
        perror("listen");
        exit(-1);
    }

    while(true) // Better signal handling required
    {
        // returns client's socket descriptor which is used to send/receive data
        if ((new = accept(sock, (struct sockaddr *)&client, &sockaddr_len)) == ERROR)

        /*
         * If it complains with an error message "invalid conversion from int to socklen", use this instead
         * if ((cli = accept(sock, (struct sockaddr *)&client, (socklen_t*)&socketaddr_len)) == ERROR)
         */
        {
            perror("accept");
            exit(-1);
        }

        printf("New Client connected from port no %d and IP %s\n", ntohs(client.sin_port), inet_ntoa(client.sin_addr));

        data_len = 1;

        while (data_len)
        {
            // receive data from the client on the socket descriptor new
            // data_len= bytes of data
            data_len = recv(new, data, MAX_DATA, 0);

            // data is not zero send it back to the server
            if (data_len)
            {
                // send data to the client
                send(new, data, data_len, 0);
                data[data_len] = '\0';
                printf("Sent mesg: &s", data);
            }
            printf("Client disconnected\n");

            close(new);
        }
    }
}

Terminal Command

./server port_no

TCP Echo Client Using Sockets

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>    // socket
#include <sys/socket.h>   // socket
#include <netinet/in.h>   // structure
#include <errno.h>        // perror
#include <string.h>
#include <unistd.h>       // sleep
#include <arpa/inet.h>    // inet_addr

#define ERROR -1
#define BUFFER 1024

int main(int argc, char **argv)
{
    struct sockaddr_in remote_server;
    int sock;
    char input[BUFFER]; // user input
    char output[BUFFER]; // from the remote server
    int len;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == ERROR)
    {
        perror("socket");
        exit(-1);
    }

    remote_server.sin_family = AF_INET;
    remote_server.sin_port = htons(atoi(argv[2])); // char->int->network byte order
    remote_server.sin_addr.s_addr = inet_addr(argv[1]);
    bzero(&remote_server.sin_zero, 8);

    if ((connect(sock, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in))) == ERROR)
    {
        perror("connect");
        exit(-1);
    }
    // connection has been made

    while (true)
    {
        // get input from the user
        fgets(input, BUFFER, stdin);

        send(sock, input, strlen(input), 0);

        len = recv(sock, output, BUFFER, 0);
        output[len] = '\0';
        printf("%s\n", output);
    }
    close(sock);   
}

Terminal Command

./client ip port

results matching ""

    No results matching ""