/*
SERVER.c: TCP Server Demo program for i-7188E series
          A server listen and wait to handle the coming connections

Compiler: BC++ 3.1, 
          Turbo C++ 1.01(3.01) 
          
Compile mode: large

Project: Server.C
         ..\lib\g4500.lib
         ..\lib\tcp_dm32.lib

Details:
    Setup a server, wait for connections.
    While a connection has been established, receive data from clients.

[2010/05/18]   Modify by yide    
[30 Apr, 2008] by Liam
*/

#include <string.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "..\lib\g4500.h"
#include "..\lib\Tcpip32.h"

#define SOCKET_NUM      32                      // define the maximum number of sockets 
#define SOCKET_QUEUE_LENGTH SOCKET_NUM -1       // maximum connection queue length 
#define NUM_OF_SERVICE_PORT     2

unsigned short service_port[NUM_OF_SERVICE_PORT]={10000, 10001};    // define the service port for Server
struct timeval seltime={0, 20};     // 2000ns=2ms

unsigned ActiveSkt[2]={0, 0};
STOPWATCH swSocket[SOCKET_NUM], swTimer; 
          
ulong ulSocketTimeout=86400000UL;   // Sets the socket communication timeout value. 
                                    // If the timeout expires, the connection will be released 
                                    // Time unit: ms

fd_set rfds;    // set of socket file descriptors for reading

typedef struct tagSocket_State 
{
    int                 active;                 // socket is active 
    int                 init;                   // socket just initiated 
    struct sockaddr_in  sin;                    // client address
    unsigned            port;                   // port number
    int                 be_master_socket;       // is master socket
} socket_state;

socket_state socket_state_table[SOCKET_NUM];    // sockets state table 
struct ip host_ip;

int TCP_server_initial(void)
{
    /*
    Return:
         0: ok.
        -1: function "llip" error
        -2: function "Ninit" error
        -3: function "Portinit" error
    */
    
    int i, iRet;
 
    GetSavedIp((struct ip*) &host_ip);   // read the saved IP address and mask from EEPROM of 7188E
    iRet=lhip("7186EX", &host_ip);

    iRet=Ninit();           // initiate host environment 
    if(iRet!=NoError)
        return -2;

    iRet=Portinit("*");     // initiate all network device 
    if(iRet!=NoError) 
    {
        Nterm();
        return -3;
    }

    FD_ZERO(&rfds);
  
    for(i=0; i<SOCKET_NUM; i++) 
    {
        socket_state_table[i].active=0;
        socket_state_table[i].init=0;
        socket_state_table[i].port=-1;
        socket_state_table[i].be_master_socket=0;
        memset(&socket_state_table[i].sin, 0, sizeof(socket_state_table[i].sin));   // bzero is a unix system call
    }

    return 0;
}

int create_socket_to_listen(unsigned port_number)
{
    /*
    port_number: port number to listen

    return: -1      sokcet create error
            -2      bind a socket error
            -3      listen on a socket error
             0      success  
    */
    
    int err;
    int socket_for_listen=socket(PF_INET, SOCK_STREAM, 0);
  
    if(socket_for_listen<0) 
    {
        Nterm();
        return -1;
    }

    // addressing for master socket
    memset(&socket_state_table[socket_for_listen].sin, 0, sizeof(socket_state_table[socket_for_listen].sin));
    socket_state_table[socket_for_listen].sin.sin_family=AF_INET;
    socket_state_table[socket_for_listen].sin.sin_addr.s_addr=0;    // INADDR_ANY is a 32bits zero value 
    socket_state_table[socket_for_listen].sin.sin_port=htons(port_number);

    err=bind(socket_for_listen, (struct sockaddr *)&socket_state_table[socket_for_listen].sin, sizeof(socket_state_table[socket_for_listen].sin));
    if(err<0) 
    {
        closesocket(socket_for_listen);
        Nterm();
        return -2;
    }
    
    err=listen(socket_for_listen, SOCKET_QUEUE_LENGTH);
    if(err<0) 
    {
        closesocket(socket_for_listen);
        Nterm();
        return -3;
    }
    
    socket_state_table[socket_for_listen].active=1;
    socket_state_table[socket_for_listen].port=port_number;
    socket_state_table[socket_for_listen].be_master_socket=1;

    return err;     // err == 0 for success
}

int wait_to_accept(void)
{
    /*
    return: -1      select socket error
            -2      select socket timeout
            -3      accept error
             0      success    
    */
    int i;
    int err;
    int client_length;
    struct sockaddr_in client_addr;     // internet socket address for client 
    
    client_length=sizeof(client_addr);
    
    for(i=0; i<SOCKET_NUM; i++)
    {
        if(socket_state_table[i].active)
        {
            FD_SET(i, &rfds);   // re-join active sockets
        }
    }

    err=selectsocket(SOCKET_NUM, &rfds, NULL, NULL, &seltime);
    if(err<0) 
        return -1;
    else if(err==0)
        return -2;

    for(i=0; i<SOCKET_NUM; i++)     //for each master socket
    { 
        if(FD_ISSET(i, &rfds))
        {
            if(socket_state_table[i].be_master_socket==1)
            {
                memset(&client_addr, 0, sizeof(client_addr));
                err=accept(i, (struct sockaddr *)&client_addr, &client_length);
                if(err<0) 
                    return -3;
      
                FD_SET(err, &rfds);     //set the socket accepted by accept()
                socket_state_table[err].active=1;
                socket_state_table[err].init=1;
                socket_state_table[err].port=socket_state_table[i].port;
                memcpy(&socket_state_table[err].sin, &client_addr, sizeof(client_addr));
            
                T_StopWatchStart(&swSocket[err]);   // for socket timeout
            
                if(err>=16)
                    ActiveSkt[1]|=1<<(err-16);
                else
                    ActiveSkt[0]|=1<<err;
                    
                FD_CLR(err, &rfds);
                
                Print("Accept one Client\r\n");
            }
        }
    }
    
    return 0;   //success
}

void TCP_server_shutdown(void)
{
    int i;
    
    FD_ZERO(&rfds);     // clear FD set 
  
    for(i=0; i<SOCKET_NUM; i++) 
    {
        if(socket_state_table[i].active)    // if socket lives, kill it 
            closesocket(i);
    }
}

void TCP_close_socket(int current_socket)
{
    if(current_socket>=16)
        ActiveSkt[1]&=~(1<<(current_socket-16));
	else
	    ActiveSkt[0]&=~(1<<current_socket);
	
    socket_state_table[current_socket].active=0;
    socket_state_table[current_socket].port=-1;
    shutdown(current_socket, 0);
    closesocket(current_socket);
}

void Port10000(int current_socket)
{
    // current_socket: Socket number
  
    int iRet;
    char buf[1024];

    iRet=recv(current_socket, buf, 1024, 0);
    //Print("iRet=%d\r\n", iRet);
    if(iRet<=0)
    {
        TCP_close_socket(current_socket);
        return;
    }
    
    buf[iRet]=0x0;
    Print("Echo for Client(10000)=>%s\r\n", buf);
    send(current_socket, buf, strlen(buf), 0);
    
    T_StopWatchStart(&swSocket[current_socket]);
}

void Port10001(int current_socket)
{
    // current_socket: Socket number
  
    int iRet;
    char buf[1024];

    iRet=recv(current_socket, buf, 1024, 0);
    
    if(iRet<=0)
    {
        TCP_close_socket(current_socket);
        return;
    }

    buf[iRet]=0x0;
    Print("Echo for Client(10001)=>%s\r\n", buf);
    send(current_socket, buf, strlen(buf), 0);
        
    T_StopWatchStart(&swSocket[current_socket]);
}

void main(void)
{
    int err, i, service_loop_continue;
   
    InitLib();
    
    ulSocketTimeout=10000;  // socket timeout=10000 ms

//Step 1: Initialize TCP Server
    err=TCP_server_initial();    
    if(err<0)
    {
        if(err==-1)
            Print("This program must be executed in uPAC-7186EX series.\r\n");
        if(err==-2)
            Print("Ninit() failed! error %d\r\n", err);
        if(err==-3)
            Print("Portinit() failed! error %d\r\n", err);
    }
    else Print("Initialize TCP Server\r\n");
    

//Step 2: Create sockets and bound to names then listen for connections
    for(i=0; i<NUM_OF_SERVICE_PORT; i++)
    {
        err=create_socket_to_listen(service_port[i]);
        if(err>=0)
            continue;  // success
        
        if(err==-1)
            Print("cannot create a socket on port: %d", service_port[i]);
        if(err==-2)
            Print("cannot bind to port %d \n\r", service_port[i]);
        if(err==-3)
            Print("cannot listen on port %d \n\r", service_port[i]);
    }

//Step 3: Service loop:     3-1: wait on sockets and accept
//                          3-2: perform service, receive and send
    service_loop_continue=1;
    
    T_StopWatchStart(&swTimer);
    
    while(service_loop_continue)    // service loop 
    {
        // check the socket timeout at 50 ms intervals
        if(T_StopWatchGetTime(&swTimer)>=50)
        {
	        T_StopWatchStart(&swTimer);

            if(ulSocketTimeout)
            {
                static int iSocket=0;
                int mask;

		        if(iSocket>=16)
		            mask=ActiveSkt[1]&(1<<(iSocket-16));
		        else
		            mask=ActiveSkt[0]&(1<<iSocket);

                if(mask && T_StopWatchGetTime(&swSocket[iSocket])>=ulSocketTimeout)
                {
                    Print("Socket timeout, socket#%d closed\r\n", iSocket);
                    TCP_close_socket(iSocket);
                }
                
                iSocket++;
         
                if(iSocket>=SOCKET_NUM)
                    iSocket=0;
            }
        }
        
        YIELD();
        
//Step 3-1: Wait for activity on sockets and accept a connection
        err=wait_to_accept();
        if(err==-1)
        {
            Print("selectsocket error\n\r");
            break;
        }
        else if(err==-2)
            continue;       //selectsocket() time out
        else if(err==-3)
            Print("accept error %d\n\r");        
            
//Step 3-2: perform service, receive and send
        for(i=0; i<SOCKET_NUM; i++)     // scan all possible sockets 
        { 
            if(FD_ISSET(i, &rfds)) 
            {
                if(socket_state_table[i].be_master_socket!=1)  // slave sockets
                {
                    if(socket_state_table[i].port==10000)
                        Port10000(i);
                    
                    if(socket_state_table[i].port==10001)
                        Port10001(i);
                }
                
                FD_CLR(i, &rfds);
            }
        }
    }

//Step 4: Shutdown the TCP server   
    TCP_server_shutdown();
}
