Author: Svetoslav P. Chukov hydra@nhydra.org

Contents

  1. Introduction
  2. Used parts of BSD Sockets API
    1. Functions list
      1. int accept (int socket, struct sockaddr addr, socklen_t length_ptr)
      2. int bind (int socket, struct sockaddr *addr, socklen_t length)
      3. int connect (int socket, struct sockaddr *addr, socklen_t length)
      4. uint16_t htons (uint16_t hostshort)
      5. uint32_t htonl (uint32_t hostlong)
      6. int listen (int socket, unsigned int n)
      7. int read (int socket, void *buffer, size_t size)
      8. int send (int socket, void *buffer, size_t size, int flags)
      9. int shutdown (int socket, int how)
      10. int socket (int namespace, int style, int protocol)
    2. Structures and data types
      1. struct sockaddr
      2. struct sockaddr_in
  3. Programming steps in a simple examples
    1. Create the socket
      1. Network socket
      2. Local socket
    2. Initialize the socket structure and make a socket address
    3. Client Side specific options
      1. Connect to the server
    4. Server Side specific options
      1. Binding the socket
      2. Listening for incoming connections
      3. Accepting connections
    5. Transferring data
      1. Sending
      2. Receiving
    6. Advanced tricks
  4. Full example source code
    1. network.h
    2. network.c
    3. server.c
    4. client.c
  5. How to compile it?
  6. Description

Introduction

Used parts of BSD Sockets API

Functions list

int accept (int socket, struct sockaddr addr, socklen_t length_ptr)

This function is used to accept a connection request on the server socket socket. The accept function waits if there are no connections pending, unless the socket socket has nonblocking mode set. (You can use select to wait for a pending connection, with a nonblocking socket.) See File Status Flags, for information about nonblocking mode.

The addr and length-ptr arguments are used to return information about the name of the client socket that initiated the connection. See Socket Addresses, for information about the format of the information.

Accepting a connection does not make socket part of the connection. Instead, it creates a new socket which becomes connected. The normal return value of accept is the file descriptor for the new socket.

After accept, the original socket socket remains open and unconnected, and continues listening until you close it. You can accept further connections with socket by calling accept again.

If an error occurs, accept returns -1.

int bind (int socket, struct sockaddr *addr, socklen_t length)

The bind function assigns an address to the socket socket. The addr and length arguments specify the address; the detailed format of the address depends on the namespace. The first part of the address is always the format designator, which specifies a namespace, and says that the address is in the format of that namespace.

The return value is 0 on success and -1 on failure.

int connect (int socket, struct sockaddr *addr, socklen_t length)

The connect function initiates a connection from the socket with file descriptor socket to the socket whose address is specified by the addr and length arguments. (This socket is typically on another machine, and it must be already set up as a server.) See Socket Addresses, for information about how these arguments are interpreted.

Normally, connect waits until the server responds to the request before it returns. You can set nonblocking mode on the socket socket to make connect return immediately without waiting for the response. See File Status Flags, for information about nonblocking mode.

The normal return value from connect is 0. If an error occurs, connect returns -1.

uint16_t htons (uint16_t hostshort)

This function converts the uint16_t integer hostshort from host byte order to network byte order.

uint32_t htonl (uint32_t hostlong)

This function converts the uint32_t integer hostlong from host byte order to network byte order.

This is used for IPv4 Internet addresses.

int listen (int socket, unsigned int n)

The listen function enables the socket socket to accept connections, thus making it a server socket.

The argument n specifies the length of the queue for pending connections. When the queue fills, new clients attempting to connect fail with ECONNREFUSED until the server calls accept to accept a connection from the queue.

The listen function returns 0 on success and -1 on failure.

int read (int socket, void *buffer, size_t size)

If nonblocking mode is set for socket, and no data are available to be read, read fails immediately rather than waiting. See File Status Flags, for information about nonblocking mode.

This function returns the number of bytes received, or -1 on failure.

int send (int socket, void *buffer, size_t size, int flags)

The send function is like write, but with the additional flags flags. The possible values of flags are described in Socket Data Options.

This function returns the number of bytes transmitted, or -1 on failure. If the socket is nonblocking, then send (like write) can return after sending just part of the data. See File Status Flags, for information about nonblocking mode.

Note, however, that a successful return value merely indicates that the message has been sent without error, not necessarily that it has been received without error.

int shutdown (int socket, int how)

The shutdown function shuts down the connection of socket socket. The argument how specifies what action to perform:

to be sent. Stop looking for acknowledgement of data already sent; don't retransmit it if it is lost.

The return value is 0 on success and -1 on failure.

int socket (int namespace, int style, int protocol)

This function creates a socket and specifies communication style style, which should be one of the socket styles listed in Communication Styles. The namespace argument specifies the namespace; it must be PF_LOCAL (see Local Namespace) or PF_INET (see Internet Namespace). protocol designates the specific protocol (see Socket Concepts); zero is usually right for protocol.

The return value from socket is the file descriptor for the new socket, or -1 in case of error.

The file descriptor returned by the socket function supports both read and write operations. However, like pipes, sockets do not support file positioning operations.

Structures and data types

struct sockaddr

The struct sockaddr type itself has the following members:

The corresponding namespace designator symbol PF_UNSPEC exists for completeness, but there is no reason to use it in a program.

struct sockaddr_in

This is the data type used to represent socket addresses in the Internet namespace. It has the following members:

When you call bind or getsockname, you should specify sizeof (struct sockaddr_in) as the length parameter if you are using an IPv4 Internet namespace socket address.

Programming steps in a simple examples

Create the socket

Before to use any socket you have to create it. This can be done via socket () function. There is two general types of sockets. A network socket and local socket. TAKE A LOOK ABOUT THE FOLLOWING LINES HOW TO CREATE THE SOCKET.

Network socket

Network socket is used about connecting to a network. Here you are some example how to do that:

int sock;
sock = socket ( PF_INET, SOCK_STREAM, IPPROTO_TCP );

The "PF_INET" argument specifies that the socket will be internet socket. Let's take a look about any of the arguments.

PF_INET - specifies the socket type (in our case - internet socket) SOCK_STREAM - specifies that the connection will be via stream. IPPROTO_TCP - the used protocol will be TCP.

In the above example we make a internet socket with TCP packets, simple and easy ...

Local socket

Local socket is used about local connecting. It is used in Interprocess communication Here you are some example how to do that:

int sock;
sock = socket ( PF_LOCAL, SOCK_DGRAM, 0 );

The "PF_INET" argument specifies that the socket will be internet socket. Let's take a look about any of the arguments.

PF_LOCAL - specifies the socket type (in our case - local socket) SOCK_DGRAM - specifies that the connection will be via diagrams. 0 - no protocol available.

Initialize the socket structure and make a socket address

After creating of the socket we have to initialize the socket structure to make it available. Well, here is how to do that:

struct sockaddr_in ServAddr;
const char * servIP;
int  ServPort;


memset(&ServAddr, 0, sizeof(ServAddr));
ServAddr.sin_family = AF_INET;
ServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServAddr.sin_port = htons ( port );

This example create internet socket and accepts ALL CONNECTIONS from ANY ADDRESS. Let's have a closer look about the above lines.

This line make memset for ServAddr structure. This structure holds the server address and all information needed about the socket work.

memset(&ServAddr, 0, sizeof(ServAddr));

This line put the socket family. So, as i said above the socket can be internet and local. It this example it is internet, because of that we put AF_INET.

ServAddr.sin_family = AF_INET;

The following line specifies that we accept connections from ANY address.

ServAddr.sin_addr.s_addr = htonl(INADDR_ANY);

s_addr is variable that holds the information about the address we agree to accept. So, in this case i put INADDR_ANY because i would like to accept connections from any internet address. This case is used about server example. In a client example i could NOT accept connections from ANY ADDRESS.

After all above stuffs the next important thing is the PORT. all internet sockets need a Input-Output PORT to make a connection. You can take any port that you want the required condition is that port to be free. So, in other words it must be available for us.

NOTE: Some ports need ROOT rights to opened. If this port is compromised it put entire Operating System on risk.

So, i suggest using a NONE ROOT PORTS that are pretty much available in the computer. These ports start from 1500 - 65536. Well, you have all these ports available for ordinary NONE ROOT users.

Here is the example about the port initializing.

ServAddr.sin_port = htons ( port );

Let me describe the above line. So, the "port" is a "integer variable". You can do it in this way:

ServAddr.sin_port = htons ( 10203 );

This above line will open port 10203 for connections.

Client Side specific options

Connect to the server

The client can be connected to the server via connect() function. That function takes our current socket ( it is a client in that case ) and the server socket and connect them both. Here is the example code:

connect(sock, (struct sockaddr *) &ServAddr, sizeof(ServAddr))

To be everything good and everybody to be happy do the following code:

if (connect(sock, (struct sockaddr *) &ServAddr, sizeof(ServAddr)) < 0) {
    printf("connect() failed\n");
}

This code make sure that we have available connection. If the connection failed then the connect function will return -1 and the logic will print the error message "connect() failed". If the function succeeded there is an available and ready for using connection to the server

Server Side specific options

Binding the socket

After the all preparations the next important step is socket binding. This will bind the socket address and the created socket. So, the address will be connected to the socket. If you miss this stuff you can not use your socket. Because it will have no address to access it. This is like the building and the street number. If you don't know street number you could not find the building you want...

Well, here is the example how to do that:

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

The bind function is very important because it will make your socket available for using. So, better way to do above is to import some processing logic to make sure yourself that is really available socket.

This will do binding of the socket but will check for errors and if the binding failed ... the logic will do exit.

if ( bind ( sock, ( struct sockaddr * ) &ServAddr, sizeof ( ServAddr ) ) < 0 ) {
    perror ( "bind" );
    exit ( EXIT_FAILURE );
}

Listening for incoming connections

listen(sock, MAXPENDING);

The second argument specifies the length of the queue for pending connections. So, if you want to use 5 pending connections you can do it in this way:

listen (sock, 5);

This marks the socket is listening and ready to accept incoming connections.

Accepting connections

The accepting of the connections goes throw some steps... First of all it needs to make a structure with type sockaddr_in to hold client address. After that have to make a variable to hold the client length. Then i put the length of the client to this variable. So, i make sure that there is enough data

Here is the example code:

struct sockaddr_in ClntAddr;
unsigned int clntLen; 


clntLen = sizeof(ClntAddr);
clntSock = accept(servSock, (struct sockaddr *) &ClntAddr, &clntLen))

Transferring data

Sending

When the sockets are connected the next step is just .... using of this connection. :) Sending of data can be established via send() or write() functions.

Here is the example:

send(sock, "\n", StringLen, 0);

This is simple example how to send a "\n" character to the server. We can send any information. Characters, symbols, any data that have to be sent...

Let me describe the above example... So, the first argument take a socket variable. The second argument take the data that will be sent.. and the 3rd argument is an integer variable that will specify how long is the data sent. The last argument is for additional options, if you don't need that just put 0 - like me. :)

NOTE: The variable "sock" is a socket that will be used. But this is your socket, not the socket of the server... I think this can confuse someone. So, i assume your make a network client and for that reason you make a client socket. That's good but throw this client socket you do all the communications. Have a closer look and see the difference between these 2 sockets. You use the client socket not the server one.. the server socket is for the server.

Just wanted to be clear because some people make mistake when they make a server and client sides.

Receiving

When some data is sent from the other side someone wait to receive it... So, this is not so hard. Here is a simple example:

recv(sock, recvBuffer, 256)

The receiving is like sending - simple and easy. The first argument takes a socket variable, the second variable takes the BUFFER for storing incoming data and the last one takes the integer variable that specifies the length of the incoming data.

So, when you put 256 the read() function will read 256 bytes from the incoming data and it will exit when the data is more or find the symbol "END OF DATA".

IMPORTANT: Reserve BUFFER as the same or larger of the length you specify as read data. DO NOT specify buffer that is smaller of the read data. If you do that you will get "SEGMENTATION FAULT" error message and YOUR PROGRAM WILL TERMINATE.

NOTE: The variable "sock" is a socket that will be used. But this is your socket, not the socket of the server... I think this can confuse someone. So, i assume your make a network client and for that reason you make a client socket. That's good but throw this client socket you do all the communications. Have a closer look and see the difference between these 2 sockets. You use the client socket not the server one.. the server socket is for the server.

Just wanted to be clear because some people make mistake when they make a server and client sides.

Advanced tricks

There is a huge amount of advanced tricks in the BSD sockets... This is the main tricks:

Full example source code

network.h

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

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

#define ADRESS_PORT           10203
#define ADRESS_IP             "127.0.0.1"
#define MAXPENDING            5
#define BUFFSIZE              21

#define SERVER_SOCKET         1
#define CLIENT_SOCKET         0

#define TRUE                  1
#define FALSE                 0
#define START                 11
#define DIVIDER               ":"

network.c

#include "network.h"

int make_socket ( uint16_t port, int type, const char * server_IP )
{
    int sock;
    struct hostent *    hostinfo = NULL;
    struct sockaddr_in  server_address;

    /* Create the socket. */
    sock = socket ( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if (sock < 0) {
        perror ( "socket" );
        exit ( 1 );
    }

    /* Give the socket a name. */
    memset(&server_address, 0, sizeof(server_address));  
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons ( port );

    if ( type == SERVER_SOCKET ) {
        server_address.sin_addr.s_addr = htonl(INADDR_ANY);
        if ( bind ( sock, ( struct sockaddr * ) &server_address, sizeof ( server_address ) ) < 0 ) {
            perror ( "bind" );
            exit ( 1 );
        }

        if ( listen(sock, MAXPENDING) < 0 ) {
            printf("listen() failed");
        }
    } else if ( type == CLIENT_SOCKET ) {
        server_address.sin_addr.s_addr = inet_addr(server_IP);

        /* Establish the connection to the server */
        if (connect(sock, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
            printf("connect() failed\n");
        }
    }
    return sock;
}

void close_socket (int socket)
{
    close (socket);
}

char * clean_data( const char * data )
{
    int count;
    char * ptr_data      = NULL;
    char * result_data   = NULL;
    char * temp_ptr_data = NULL;
    int len;
    int write_info, ifone;

    ptr_data = strstr (data, DIVIDER);
    ptr_data =& ptr_data[strlen(DIVIDER)];

    temp_ptr_data = malloc ( strlen (ptr_data) );
    strcpy (temp_ptr_data, ptr_data);
    result_data = (char *) strsep (&temp_ptr_data, DIVIDER);
    printf ("%i, %i, %s", strlen (data), strlen (ptr_data), result_data);
    return result_data;
}

void send_data ( int socket, const char * data )
{
    int sent_bytes, all_sent_bytes;
    int err_status;
    int sendstrlen;

    sendstrlen = strlen ( data );
    all_sent_bytes = 0;

    sent_bytes = send ( socket, data, sendstrlen, 0 );
    all_sent_bytes = all_sent_bytes + sent_bytes;
    printf ("\t !!! Sent data: %s --- \n", data);
}

server.c

#include "network.h"

int accept_connection(int server_socket)
{
    int client_socket;                          /* Socket descriptor for client */
    struct sockaddr_in client_address;          /* Client address */
    unsigned int client_length;                 /* Length of client address data structure */

    /* Set the size of the in-out parameter */
    client_length = sizeof(client_address);

    /* Wait for a client to connect */
    if ((client_socket = accept(server_socket, (struct sockaddr *) &client_address, &client_length)) < 0) {
        printf("accept() failed");
    }

    /* client_socket is connected to a client! */
    printf("Handling client %s\n", inet_ntoa(client_address.sin_addr));

    return client_socket;
}

void handle_client (int client_socket)
{
    char buffer [BUFFSIZE];           /* Buffer for incomming data */
    int msg_size;                     /* Size of received message  */
    int bytes, all_bytes;

    do {
        alarm (60);
        msg_size = read (client_socket, buffer, BUFFSIZE);
        alarm (0);

        if ( msg_size <= 0 ) {
            printf ( " %i ", msg_size );
            printf ( "End of data\n" );
        }
    } while ( msg_size > 0 );
    printf ("Data received: %s", buffer);
    bytes = 0;
}

int main()
{
    int clnt_sock;
    int sock = make_socket(ADRESS_PORT, SERVER_SOCKET, "none");
    clnt_sock = accept_connection (sock);
    handle_client(clnt_sock);
    close_socket(sock);
    return 0;
}

client.c

#include "network.h"

int main()
{
    int sock = make_socket(ADRESS_PORT, CLIENT_SOCKET, "10.35.43.41");
    send_data (sock, "Some data to be sent");
    close_socket(sock);
    return 0;
}

How to compile it?

Compile via cc:

cc network.c server.c -o server_example     (server)
cc network.c client.c -o client_example     (client)

To compile and use the sockets just have to include the main "include" files. If you don't know which are they ... here you are:

Just import these lines in the beginning of your program .h files.

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

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

Include these and should have no problems...

Description

BSD sockets are the base part of the networks and internet. The entire HOW-TO is specified about the BSD socket programming but it could be used by other programmers too. Well, the sockets are the same in all operating systems.

In the general case this HOW-TO will describe about Sockets programming in all NIX-like operating systems. This include GNU/Linux, BSD, OpenSolaris and others.

Add a comment