C Socket Programming : Part 2
R.I.P. IPv4
IPv4 ( Internet Protocol Version 4 ) is the current, most used Internet Protocol, officially described in September 1981 by the IETF, providing 4,294,967,296 (232) addresses.
Address Exhaustion
The biggest problem of IPv4 is the address exhaustion. Back at 1980 when it was defined, nobody could imagine the incredible rate the internet grows. Nowdays every embedded device ( mobile phones, PDAs, palmtops ) is a part of the internet, thus more and more addresses need to be allocated.
Welcome IPv6
IPv6 was defined in December 1998 by the IETF, almost 20 years after the release of IPv4.
IPv6 is the next-generation Internet Protocol, designated as the successor of IPv4 which still dominates the biggest part of the internet…. For now
IPv6 addresses use an 128-bit encoding, thus providing about 3.4×1038 addresses and this is the main advantage of it, considering the IPv4 ( 32-bit encoding ) adrress exhaustion.
It also implements new features, additions and many more advantages but that’s not what we are here to cover
You can read the wikipedia links for more information.
IPv6 and Network Programming
Since IPv6 will be the new internet protocol, and is already defined, why make our applications with only IPv4 in mind? We can develop our applications compatible with IPv6, but at the same time, backwards compatible with IPv4. Doing so, we put our little bit, to help IPv6 come faster, since one minor problem which slows down the successor is that many programs are not developed with IPv6 in mind.
C Sockets. Changes to make
Remember C Socket Programming : Part 1 ? Lets see what changes have to be made, in order to make our applications IPv6 compatible. Take a quick look at the examples provided at Part 1.
[c light="true"]char ip[MAX_IPV4_SIZE]; //max would be lets say 111.111.111.111 = 3×4 ( nums ) + 3×1 ( dots ) = 15[/c]
We used char ip[15] in order to store the remote host IP at our example server. But since IPv6 uses a 128-bit representation we need more space to store an IPv6 address. For this reason we use :
[c light="true"]char ip[INET6_ADDRSTRLEN];[/c]
INET6_ADDRSTRLEN is define as 45 in header file <netinet/in.h> which is included by <netdb.h> which we include ourselves.
struct addrinfo.ai_family = AF_UNSPEC;
Back when we used only IPv4, the ai_family was always AF_INET. For IPv6 we would use AF_INET6, but this would break backwards-compability with IPv4. So we use AF_UNSPEC to declare that either IPv4 or IPv6 might be used, and this will be decided during the program runtime.
struct sockaddr_storage;
The struct used to storage IPv4 family information is sockaddr_in, but because IPv6 addresses require more space, there is struct sockaddr_in6 for such a case. But if we want to be compatible with both protocols, there is struct sockaddr_storage, which can hold information for both protocols!
Using helper / wrapper functions
When we called inet_ntop() in our server example, to convert the network byte-order address into human-readable format, we passed straight their_addr.sin_addr, asssuming the remote address is IPv4. But this wont be the case with IPv6 hosts, so we have to use a helper function, that will return either sin_addr or sin6_addr, depending on struct sockaddr_storage.ss_family.
Examples redefined
So, considering those changes to be made, here are the examples of C Socket Programming : Part 1 should look like.
[ server_ipv6.c ]
[c]
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#define CON_NUM 10
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char** argv) {
int sockfd, new_fd;
struct addrinfo hints, *servinfo;
struct sockaddr_storage their_addr;
socklen_t sin_size;
char *buff = (char*)malloc(1024);
char ip[INET6_ADDRSTRLEN];
int rc;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if (argc != 2) {
printf("Usage : %s PORT\n", argv[0]);
return -1;
}
if ((rc = getaddrinfo(NULL, argv[1], &hints, &servinfo)) != 0) {
printf("Error at getaddrinfo() : %s\n", gai_strerror(rc));
return 1;
}
if ((sockfd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) {
printf("Error at socket() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
return 1;
}
if (bind(sockfd, servinfo->ai_addr, servinfo->ai_addrlen) == -1) {
printf("Error at bind() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
close(sockfd);
return -1;
}
if (listen(sockfd, CON_NUM) == -1) {
printf("Error at listen() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
close(sockfd);
return -1;
}
sin_size = sizeof their_addr;
while(1) {
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
printf("Error at accept() : %s\n", strerror(errno));
continue;
}
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr*)&their_addr), ip, sizeof ip);
printf("Got connection from %s\n", ip);
int j = 1;
while (1) {
j = recv(new_fd, buff, 1024, 0);
if ( j == 0 )
break;
if ( j == -1 ) {
printf("Error at recv() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
close(sockfd);
return -1;
}
printf("Recieved : %s\n", buff);
}
}
freeaddrinfo(servinfo);
close(sockfd);
close(new_fd);
return 0;
}
[/c]
[ client.c ]
[c]
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
int status, sock, bytes_sent;
struct addrinfo hints, *servinfo;
char msg[] = "hello!!";
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
//Not correct usage, print help and exit.
if (argc != 3) {
printf("Usage : %s HOSTNAME PORT\n", argv[0]);
return -1;
}
//get info about remote host.
if ((status = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
printf("Error during getaddrinfo() : %s\n", gai_strerror(status));
return -1;
}
//Initialize socket.
if ((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) {
printf(" Error during socket() : %s\n", strerror(errno));
freeaddrinfo(servinfo); //free servinfo allocated space and exit.
return -1;
}
//connect to remote host.
if (connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) == -1 ) {
printf("Error during connect() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
close(sock); //Also close the socket.
return -1;
}
if ( (bytes_sent = send(sock, msg, strlen(msg), 0)) == -1 ) {
printf("Error during send() : %s\n", strerror(errno));
freeaddrinfo(servinfo);
close(sock);
return -1;
}
printf("Sent %d bytes.\n", bytes_sent);
freeaddrinfo(servinfo);
close(sock);
return 0;
}
[/c]
Resources : beej.us
Incoming Part 3 : Converting all this bunch of code, into pretty C++ Class!
As always, you can subscribe via RSS or follow wedevblog on twitter for the latest development posts!
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
int status, sock, bytes_sent;
struct addrinfo hints, *servinfo;
char msg[] = “hello!!”;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
//Not correct usage, print help and exit.
if (argc != 3) {
printf(“Usage : %s HOSTNAME PORT\n”, argv[0]);
return -1;
}
//get info about remote host.
if ((status = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
printf(“Error during getaddrinfo() : %s\n”, gai_strerror(status));
return -1;
}
//Initialize socket.
if ((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) {
printf(” Error during socket() : %s\n”, strerror(errno));
freeaddrinfo(servinfo); //free servinfo allocated space and exit.
return -1;
}
//connect to remote host.
if (connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) == -1 ) {
printf(“Error during connect() : %s\n”, strerror(errno));
freeaddrinfo(servinfo);
close(sock); //Also close the socket.
return -1;
}
if ( (bytes_sent = send(sock, msg, strlen(msg), 0)) == -1 ) {
printf(“Error during send() : %s\n”, strerror(errno));
freeaddrinfo(servinfo);
close(sock);
return -1;
}
printf(“Sent %d bytes.\n”, bytes_sent);
freeaddrinfo(servinfo);
close(sock);
return 0;
}#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
int status, sock, bytes_sent;
struct addrinfo hints, *servinfo;
char msg[] = “hello!!”;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
//Not correct usage, print help and exit.
if (argc != 3) {
printf(“Usage : %s HOSTNAME PORT\n”, argv[0]);
return -1;
}
//get info about remote host.
if ((status = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
printf(“Error during getaddrinfo() : %s\n”, gai_strerror(status));
return -1;
}
//Initialize socket.
if ((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) {
printf(” Error during socket() : %s\n”, strerror(errno));
freeaddrinfo(servinfo); //free servinfo allocated space and exit.
return -1;
}
//connect to remote host.
if (connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) == -1 ) {
printf(“Error during connect() : %s\n”, strerror(errno));
freeaddrinfo(servinfo);
close(sock); //Also close the socket.
return -1;
}
if ( (bytes_sent = send(sock, msg, strlen(msg), 0)) == -1 ) {
printf(“Error during send() : %s\n”, strerror(errno));
freeaddrinfo(servinfo);
close(sock);
return -1;
}
printf(“Sent %d bytes.\n”, bytes_sent);
freeaddrinfo(servinfo);
close(sock);
return 0;
}
Recent Comments