// ==========================================================================
// Author:  Yee Hsu
// Date:    6/4/2005
// File:    Server.cpp
//
// Desc:    This is a TCP and UDP multi-server. Server is able to open and
//          listen to open connections and allow either TCP or UDP connections.
//          Server opens a communication port and allows incomming connections
//          to be established with the server. Server performs request
//          by taking in request and replying with messages back to the client.
//
//          Server accepts connection from either Windows/Linux and can run as
//          a Windows service or Linux daemon. Code is written in portable
//          format to compile in both Windows/Linux.
// ==========================================================================

/* Global include files for Windows & Unix */
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#ifdef _WIN32

/* Socket include file for Windows */
#include <winsock.h>
#pragma comment(lib, "wsock32.lib")
#define close(sd) closesocket(sd);

#else

/* Socket include files for Unix */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#endif

/* Global define constants */
#define PORT_NUM    0
#define MAXCLIENT   5
#define MAXBUFFER   256

// macro to return the max of two integers
#define MAX(x,y)    ((x) > (y) ? (x) : (y))

// ==========================================================================
// Identifier:  main()
//
// Description: main routine. Here is where the program starts.
// ==========================================================================

int main(int argc, char* argv[])
{
    // initialize some variables
    int usd, tsd, alen, nfds;
    struct sockaddr_in sin, tsin;
    fd_set rdfs;
    char msg[MAXBUFFER];

#ifdef _WIN32

    // initialize Winsock DLL files
    WORD wVersionRequested;
    WSADATA wsaData;
    int err = 0;

    wVersionRequested = MAKEWORD(2,0);
    err = WSAStartup(wVersionRequested, &wsaData);

    if (err != 0)
    {
        printf("Error: unable to initialize WinSock DLL\n");
        exit(0);
    }
#endif

    // lets open a port to take incomming connections
    memset(&sin, '\0', sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(PORT_NUM);

    // create a UDP socket
    if ((usd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("Error: UDP socket()\n");
        exit(0);
    }

    // bind to the socket
    if (bind(usd, (struct sockaddr*) &sin, sizeof(sin)) < 0)
    {
        printf("Error: UDP bind()\n");
        exit(0);
    }

    alen = sizeof(tsin);

    // get the socket name
    if (getsockname(usd, (struct sockaddr*) &tsin, &alen) < 0)
    {
        printf("Error: getsocketname()\n");
        exit(0);
    }

    sin.sin_port = tsin.sin_port;

    // create a TCP socket
    if ((tsd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Error: TCP socket()\n");
        exit(0);
    }

    // bind to the socket
    if (bind(tsd, (struct sockaddr*) &sin, sizeof(sin)) < 0)
    {
        printf("Error: TCP bind()\n");
        exit(0);
    }

    // listen for incomming connections to your port
    if (listen(tsd, MAXCLIENT) < 0)
    {
        printf("Error: listen()\n");
        exit(0);
    }

    printf("UDP and TCP multiserver online: %d\n", ntohs(tsin.sin_port));

    FD_ZERO(&rdfs);
    nfds = MAX(usd,tsd) + 1;

    // infinite loop, this is where the server runs indefinitely to serve the client
    while (1)
    {
        // connect and server either TCP or UDP connections
        FD_SET(tsd, &rdfs);
        FD_SET(usd, &rdfs);

        // select which channel to support
        if (select(nfds, &rdfs, (fd_set*)0, (fd_set*)0, (struct timeval*)0) < 0)
        {
            printf("Error: select()\n");
            exit(0);
        }

        // serve the TCP connection
        if (FD_ISSET(tsd, &rdfs))
        {
            int sd;
            alen = sizeof(sin);

            if ((sd = accept(tsd, (struct sockaddr*) &sin, &alen)) < 0)
            {
                printf("Error: TCP accept()\n");
                exit(0);
            }

            if (recv(sd, msg, sizeof(msg), 0) < 0)
            {
                printf("Error: TCP recv()\n");
                exit(0);
            }

            //////////////////////////////////////
            printf("TCP: %s\n", msg);
            strcpy(msg, "GOT IT!");
            //////////////////////////////////////

            if (send(sd, msg, strlen(msg)+1, 0) < 0)
            {
                printf("Error: TCP send()\n");
                exit(0);
            }
            close(sd);
        }

        // server the UDP connection
        if (FD_ISSET(usd, &rdfs))
        {
            alen = sizeof(sin);

            if (recvfrom(usd, msg, sizeof(msg), 0, (struct sockaddr*) &sin, &alen) < 0)
            {
                printf("Error: UDP recvfrom()\n");
                exit(0);
            }

            //////////////////////////////////////
            printf("UDP: %s\n", msg);
            strcpy(msg, "GOT IT!");
            //////////////////////////////////////

            if (sendto(usd, msg, sizeof(msg), 0, (struct sockaddr*) &sin, sizeof(sin)) < 0)
            {
                printf("Error: UDP sendto()\n");
                exit(0);
            }
        }
    }

#ifdef _WIN32
    // perform all cleanups when connections close
    WSACleanup();
#endif 

    return 0;
}