Beginning Socket Programming – API Introduction

Home / Beginning Socket Programming – API Introduction

In previous article, we have discussed some basic for network programming such as IP, port number, and byte order of computer. In this article we will look some API and its basic usage.

Converting Byte Order

We have know that each computer might have different endian but the network infrastructure ensure format in network order. To convert our host order to network order (or vice versa) we can use some function:

htons()
htonl()
ntohs()
ntohl()

Some Structs

We will discussed some struct used here. Try to remember them, okay?

struct addrinfo – This structure is more recent invention and is used to prepare the socket address structures for subsequent use. It’s also used in host name lookups, and service name lookups.

struct addrinfo {
   int              ai_flags;     // AI_PASSIVE, AI_CANONNAME, etc
   int              ai_family;    // AF_INET, AF_INET6, AF_UNSPEC
   int              ai_socktype;  // SOCK_STREAM, SOCK_DGRAM
   int              ai_protocol   // use 0 for "any"
   size_t           ai_addrlen;   // size of ai_addr in bytes
   struct sockaddr *ai_addr;      // can be struct sockaddr_in or sockaddr_in6
   char            *ai_canonname; // full canonical hostname
   struct addrinfo *ai_next;	  // linked list, next node
};

Load this struct up a bit and then call getaddrinfo(). It’ll return a pointer to a new linked list of these structures filled out with all the information needed. We can force it to use IPv4 or IPv6 or leave it as AF_UNSPEC to use whatever.

struct sockaddr – This structure hold information about address.

struct sockaddr {
   unsigned short  sa_family;    // address family, AF_XXX
   char            sa_data[14];  // 14 bytes of protocol address;
};

sa_family can be variety, but it’ll be AF_INET or AF_INET6 (you must have known the difference when I write that ;p . sa_data containes destination address and port number for socket.

struct sockaddr_in – structure for specific use, IPv4. Make reference elements of the socket address. Please remember that sin_zero must be set to all zero with memset() function.

struct sockaddr_in {
   short int          sin_family;    // Address family, AF_INET
   unsigned shot int  sin_port;      // Port number
   struct in_addr     sin_addr;      // Internet address
   unsigned char      sin_zero[8];   // filled with zero to keep same size as sockaddr
};

struct in_addr – structure for hold internet address (IPv4)

struct in_addr {
   uint32_t s_addr;    // 32 bit int
};

struct sockaddr_in6 – structure for specific use, IPv6. Similiar to IPv4 version

struct sockaddr_in6 {
   u_int16_t        sin6_family;    // Address family, AF_INET6
   u_int16_t        sin6_port;	    // Port number, network byte
   u_int16_t        sin6_flowinfo;  // IPv6 flow information
   struct in6_addr  sin6_addr;      // IPv6 address
   u_int32_t        sin6_scope_id;  // Scope ID
};

struct in6_addr – structure for hold internet address (IPv6)

struct in6_addr {
   unsigned char s6_addr[16];   // IPv6 address
};

struct sockaddr_storage – structure large enough to hold both IPv4 and IPv6.

struct sockaddr_storage {
   sa_family_t  ss_family;	// address family

   // all this is padding, implementation specific
   char         __ss_pad1[_SS_PAD1SIZE];
   int64_t      __ss_align;
   char         __ss_pad2[_SS_PAD2SIZE];
};

The important one is the ss_family field, we can check it for AF_INET or AF_INET6. Then we can cast it to struct sockaddr_in or struct sockaddr_in6 if we want.

IP Address Representation

There are a bunch of function to manipulate IP address. If we have IP address “10.12.110.57” and “2001:db8:63b3:1::3490” we will use inet_pton() function to converts them to their respective address representation. To reverse it (convert to internet address representation to string), use inet_ntop() function.

#include <arpa/inet.h>

struct sockaddr_in sa;	// IPv4
struct sockaddr_in6 sa6	// IPv6
char ip4[INET_ADDRSTRLEN];
char ip6[INET6_ADDRSTRLEN];

inet_pton(AF_INET,"10.12.110.57",&(sa.sin_addr));	// IPv4
inet_pton(AF_INET6,"2001:db8:63b3:1::3490", &(sa6.sin6_addr));	// IPv6

inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("IPv4 Address is: %s\n", ip4);
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("IPv6 Address is: %s\n", ip6);

Socket Descriptor

As we discussed in some articles ago, a socket in Unix is a file associated with a integer. To create a socket we need create a variable for socket which can be as simple as integer.

Socket Creation

To create a socket we will call a function socket(). Function socket() has 3 arguments: a family type (AF_INET / AF_INET6), packet type (SOCK_STREAM / SOCK_DGRAM), protocol type.

#include <sys/types.h>
#include <sys/socket.h>

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);

Socket Bind to

Socket has been created on creation step and stored in socket_fd. But it would be meaningless if we cannot use it for communication because it has not bind into any port. Bind a port mean attach our socket to port and create a “tunnel” for our application to the port, exclusively. Once the socket bounded to a port, other program cannot use same port until our socket is unattached from the port. The information needed would be stored in struct sockaddr_* respective to type of address it want to use (IPv4 or IPv6).

#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr_in sin;

memset(sin.sin_zero,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(13510);
inet_pton(AF_INET, "127.0.0.1", &(sa.sin_addr));

bind( socket_fd, (struct sockaddr*) &sin, sizeof(sin) );

Socket Listen to

The bounded socket can listen to some incoming connections. A function listen() will set the maximum number of connection it can handle.

#include <sys/types.h>
#include <sys/socket.h>

listen( socket_fd, 20 );

Socket Accept request

Once a listening socket got an incoming packet, it is up to programmer to accept it or drop it. To accept incoming packet we use accept function and create a new file descriptor. The data can be fetched from new file descriptor (remember that everything in Unix is file?). Peer is remote computer who initiate communication.

#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr* peer_addr;

int accfd = accept( socket_fd, (struct sockaddr*) peer_addr, sizeof(peer_addr) );

Socket Receive message

After accepting connection, we have a new file descriptor that store message. The next thing to do is fetching the data.

#include <sys/types.h>
#include <sys/socket.h>

char message[1024];
int nbytes;

nbytes = recv(accfd, message, 1024, 0);

Socket Connect to

A socket can also used for sending message or simply connecting to another host. The open socket, socket created from socket(), is unnecessary to bounded to any port. It only need information about address it want to connect.

#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>

struct sockaddr_in sin;

memset( &sin.sin_zero, 0, sizeof(sin) );
sin.sin_family = AF_INET;
sin.sin_port = htons(13510);
inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr));

int result = connect( socket_fd, (struct sockaddr*) &addr, sizeof(addr) );

Socket Send message

When connection established, we can send message over our socket.

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>

char message[1024];
strcpy(message,"To server, hello");
send(sock_fd, message,1024,0)

, , ,

About Author

about author

xathrya

A man who is obsessed to low level technology.

Leave a Reply

Your email address will not be published. Required fields are marked *

Social media & sharing icons powered by UltimatelySocial