< inetntop() & inetpton() | Internet Protocol Main | IPv4 & IPv6 Server Examples >
What do we have in this chapter 3 part 5?
|
The getprotobyname() Program Example
The following program example tries to demonstrate the getprotobyname() function. Create a new empty Win32 console mode application and add the project/solution name.
#include <winsock2.h> #include <stdio.h>
int main(int argc, char **argv) { // Declare and initialize variables WSADATA wsaData; struct protoent *servptr; // http://www.iana.org/assignments/port-numbers char protocol[12][10] = {"ip","icmp","ggp","tcp", "egp","pup","udp","hmp","xns-idp", "rdp","rvd" }; int i;
// Initialize Winsock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup() failed with error code %ld\n", WSAGetLastError()); return 1; } else printf("WSAStartup() looks fine!\n");
// Traverse the array content... // Be careful with the array index... for(i=0; i <= 10;i++) { servptr = getprotobyname(protocol[i]);
if (servptr == NULL) printf ("\ngetprotobyname() - %d failed with error code %ld\n", protocol[i], WSAGetLastError()); else { // Print the related info printf("getprotobyname() is OK - %s\n", protocol[i]); printf(" Official name is %s\n", servptr->p_name); printf(" Protocol number is %d\n", servptr->p_proto); } }
WSACleanup(); return 0; } |
Build and run the project. The following screenshot shows a sample output.
The following program example tries to demonstrate the getprotobyport() function.
#include <winsock2.h>
#include <stdio.h>
int main(int argc, char **argv)
{
// Declare and initialize variables
WSADATA wsaData;
struct protoent *servptr;
// http://www.iana.org/assignments/protocol-numbers/
// A sample of protocol numbers
int protocol[12] = {1,2,3,11,30,20,22,27,8,17,12};
int i;
// Initialize Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("WSAStartup() failed with error code %ld\n", WSAGetLastError());
return 1;
}
else
printf("WSAStartup() looks fine!\n");
// Traverse the array content...
// Be careful with the array index...
for(i=0; i <= 10;i++)
{
servptr = getprotobynumber(protocol[i]);
if (servptr == NULL)
printf ("getprotobynumber() - %d failed with error code %ld\n", protocol[i], WSAGetLastError());
else
{
// Print the related info
printf("getprotobynumber() is OK - %d\n", protocol[i]);
printf(" Official name is %s\n", servptr->p_name);
printf(" Protocol number is %d\n", servptr->p_proto);
}
}
WSACleanup();
return 0;
}
Build and run the project. The following screenshot shows a sample output.
As discussed previously, if you are curious where those protocol names and numbers are extracted in the local system, the files containing the information are under the %system32/driver/etc folder (Windows XP Pro SP2). You can open those files in text editor.
In this section, we'll cover how to develop applications that work seamlessly over IPv4 and IPv6. This method requires using the new name resolution APIs getaddrinfo() and getnameinfo() and requires a bit of rearranging Winsock calls from what you are probably used to.
Before we get into the specifics, let's cover some of the basic practices that you should follow. First, applications should not allocate the socket address structures specific to each protocol (such as SOCKADDR_IN and SOCKADDR_IN6 for IPv4 and IPv6, respectively) because they can be different sizes. Instead, a new socket address structure SOCKADDR_STORAGE has been introduced that is as large as the largest possible protocol specific address structure and includes padding for 64-bit alignment issues. The following code uses a SOCKADDR_STORAGE structure to store the destination IPv6 address.
SOCKADDR_STORAGE saDestination;
SOCKET s;
int addrlen, rc;
s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
// socket failed
}
addrlen = sizeof(saDestination);
rc = WSAStringToAddress(
"3ffe:2900:d005:f28d:250:8bff:fea0:92ed",
AF_INET6,
NULL,
(SOCKADDR *)&saDestination,
&addrlen
);
if (rc == SOCKET_ERROR)
{
// conversion failed
}
rc = connect(s, (SOCKADDR *)&saDestination, sizeof(saDestination));
if (rc == SOCKET_ERROR)
{
// connect failed
}
Second, functions that take an address as a parameter should pass the entire socket address structure and not the protocol specific types like struct in_addr or struct in6_addr. This is important for IPv6, which might require the scope ID information to successfully connect. The SOCKADDR_STORAGE structure containing the address should be passed instead.
Third, avoid hardcode addresses regardless of whether they are IPv4 or IPv6. The Winsock header files define constants for all the address that are hard coded such as the loopback address and the wildcard address used for binding.
Now that some of the basic issues are out of the way, let's move to discussing how an application should be structured to be IP independent. We will divide our discussion into two sections: the client and the server.
For both TCP and UDP clients, the application typically possesses the server (or recipient's) IP address or hostname. Whether it resolves to an IPv4 address or IPv6 address doesn't matter. The client should follow these three steps:
The following code sample illustrates these principles.
SOCKET s;
struct addrinfo hints, *res=NULL;
char *szRemoteAddress=NULL, *szRemotePort=NULL;
int rc;
// Parse the command line to obtain the remote server's
// hostname or address along with the port number, which are contained
// in szRemoteAddress and szRemotePort.
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// first resolve assuming string is a string literal address
rc = getaddrinfo(szRemoteAddress, szRemotePort, &hints, &res);
if (rc == WSANO_DATA)
{
// Unable to resolve name - bail out
}
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == INVALID_SOCKET)
{
// socket failed
}
rc = connect(s, res->ai_addr, res->ai_addrlen);
if (rc == SOCKET_ERROR)
{
// connect failed
}
freeaddrinfo(res);
First, you will notice that there are no explicit references to AF_INET or AF_INET6. Also, there's no need to manipulate the underlying SOCKADDR_IN or SOCKADDR_IN6 addresses. The getaddrinfo() call fully initializes the returned socket address structure with all the required information - address family, binary address, etc., that is necessary for connecting or sending datagrams.
If the client application needs to explicitly bind the socket to a local port after socket creation but before connect() or sendto(), then another getaddrinfo() call can be made. This call would specify the address family, socket type, and protocol returned from the first call along with the AI_PASSIVE flag and desired local port, which will return another socket address structure initialized to the necessary bind address (such as 0.0.0.0 for IPv4 and :: for IPv6). The following program example shows the working version.
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
// Declare and initialize variables
WSADATA wsaData;
SOCKET s;
struct addrinfo hints, *res=NULL;
char *szRemoteAddress=NULL, *szRemotePort=NULL;
int rc;
char sendbuf[1024] = "This is a test string from client!";
int BytesSent;
// Validate the parameters
if (argc != 3)
{
printf("Usage: %s <remote_host_name> <port_number>\n", argv[0]);
printf("Example: %s www.bodopiang.com 7777\n", argv[0]);
// Just return with error
return 1;
}
// Initialize Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Client: WSAStartup() failed with error code %ld\n", WSAGetLastError());
return 1;
}
else
printf("Client: WSAStartup() looks fine!\n");
// Parse the command line to obtain the remote server's hostname
// or IP address along with the port number, which are contained
// in szRemoteAddress and szRemotePort.
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
szRemoteAddress = argv[1];
szRemotePort = argv[2];
// first resolve assuming string is a string literal address
// The addrinfo structure is used by the ANSI getaddrinfo()
// function to hold host address information. The addrinfoW()
// structure is the version of this structure used by the
// Unicode GetAddrInfoW() function
// res struct contains info of the remote response
rc = getaddrinfo(szRemoteAddress, szRemotePort, &hints, &res);
if (rc != 0)
{
printf("Client: getaddrinfo() failed miserably with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Client: getaddrinfo() seems fine!\n");
// The res struct info
// http://msdn.microsoft.com/en-us/library/ms737530(VS.85).aspx
printf("\n The getaddrinfo() options used: %d\n", res->ai_flags);
printf(" The address family: %d\n", res->ai_family);
printf(" The socket type: %d\n", res->ai_socktype);
printf(" The protocol type: %d\n", res->ai_protocol);
// The hints struct info
printf("\n The hints.ai_family: %d\n", hints.ai_family);
printf(" The hints.ai_socktype: %d\n", hints.ai_socktype);
printf(" The hints.ai_protocol: %d\n", hints.ai_protocol);
// Use the res info to create a socket
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == INVALID_SOCKET)
{
printf("\nClient: socket() failed miserably with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("\nClient: socket() is OK\n");
// Then use the res to connect
rc = connect(s, res->ai_addr, res->ai_addrlen);
if (rc == SOCKET_ERROR)
{
printf("Client: connect() failed miserably with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Client: connect() is OK\n");
printf("Client: I\'m ready to send or receive data...\n");
// Sends some data to server/receiver...
printf(" Sending: \"%s\"\n", sendbuf);
BytesSent = send(s, sendbuf, strlen(sendbuf), 0);
if(BytesSent == SOCKET_ERROR)
{
printf("Client: send() failed with error code %ld.\n", WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
else
printf("Client: send() is OK - bytes sent: %ld\n", BytesSent);
// If the socket type is TCP - IPPROTO_TCP, do the shutdown
if(res->ai_protocol == 6)
{
if(shutdown(s, SD_SEND) != 0)
{
printf("Client: shutdown() failed with error code: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Client: shutdown() the sending part...\n");
}
// Deallocate resource
freeaddrinfo(res);
// Close socket
if(closesocket(s) != 0)
{
printf("Client: Cannot close s socket. Error code: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Client: Closing s socket...\n");
// Clean up the WSA
if(WSACleanup() != 0)
{
printf("Client: WSACleanup() failed!...\n");
return 1;
}
else
printf("Client: WSACleanup() is OK...\n");
return 0;
}
The following screenshot shows a sample output using the following arguments:
IPindependentClientexample www.yahoo.com 80
IPindependentClientexample 216.239.61.104 80
--------------------------------------------------------------
The following screenshot shows a sample output using the following arguments:
IPindependentClientexample ipv6.internet2.edu 80
IPindependentClientexample 2001:1888:0:1:2d0:b7ff:fe7d:bed6 80
< inetntop() & inetpton() | Internet Protocol Main | IPv4 & IPv6 Server Examples >