Friday 26 June 2015

Socket API C Programs That Compile For Both UNIX/Linux and Windows

This web page is a good starting point for anyone who wants to learn how to program with the UNIX socket API :
http://www.cs.dartmouth.edu/~campbell/cs60/socketprogramming.html

Many of the IP connected applications I have written need to support both Windows and Unix/Linux. There are some minor differences between the two socket APIs but these are primarily related to configuration.

Once the code has been configured the vast majority of the code will be the same for both operating systems.

These programs use the following predefined macros to select between Windows and Unix/Linux :

#if (_WIN32) || (_WIN64)

Here are versions of the code included in the above URL, that compile on both Windows and Linux.

echoServer.c


#if (_WIN32) || (_WIN64)
#undef UNICODE
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAXLINE 4096 /*max text line length*/
#define SERV_PORT 3000 /*port*/
#define LISTENQ 8 /*maximum number of client connections */

int main (int argc, char **argv)
{
 int n;
 socklen_t clilen;
 char buf[MAXLINE];
#if (_WIN32) || (_WIN64)
 SOCKET listenfd, connfd;
 WSADATA wsaData;
 int iResult;
#else
 int listenfd, connfd;
#endif

 struct sockaddr_in cliaddr, servaddr;

#if (_WIN32) || (_WIN64)
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
#endif

 //creation of the socket
 listenfd = socket (AF_INET, SOCK_STREAM, 0);

 //preparation of the socket address 
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servaddr.sin_port = htons(SERV_PORT);

 bind (listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

 listen (listenfd, LISTENQ);

 printf("%s\n","Server running...waiting for connections.");

 for ( ; ; ) {

  clilen = sizeof(cliaddr);
  connfd = accept (listenfd, (struct sockaddr *) &cliaddr, &clilen);
  printf("%s\n","Received request...");

  while ( (n = recv(connfd, buf, MAXLINE,0)) > 0)  {
   printf("%s","String received from and resent to the client:");
   puts(buf);
   send(connfd, buf, n, 0);
  }

 if (n < 0) {
  perror("Read error"); 
  exit(1);
 }
 close(connfd);

 }
 //close listening socket
 close (listenfd);

#if (_WIN32) || (_WIN64)
 WSACleanup();
#endif
}

echoClient.c


#if (_WIN32) || (_WIN64)
#undef UNICODE
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define MAXLINE 4096 /*max text line length*/
#define SERV_PORT 3000 /*port*/

int
main(int argc, char **argv) 
{
 struct sockaddr_in servaddr;
 char sendline[MAXLINE], recvline[MAXLINE];
#if (_WIN32) || (_WIN64)
 SOCKET sockfd;
 WSADATA wsaData;
 int iResult;
#else
 int sockfd;
#endif

#if (_WIN32) || (_WIN64)
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
#endif

 //basic check of the arguments
 //additional checks can be inserted
 if (argc !=2) {
  perror("Usage: TCPClient <IP address of the server>"); 
  exit(1);
 }
 //Create a socket for the client
 //If sockfd<0 there was an error in the creation of the socket
 if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) <0) {
  perror("Problem in creating the socket");
  exit(2);
 }
 //Creation of the socket
 memset(&servaddr, 0, sizeof(servaddr));
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr= inet_addr(argv[1]);
 servaddr.sin_port =  htons(SERV_PORT); //convert to big-endian order
 //Connection of the client to the socket 
 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))<0) {
  perror("Problem in connecting to the server");
  exit(3);
 }
 while (fgets(sendline, MAXLINE, stdin) != NULL) {
  send(sockfd, sendline, strlen(sendline), 0);
  if (recv(sockfd, recvline, MAXLINE,0) == 0){
   //error: server terminated prematurely
   perror("The server terminated prematurely"); 
   exit(4);
  }
  printf("%s", "String received from the server: ");
  fputs(recvline, stdout);
 }

#if (_WIN32) || (_WIN64)
 WSACleanup();
#endif

 exit(0);
}

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.
Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/