< getprotobyname() & getprotobyport() | Internet Protocol Main | IPv6 Client & Server Examples >
What do we have in this chapter 3 part 6?
|
The Server Example
The server side is a bit more involved than the client side. This is because the Windows IPv6 stack is a dual stack. That is, there is a separate stack for IPv4 and IPv6, so if a server wishes to accept both IPv4 and IPv6 connections, it must create a socket for each one. The two steps for creating an IP independent server are the following:
The following code illustrates this principle.
SOCKET slisten[16]; char *szPort="5150"; struct addrinfo hints, * res=NULL, * ptr=NULL; int count=0, rc;
memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(NULL, szPort, &hints, &res);
if (rc != 0) { // failed for some reason } ptr = res; while (ptr) { slisten[count] = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (slisten[count] == INVALID_SOCKET) { // socket failed }
rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen); if (rc == SOCKET_ERROR) { // bind failed }
rc = listen(slisten[count], 7); if (rc == SOCKET_ERROR) { // listen failed } count++; ptr = ptr->ai_next; } |
Once the sockets are created and bound, the application simply needs to wait for incoming connections on each. The following program example tries to demonstrate the server part.
#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 slisten[16], NewConnection;
char *szPort = "7777";
struct addrinfo hints, * res=NULL, * ptr=NULL;
int count=0, rc;
char recvbuff[1024];
int ByteReceived, i;
// Initialize Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Server: WSAStartup() failed with error code %ld\n", WSAGetLastError());
return 1;
}
else
printf("Server: WSAStartup() looks fine!\n");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
rc = getaddrinfo(NULL, szPort, &hints, &res);
if (rc != 0)
{
printf("Server: getaddrinfo() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Server: getaddrinfo() is OK...\n");
ptr = res;
while (ptr)
{
printf("\nServer: count value = %d\n", count);
// Use the res struct info for listening...
slisten[count] = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (slisten[count] == INVALID_SOCKET)
{
printf("Server: socket() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Server: socket() is OK...\n");
// The res struct info
printf("\n The address family: %d\n", res->ai_family);
printf(" The socket type: %d\n", res->ai_socktype);
printf(" The protocol: %d\n\n", res->ai_protocol);
// Then bind
rc = bind(slisten[count], ptr->ai_addr, ptr->ai_addrlen);
if (rc == SOCKET_ERROR)
{
printf("Server: bind() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
printf("Server: bind() is OK...\n");
// Next, listen
rc = listen(slisten[count], 10);
if (rc == SOCKET_ERROR)
{
printf("Server: listen() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
else
{
printf("Server: listen() is OK...\n");
NewConnection = SOCKET_ERROR;
// While the NewConnection socket equal to SOCKET_ERROR
// which is always true in this case...
while(NewConnection == SOCKET_ERROR)
{
// Accept connection on the slisten[count] socket and assign
// it to the NewConnection socket, let the slisten[count]
// do the listening for more connection
NewConnection = accept(slisten[count], NULL, NULL);
printf("Server: accept() is OK...\n");
printf("Server: New client got connected, ready to receive and send data...\n");
// Wait for more connections by calling accept again on ListeningSocket (loop)
// or start sending or receiving data on NewConnection.
ByteReceived = recv(NewConnection, recvbuff, sizeof(recvbuff), 0);
// When there is problem
if ( ByteReceived == SOCKET_ERROR )
{
printf("Server: recv() failed with error code %ld\n", WSAGetLastError());
WSACleanup();
break;
}
else
{
printf("Server: recv() is OK....\n");
// Print the received bytes. Take note that this is the total
// byte received, it is not the size of the declared buffer
printf("Server: Bytes received: %d\n", ByteReceived);
// Print what those bytes represent
printf("Server: Those bytes are: \"");
// Print the string only, discard other
// remaining 'rubbish' in the 1024 buffer size
for(i=0;i < ByteReceived;i++)
printf("%c", recvbuff[i]);
printf("\"\n");
}
}
}
if(res->ai_protocol == 6)
{
printf("Doing the TCP shutdown on the receiving part...\n");
shutdown(slisten[count], SD_RECEIVE);
}
closesocket(slisten[count]);
count++;
ptr = ptr->ai_next;
}
freeaddrinfo(res);
WSACleanup();
return 0;
}
------------------------------------------------
Then we run the previous client from the localhost and another peer computer in the same subnet. The ::1 is the IPv6 address for localhost (and make sure the computer is IPv6 enabled). Take note that more control should be implemented for the getaddrinfo() for example, if the address is numeric, getaddrinfo() should be disallowed from doing any name resolution. Otherwise a host name was provided that needs to be resolved when the getaddrinfo() call is issued
The following previous client example was run from another computer with IP address 192.168.1.2.
The following screenshot shows the server sample output when both client connections were completed.
The server used to test this program example is IPv6 enabled and it will fail on IPv4 server. The setsockopt() and getpeername() functions are introduced in this example.
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
// Some constants used by this program
#define SERVER_PORT 7171
#define BUFFER_LENGTH 256
#define FALSE 0
int main(int argc, char **argv)
{
// Variable and structure declarations
WSADATA wsaData;
int sd = -1, RetVal, sdconn = -1;
int rc, on = 1, j = 0, rcdsize=BUFFER_LENGTH;
char buffer[BUFFER_LENGTH];
struct sockaddr_in6 serveraddr, clientaddr;
int addrlen = sizeof(struct sockaddr_storage);
WCHAR MyAddrString[256] = L"Some dummy initializer";
int str = sizeof(struct sockaddr_storage);
DWORD str2 = sizeof(struct sockaddr_storage);
// Initialize the use of the Winsock DLL by a process
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Server: WSAStartup() failed with error code %ld\n", WSAGetLastError());
return 1;
}
else
printf("Server: WSAStartup() looks fine!\n");
// Using a loop for more 'efficient' code
do
{
// The socket() function returns a socket descriptor, which represents
// an endpoint. Get a socket for address family AF_INET6 to
// prepare to accept incoming connections on
if ((sd = socket(AF_INET6, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
printf("Server: socket() failed with code error %ld\n", WSAGetLastError());
break;
}
else
printf("Server: socket() looks good!\n");
// The setsockopt() used to allow the local address to be reused when
// the server is restarted before the required wait time expires
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == SOCKET_ERROR)
{
printf("Server: setsockopt(SO_REUSEADDR) failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Server: setsockopt(SO_REUSEADDR) found OK!\n");
// After the socket descriptor is created, a bind() function gets a
// unique name for the socket. In this example, the user sets the
// address to in6addr_any, which (by default) allows connections to
// be established from any IPv4 or IPv6 client based on the hostname
// that specifies port 7171.
// That is, the bind is done to both the IPv4 and IPv6 TCP/IP
// stacks. However this sample program only accept the IPv4 hostname, then
// the client must prepare to convert the IPv4 address to the hostname
// before translating the IP string to network address before making a connection
// using various Winsock API such as getaddressinfo() etc.
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
serveraddr.sin6_port = htons(SERVER_PORT);
// Applications use in6addr_any similarly to the way they use
// INADDR_ANY in IPv4.
serveraddr.sin6_addr = in6addr_any;
// The remaining fields in the sockaddr_in6 are currently not
// supported and should be set to 0 to ensure upward compatibility
if (bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == SOCKET_ERROR)
{
printf("Server: bind() failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Server: bind() also looks fine!\n");
// The listen() function allows the server to accept incoming
// client connections. In this example, the backlog is set to 7
// This means that the system will queue 10 incoming connection
// requests before the system starts rejecting the incoming requests
if (listen(sd, 7) == SOCKET_ERROR)
{
printf("Server: listen() failed with error code %ld\n", WSAGetLastError());
break;
}
printf("Server: listen() is OK and ready for client connect()...\n");
// The server uses the accept() function to accept an incoming
// connection request. The accept() call will block indefinitely
// waiting for the incoming connection to arrive from an IPv4 or IPv6 client
if ((sdconn = accept(sd, NULL, NULL)) == INVALID_SOCKET)
{
printf("Server: accept() failed with error code %ld\n", WSAGetLastError());
break;
}
else
{
printf("Server: accept() return success!\n");
// Display the client address. Note that if the client is
// an IPv4 client, the address will be shown as an IPv4 Mapped IPv6 address
if(getpeername(sdconn, (struct sockaddr *)&clientaddr, &addrlen) == SOCKET_ERROR)
{
printf("Server: getpeername() failed miserably with error code %ld\n", WSAGetLastError());
}
else
printf("Server: getpeername() found OK!\n");
// converts an IPv4 or IPv6 Internet network address into a string in Internet standard
// format. The ANSI version of this function is inet_ntop() which is used in UNIX/BSD
RetVal = WSAAddressToString((LPSOCKADDR)&clientaddr, addrlen, NULL, MyAddrString, &str);
if(RetVal != 0)
printf("Server: WSAAddressToString() failed with error code %ld\n", WSAGetLastError());
else
{
printf("Server: Client address is %S\n", MyAddrString);
printf("Server: Client port is %d\n", ntohs(clientaddr.sin6_port));
}
}
// In this example we know that the client will send 256 bytes of
// data over. Knowing this, we can use the SO_RCVTIMEO socket
// option and specify that we don't want our recv() to wake up
// until all 256 bytes of data have arrived
// http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx
// Take note that some parameters are Microsoft specific
if (setsockopt(sdconn, SOL_SOCKET, SO_RCVTIMEO, (char *)&rcdsize, sizeof(rcdsize)) == SOCKET_ERROR)
{
printf("Server: setsockopt(SO_RCVTIMEO) failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Server: setsockopt(SO_RCVTIMEO) looks good!\n");
// Receive that 256 bytes of data from the client
rc = recv(sdconn, buffer, sizeof(buffer), 0);
if (rc == SOCKET_ERROR)
{
printf("Server: recv() failed with error code %ld\n", WSAGetLastError());
break;
}
else
{
printf("Server: recv() is OK...\n");
}
printf("Server: %d bytes of data were received\n", rc);
// Just for display!
printf("Server: Received money is - ");
for(j=0; buffer[j] == '$';j++)
printf("%c",buffer[j]);
printf("\n");
if (rc == 0 || rc < sizeof(buffer))
{
printf("Server: The client closed the connection before sending all the data!\n");
break;
}
else
{
printf("Server: All the data sent by client!\n");
}
// Echo the data back to the client
printf("Server: Echoing back the sent data to client...\n");
rc = send(sdconn, buffer, sizeof(buffer), 0);
if (rc == SOCKET_ERROR)
{
printf("Server: send() failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Server: send() is OK...\n");
} while (FALSE);
// Program should complete here
// Do the shutdown() for TCP so receive and send operation will no longer be allowed
printf("Server: Ready to do the TCP shutdown()...\n");
if(shutdown(sdconn, SD_BOTH) == SOCKET_ERROR)
printf("Server: shutdown sdconn socket and the other end failed with error %ld\n", WSAGetLastError());
else
printf("Server: shutdown() on both end for sdconn socket looks OK...\n");
// Close down any open socket descriptors,
// BSD/UNIX uses close()
if (closesocket(sd) == 0)
printf("Server: sd socket was closed...\n");
if (closesocket(sdconn) == 0)
printf("Server: sdconn socket was closed...\n");
// Terminates the use of the Ws2_32.DLL for all threads (if any)
printf("Server: Doing the WSACleanup()...\n");
if( WSACleanup() == SOCKET_ERROR)
printf("Server: WSACleanup() failed with error code %ld\n", WSAGetLastError());
else
printf("Server: WSACleanup() confirmed OK!\n");
return 0;
}
The following screenshot shows a sample output.
< getprotobyname() & getprotobyport() | Internet Protocol Main | IPv6 Client & Server Examples >