Microsoft Socket programming

I don't plan to spend much time on this, but I wanted to show how socket programming on Microsoft Windows looks very similar to Unix/Linux. This works with Visual C++ Version 6. This set of notes demonstrates TCP/IP, while the following link shows you how datagrams work on a Microsoft platform (again similar to Unix/Linux)
microsoft datagram programming

The above link does contain code to a functioning PING program.

From all of this you can see the Microsoft Winsock programming is almost identical to Unix/Linux socket programming.

TCP/IP and Socket Programming

Berkeley Sockets provides the foundation for many of the implementations of TCP/IP programs. This includes Windows programs. We will start out with some simple programs that have simple user interfaces. The next several programs that we are going to put together look very similar iin Unix and in Windows. These programs are using the basic Berkeley Socket concepts. Consequently, we are going to start out with a Console Interface approach. We will later migrate these programs to more GUI style user interfaces.


Simple Client TCP/IP Access

The following code can be used to make a simple Client TCP/IP request. To create this program, use the following steps:

//
// Client.cpp
//
// Extremely simple, stream client example.
// Works in conjunction with Server.cpp.
//
// The program attempts to connect to the server and port
// specified on the command line. The Server program prints
// the needed information when it is started. Once connected,
// the program sends data to the server, waits for a response
// and then exits.
//
// Compile and link with wsock32.lib
//
// Pass the server name and port number on the command line. 
//
// Example: Client MyMachineName 2000
//
#include <stdio.h>
#include <winsock.h>

// Function prototype
void StreamClient(char *szServer, short nPort);

// Helper macro for displaying errors
#define PRINTERROR(s)	\
		fprintf(stderr,"\n%: %d\n", s, WSAGetLastError())

////////////////////////////////////////////////////////////

void main(int argc, char **argv)
{
	WORD wVersionRequested = MAKEWORD(1,1);
	WSADATA wsaData;
	int nRet;
	short nPort;

	//
	// Check for the host and port arguments
	//
	if (argc != 3)
	{
		fprintf(stderr,"\nSyntax: client ServerName PortNumber\n");
		return;
	}

	nPort = atoi(argv[2]);


	//
	// Initialize WinSock and check the version
	//
	nRet = WSAStartup(wVersionRequested, &wsaData);
	if (wsaData.wVersion != wVersionRequested)
	{	
		fprintf(stderr,"\n Wrong version\n");
		return;
	}


	//
	// Go do the stuff a stream client does
	//
	StreamClient(argv[1], nPort);

	
	//
	// Release WinSock
	//
	WSACleanup();
}

////////////////////////////////////////////////////////////

void StreamClient(char *szServer, short nPort)
{
	printf("\nStream Client connecting to server: %s on port: %d",
				szServer, nPort);

	//
	// Find the server
	//
    LPHOSTENT lpHostEntry;

	lpHostEntry = gethostbyname(szServer);
    if (lpHostEntry == NULL)
    {
        PRINTERROR("gethostbyname()");
        return;
    }

	//
	// Create a TCP/IP stream socket
	//
	SOCKET	theSocket;

	theSocket = socket(AF_INET,// Address family
		SOCK_STREAM,// Socket type
		IPPROTO_TCP);		// Protocol
	if (theSocket == INVALID_SOCKET)
	{
		PRINTERROR("socket()");
		return;
	}

	//
	// Fill in the address structure
	//
	SOCKADDR_IN saServer;

	saServer.sin_family = AF_INET;

	// Server's address
	saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list);
	saServer.sin_port = htons(nPort);	// Port number from command line

	//
	// connect to the server
	//
	int nRet;

	nRet = connect(theSocket,	// Socket
		(LPSOCKADDR)&saServer,	// Server address
		sizeof(struct sockaddr));// Length of server address structure
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("socket()");
		closesocket(theSocket);
		return;
	}


	//
	// Send data to the server
	//
	char szBuf[256];

	strcpy(szBuf, "From the Client");
	nRet = send(theSocket,// Connected socket
		szBuf,	// Data buffer
		strlen(szBuf),	// Length of data
		0);	// Flags
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("send()");
		closesocket(theSocket);
		return;
	}


	//
	// Wait for a reply
	//
	nRet = recv(theSocket, // Connected socket
		szBuf,	// Receive buffer
		sizeof(szBuf),	// Size of receive buffer
		0);	// Flags
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("recv()");
		closesocket(theSocket);
		return;
	}


	//
	// Display the received data
	//

        szBuf[nRet]=0;
	printf("\nData received: %s", szBuf);


	closesocket(theSocket);
	return;
}


Simple Server TCP/IP Program

The following code provides the Server end of the Simple TCP/IP program. To create this program, use the following steps:

 


//
// Server.cpp
//
// Extremely simple, stream server example.
// Works in conjunction with Client.cpp.
//
// The program sets itself up as a server using the TCP
// protoocl. It waits for data from a client, displays
// the incoming data, sends a message back to the client
// and then exits.
//
// Compile and link with wsock32.lib
//
// Pass the port number that the server should bind() to
// on the command line. Any port number not already in use
// can be specified.
//
// Example: Server 2000
//

#include <stdio.h>
#include <winsock.h>

// Function prototype
void StreamServer(short nPort);

// Helper macro for displaying errors
#define PRINTERROR(s)	\
		fprintf(stderr,"\n%: %d\n", s, WSAGetLastError())

////////////////////////////////////////////////////////////

void main(int argc, char **argv)
{
	WORD wVersionRequested = MAKEWORD(1,1);
	WSADATA wsaData;
	int nRet;
	short nPort;

	//
	// Check for port argument
	//
	if (argc != 2)
	{
		fprintf(stderr,"\nSyntax: server PortNumber\n");
		return;
	}

	nPort = atoi(argv[1]);

	//
	// Initialize WinSock and check version
	//
	nRet = WSAStartup(wVersionRequested, &wsaData);
	if (wsaData.wVersion != wVersionRequested)
	{	
		fprintf(stderr,"\n Wrong version\n");
		return;
	}


	//
	// Do the stuff a stream server does
	//
	StreamServer(nPort);

	
	//
	// Release WinSock
	//
	WSACleanup();
}

////////////////////////////////////////////////////////////

void StreamServer(short nPort)
{
	//
	// Create a TCP/IP stream socket to "listen" with
	//
	SOCKET	listenSocket;

	listenSocket = socket(AF_INET,			// Address family
						  SOCK_STREAM,		// Socket type
						  IPPROTO_TCP);		// Protocol
	if (listenSocket == INVALID_SOCKET)
	{
		PRINTERROR("socket()");
		return;
	}


	//
	// Fill in the address structure
	//
	SOCKADDR_IN saServer;		

	saServer.sin_family = AF_INET;
	saServer.sin_addr.s_addr = INADDR_ANY;	// Let WinSock supply address
	saServer.sin_port = htons(nPort);		// Use port from command line

	//
	// bind the name to the socket
	//
	int nRet;

	nRet = bind(listenSocket,	// Socket 
		(LPSOCKADDR)&saServer,	// Our address
		sizeof(struct sockaddr));// Size of address structure
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("bind()");
		closesocket(listenSocket);
		return;
	}

	//
	// This isn't normally done or required, but in this 
	// example we're printing out where the server is waiting
	// so that you can connect the example client.
	//
	int nLen;
	nLen = sizeof(SOCKADDR);
	char szBuf[256];

	nRet = gethostname(szBuf, sizeof(szBuf));
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("gethostname()");
		closesocket(listenSocket);
		return;
	}

	//
	// Show the server name and port number
	//
	printf("\nServer named %s waiting on port %d\n",
			szBuf, nPort);

	//
	// Set the socket to listen
	//

	printf("\nlisten()");
	nRet = listen(listenSocket,	// Bound socket
		SOMAXCONN);	// Number of connection request queue
	if (nRet == SOCKET_ERROR)
	{
		PRINTERROR("listen()");
		closesocket(listenSocket);
		return;
	}

	//
	// Wait for an incoming request
	//
	SOCKET	remoteSocket;

	printf("\nBlocking at accept()");
	remoteSocket = accept(listenSocket,  // Listening socket
		NULL,	// Optional client address
		NULL);
	if (remoteSocket == INVALID_SOCKET)
	{
		PRINTERROR("accept()");
		closesocket(listenSocket);
		return;
	}

	//
	// We're connected to a client
	// New socket descriptor returned already
	// has clients address

	//
	// Receive data from the client
	//
	memset(szBuf, 0, sizeof(szBuf));
	nRet = recv(remoteSocket,					// Connected client
				szBuf,							// Receive buffer
				sizeof(szBuf),					// Length of buffer
				0);								// Flags
	if (nRet == INVALID_SOCKET)
	{
		PRINTERROR("recv()");
		closesocket(listenSocket);
		closesocket(remoteSocket);
		return;
	}

	//
	// Display received data
	//
	szBuf[nRet]=0;
        printf("\nData received: %s", szBuf);

	//
	// Send data back to the client
	//
	strcpy(szBuf, "From the Server");
	nRet = send(remoteSocket,				// Connected socket
				szBuf,						// Data buffer
				strlen(szBuf),				// Lenght of data
				0);							// Flags

	//
	// Close BOTH sockets before exiting
	//
	closesocket(remoteSocket);
     closesocket(listenSocket);
	return;
}



Collecting Address Information


It's good to know how to use the DNS name server to gather IP Address information. The DNS name server can give us an IP address from a Domain name or it can give us the inverse translation (Domain Name from an IP address). The following little Console program gives you a sample of some of the possibilities. To create this program use the same instructions as we used for the previous Console applications. The key thing that you will need to remember is to add the wsock32.lib library to your Link Settings:

 

 

//
// HostInfo.c  
//
// Use WinSock gethostbyX functions to lookup a host
// name or IP address and print the returned hostent
// structure;
//

//
// Pass a server name or IP address on the command line.
//
// Examples:
//        HostInfo www.idgbooks.com
//        HostInfo www.sockets.com
//        HostInfo 207.68.156.52
//

#include <stdio.h>
#include <winsock.h>

// Function to print a hostent structure
int Printhostent(LPCSTR lpServerNameOrAddress);

void main(int argc, char **argv)
{
    WORD wVersionRequested = MAKEWORD(1,1);
    WSADATA wsaData;
    int nRC;

    // Check arguments
    if (argc != 2)
    {
        fprintf(stderr,
            "\nSyntax: HostInfo ServerNameOrAddress\n");
        return;
    }

    // Initialize WinSock.dll
    nRC = WSAStartup(wVersionRequested, &wsaData);
    if (nRC)
    {
        fprintf(stderr,"\nWSAStartup() error: %d\n", nRC); 
        WSACleanup();
        return;
    }

    // Check WinSock version
    if (wVersionRequested != wsaData.wVersion)
    {
        fprintf(stderr,"\nWinSock version 1.1 not supported\n");
        WSACleanup();
        return;
    }

    // Call Printhostent() to do all the work
    nRC = Printhostent(argv[1]);
    if (nRC)
        fprintf(stderr,"\nPrinthostent return code: %d\n", nRC);
    WSACleanup();
}

int Printhostent(LPCSTR lpServerNameOrAddress)
{
    LPHOSTENT lpHostEntry;     // Pointer to host entry structure
    struct in_addr iaHost;     // Internet address structure
    struct in_addr *pinAddr;   // Pointer to an internet address
    LPSTR lpAlias;             // Character pointer for alias names
    int iNdx;

    // Use inet_addr() to determine if we're dealing with a name
    // or an address
    iaHost.s_addr = inet_addr(lpServerNameOrAddress);
    if (iaHost.s_addr == INADDR_NONE)
    {
        // Wasn't an IP address string, assume it is a name
        lpHostEntry = gethostbyname(lpServerNameOrAddress);
    }
    else
    {
        // It was a valid IP address string
        lpHostEntry = gethostbyaddr((const char *)&iaHost, 
                        sizeof(struct in_addr), AF_INET);
    }

    // Check return value
    if (lpHostEntry == NULL)
    {
        fprintf(stderr,"\nError getting host: %d",
                 WSAGetLastError());
        return WSAGetLastError();
    }

    // Print structure
    printf("\n\nHOSTENT");
    printf("\n-----------------");

    // Host name
    printf("\nHost Name........: %s", lpHostEntry->h_name);

    // List of host aliases
    printf("\nHost Aliases.....");
    for (iNdx = 0; ; iNdx++)
    {
        lpAlias = lpHostEntry->h_aliases[iNdx];
        if (lpAlias == NULL)
            break;
        printf(": %s", lpAlias);
        printf("\n                 ");
    }

    // Address type
    printf("\nAddress type.....: %d", lpHostEntry->h_addrtype);
    if (lpHostEntry->h_addrtype == AF_INET)
        printf(" (AF_INET)");
    else
        printf(" (UnknownType)");

    // Address length
    printf("\nAddress length...: %d", lpHostEntry->h_length);

    // List of IP addresses
    printf("\nIP Addresses.....");
    for (iNdx = 0; ; iNdx++)
    {
        pinAddr = ((LPIN_ADDR)lpHostEntry->h_addr_list[iNdx]);
        if (pinAddr == NULL)
            break;
        printf(": %s", inet_ntoa(*pinAddr));
        printf("\n                 ");
    }
    printf("\n");
    return 0;
}


Sending Commands Using the Select Statement

Sometimes you want have a timeout on Socket commands. Also you may need to monitor multiple sockets for activity. The select statement is the key routine that you need to be aware of. The following example gives you a program that will send commands and wait for responses until a timeout occurs. This program will take multiple line commands. A command is terminated by a single period line. Once a command has been sent and the select has waited for the resonse, this program will then let you enter in more commands. You need to use the same techniques already described for building this Console Application.

 

 

#include <stdio.h>
#include <winsock.h>

int nSelectWinsock (int nPort, int nTime, 
                    char *szHost);


int main(int argc, char * argv[])
{
     int nPort, nTime;
     char *szHost;
     char cHost[300], cPort[100], cTime[100];
    
   
     printf("\nEnter Host Name:\n");
     gets(cHost);
     szHost = cHost;

     printf("\nEnter Port:\n");
     gets(cPort);
     nPort = atoi(cPort);

     printf("\nEnter Timeout in seconds:\n");
     gets(cTime);
     nTime = atoi(cTime);

    

   

        printf("\nSending to Host(%s) Port(%d) Timeout(%d) Cmd:\n"
            "%s\n-----------------------------",
            szHost, nPort, nTime);
         nSelectWinsock(nPort, nTime, szHost);
        return 0;
    
}

int nSelectWinsock (int nPort, int nTime,
                    char *szHost)
{
    int s;
    struct hostent *hp;
    struct sockaddr_in sin;
    WSAData ws;
    int  nLen, nRet=0;
    
    const int BUFF_SIZE=4000;
    char rcvBuff[BUFF_SIZE], cNextCmd[300];

    
    nRet=WSAStartup(0x0101,&ws);
    WSASetLastError(0);
    printf("WSAStartup ..%ld",nRet);
  
    WSASetLastError(0);

    hp = gethostbyname(szHost);
    if(hp  == NULL)
		{
                printf("%s: unknown host.", szHost);
		return -1;
		}
    printf("Host Found\n");

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		{
		printf("----------client: socket():%d:%s", 
                WSAGetLastError(),strerror(errno));
		return -1;
		}
    printf("Socket Created\n");

		
    sin.sin_family = AF_INET;
    sin.sin_port = htons(nPort);
    memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
		{
		printf("--------------client: connect():%d", WSAGetLastError());
		return -1;
		}

    printf("Connection Made\n");

    printf("\nEnter Cmd Line \n");
    
    gets(cNextCmd);
    nLen = strlen(cNextCmd);
    //Add Carriage Return + New Line
    cNextCmd[nLen] = '\r';
    cNextCmd[nLen+1] = '\n';
    cNextCmd[nLen+2] = 0;  // Terminate String
        
    if (strlen(cNextCmd) > 0)
     {
       send(s, cNextCmd, strlen(cNextCmd), 0);
       printf("Command Sent\n");
     }
    
	
    struct timeval stv;
    long lBytes;
    int iMaxFd, iNumInputs, nContinue = 1;
    fd_set fdCheck;

/* Wait for output */

    for (; nContinue;)
    {        
        nContinue = 0; // Only reading >0 bytes will continue
	stv.tv_sec = nTime;
	stv.tv_usec = 0;
	FD_ZERO(&fdCheck);
	iMaxFd = s;		
	iMaxFd += 1;

	FD_SET(s, &fdCheck);
            printf("Waiting in select\n");
	    iNumInputs = select(iMaxFd, &fdCheck, 0, 0, &stv);
	    if (iNumInputs == -1)
		    {
		    /* Error*/
		    printf("------------Select Err(%d):%s\n==> die", 
                    errno, strerror(errno));
		    }
	    else if (iNumInputs == 0)
		    {
		    /* Timeout */
		    printf("Timeout from select");
		    }
	    else if (FD_ISSET(s, &fdCheck))
		    {
		    lBytes = recv(s, rcvBuff,
                               (BUFF_SIZE - 1), 0);
		    printf("Received %d Bytes\n", lBytes);

		    if (lBytes > 0)
			    {
                             rcvBuff[lBytes] =0;
                             printf("RCV: %s\n", rcvBuff);
                             nContinue=1;
			    }
		    else if (lBytes == 0)
			    {
                              printf("Graceful termination");
                              nRet = 0;	
			    }
            else
                {
                printf("----------------Socket Error: %d", WSAGetLastError());                
                nRet = -1;
                }
		
        }		
    }
    closesocket(s);
    WSACleanup();
return nRet;
}