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.
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.
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; }
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; }
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; }
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; }