/*************************************************************************** 
Name:             selectchatclient.cpp 
Autor:            www.c-worker.ch 
Comments:      deutsch/english 

Beschreibung:  
Zeigt wie man blocking calls umgehen kann indem man select verwendet. 
Dies ist der Client für selectchatsrv.cpp 
ACHTUNG: du wirst nicht sehen was du eingibst, bis du enter drückst 
um die nachricht abzusenden!! 


Description: 
The client for selectchatsrv.cpp 
ATTENTION: you wan't see what you type, until you press enter to 
send the message! 
             
***************************************************************************/ 

#include <stdlib.h> 
#include <stdio.h> 
#include <windows.h> 
#include <winsock2.h> 
#include <conio.h> 


// Startet Winsock und gibt einige Infos zur Version aus 
// Starts winsock and dumps some infos about the version 
int startWinsock() { 
   int rc; 
   WSADATA wsaData; 
   rc=WSAStartup(MAKEWORD(2,0),&wsaData); 
   printf("Return Code: %d\n",rc); 
   if(rc==SOCKET_ERROR) { 
      printf("Error, exiting!\n"); 
      return rc; 
   } 
   printf("Winsock started:\n"); 
   printf("Version: %d.%d\n",LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion)); 
   printf("High Version: %d.%d\n",LOBYTE(wsaData.wHighVersion),HIBYTE(wsaData.wHighVersion)); 
   printf("Description: %s\n",wsaData.szDescription); 
   printf("System Status: %s\n",wsaData.szSystemStatus); 
   return 0; 
} 

// Sucht eine IP anhand eines Strings, der entweder die IP als String 
// oder einen Hostname enthalten kann 
// Searches an IP based on a string, which can contain an IP as string 
// or a hostname 
long getAddrFromString(char* hostnameOrIp, SOCKADDR_IN* addr) { 
   long rc; 
   unsigned long ip; 

   HOSTENT* he; 

   if(hostnameOrIp==NULL || addr==NULL) 
      return SOCKET_ERROR; 
   ip=inet_addr(hostnameOrIp); 

   if(ip!=INADDR_NONE){ 
      addr->sin_addr.s_addr=ip; 
      return 0; 
   } else { 
      he=gethostbyname(hostnameOrIp); 
      if(he==NULL) { 
         return SOCKET_ERROR; 
      } else { 
         memcpy(&(addr->sin_addr),he->h_addr_list[0],4); 
      } 
      return 0; 
   } 
} 

// main... 
int main(int argc, char** argv) { 

   SOCKET s; 
   SOCKADDR_IN addr; 
   char c; 
   char buf[1024]; 
   char inpBuf[1024]; 
   int inpBufPos=0; 
   fd_set fdSetRead; 
   TIMEVAL timeout; 
   int rc; 


   if(argc<2) { 
      printf("Usage: chatclient <server>\n"); 
      return 1; 
   } 

   // start winsock 
   rc=startWinsock(); 
   if(rc==SOCKET_ERROR) 
      return 1; 
       
   // addr vorbereiten, hostname auflösen 
   // prepare addr, resolve hostname 
   memset(&addr,0,sizeof(SOCKADDR_IN)); 
   addr.sin_family=AF_INET; 
   addr.sin_port=htons(1234); 
   rc=getAddrFromString(argv[1],&addr); 
   if(rc==SOCKET_ERROR) { 
      printf("Error: Cannot resolve Host %s\n", argv[1]); 
      return 1; 
   } 

   // socket erstellen 
   // create socket 
   s=socket(PF_INET,SOCK_STREAM,0); 
   if(s==INVALID_SOCKET) { 
      printf("Error, cannot create socket: %d\n",WSAGetLastError()); 
      return 1; 
   } 

   // verbinden.. 
   // connect.. 
   printf("Connecting...\n"); 
   rc=connect(s,(SOCKADDR*)&addr,sizeof(SOCKADDR)); 
   if(rc==SOCKET_ERROR) { 
      printf("Error: connect failed: %d\n",WSAGetLastError()); 
      return 1; 
   } 

   printf("\n"); 
    
    
   while(rc!=SOCKET_ERROR) { 
    
      // keyboard input ? 
      while(kbhit()) { 
         c=getch(); 
         if(c==13) { 
            rc=send(s,inpBuf,inpBufPos,0); 
            inpBuf[0]='\0'; 
            inpBufPos=0; 
         } else { 
            inpBuf[inpBufPos++]=c; 
         } 
      } 
      inpBuf[inpBufPos]='\0'; 
       
       
      // fd_set und timeout vorbereiten 
      // prepare fd_set and timeout 
      FD_ZERO(&fdSetRead); 
      FD_SET(s,&fdSetRead); 
      timeout.tv_sec=0; 
      timeout.tv_usec=0; 

      // prüfen ob ein socket bereit ist, da timeout=0 kehrt die funktion 
      // sofort wieder zurück nach dem aufruf. 
      // achtung: das timeout auf 0 setzen oder als paremeter NULL mitgeben 
      // ist NICHT das gleiche. auf 0 gesetzt kehrt sofort zurück, während 
      // NULL blockt. 
      // check if any socket is ready (timeout=0, the function returns without delay) 
      while((rc=select(0,&fdSetRead,NULL,NULL,&timeout))>0) { 
            rc=recv(s,buf,1023,0); 
            // server hat die verbindung beendet ? 
            // server closed connection ? 
            if(rc==0) { 
                printf("Server closed connection!\n"); 
                return 1; 
            // fehler: beenden! 
            // error: abort! 
            } else if(rc==SOCKET_ERROR) { 
                printf("Error: recv failed: %d\n",WSAGetLastError()); 
                return 1; 
            } 
            // empfangene daten ausgeben 
            // print received data 
            buf[rc]='\0'; 
            printf("%s\n",buf); 
      } 
       
   } 

   // aufräumen 
   // cleanup.. 
   closesocket(s); 
   WSACleanup(); 
   printf("Client shutdown, press any key to exit\n"); 
   getch(); 
   return 0; 
} 

