---
# System prepended metadata

title: '運用 OOP 觀念與 pthread, callback function, nonblocking connect 寫成的簡單 HTTP server'
tags: [OOP, network programming, callback, http server, GET/POST, thread, nonblocking, C LANGUAGE]

---

# 運用 OOP 觀念與 pthread, callback function, nonblocking connect 寫成的簡單 HTTP server
# 
###### tags: `C LANGUAGE` `network programming` `thread` `OOP` `nonblocking` `http server` `callback` `GET/POST`
<style>
.blue {
  color: blue;
}
.bgblue {
  color: blue;
    font-size: 24px;
    font-weight: bold; 
}    
.red {
  color: red;
  font-size: 24px;
   font-weight: bold;  
}    
  h1 {text-align: center;}  
</style>

Author: WhoAmI
Date: 20220728
email: kccddb@gmail.com
Copyright: CC BY-NC-SA


nonblocking connect: shttp.c
non-blocking connect 的重要用途之一就是選擇反映最快的 server  的方法之一
(您可自行修改使用)





請自行準備 index.html 檔案供實驗




:::info
servercpp.h
```c=
#define MAXCLIENTNO 20

typedef struct _CServercpp {

 int fd;
    unsigned short int port;
    int time_out;
    struct sockaddr client_addr[MAXCLIENTNO];
 
    int (*on_read)(struct _CServercpp *pServer,char *buf,int len);
    int client_fd[MAXCLIENTNO];
    int client_num;
    int active_fd;
    int (*on_accept)(struct _CServercpp *pServer);
    int fdmax;

    int (*on_callback)(void *items,int argc, char* argv[]);
    int (*callback[MAXCLIENTNO])(void *item,int argc, char* argv[]);
    void(*closefd)(struct _CServercpp *pServer,int fd);
    void(*close)(struct _CServercpp *pServer);

    pthread_mutex_t demomutex;

}CServer;
CServer *CServer_init(unsigned short int port);
void CServer_exit(CServer *pServer);


int writet(int fd,char *buf,int len,int msecs);
int readt(int fd,char *buf,int len,int msecs);

int read_send(int fd,char *fname);
```

:::
:::info
servercpp.c 

```c=

/*

http simple server, OOP version
pthread version
gcc servercpp.c -o servercpp -lpthread
.
GET/POST Query string decode!

callback function


One can change the default callback function


Author: WhoAmI,20191215




*/





#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <pthread.h>

#include <sys/stat.h>
#include <fcntl.h>

#include <time.h>
#include "servercpp.h"

//#define TEST

#define debug000(x)
#define debug001(x) x
#define debug003(x)
#define HTTPLEN 4000
#define HTTPheader "HTTP/1.1 200 OK\nContent-Type: text/html;  charset=UTF-8\r\n\r\n"
#define HTTPbody "<html><body><p style='color:Tomato;' >Welcome! HTTP header:</p><BR> %s <BR></body></html>"


#define DEFAULTMSECS 50

static int callback(void *item,int argc, char* argv[]);


static int bind_server(unsigned short int port)
{
    int server_fd;
    int yes;

    struct sockaddr_in server_addr;

    yes=1;

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }
    if (setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
    {
        perror("setsockopt");
        close(server_fd);
        return -1;

    }

    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);

    if (bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))==-1)
    {
        perror("bind");

        close(server_fd);
        return -1;

    }
    if (listen(server_fd,50) == -1)
    {
        perror("listen");
        close(server_fd);

    }
    printf("[1] Create TCP Server on %d\n",port);
    return server_fd;

}

int writet(int fd,char *buf,int len,int msecs)
{
    fd_set w_fds;
    struct timeval tv;
    int n;
    int m,j;

    tv.tv_sec = msecs/1000;
    tv.tv_usec = msecs*1000;
    FD_ZERO(&w_fds);
    FD_SET(fd,&w_fds);

    m=0;
    j=0;
    do
    {

        FD_SET(fd,&w_fds);
        if(msecs<=0)
        {
            n = select(fd+1, NULL, &w_fds, NULL,  NULL );
        }
        else
        {

            n = select(fd+1, NULL, &w_fds, NULL,  &tv );
        }

        switch(n)
        {
            case 0:

                errno = ETIMEDOUT;
                debug000(printf("Write timeout!\n"));
                return j; //20191215

                break;
            case -1:
                return -1;

                default:
                if( FD_ISSET(fd,&w_fds))
                {
                    m=write(fd,buf+j,len-j);
                    j=j+m;
                }

                break;
        }

    }
    while(m>0);
}


int readt(int fd,char *buf,int len,int msecs)
{
    fd_set r_fds;
    struct timeval tv;
    int n;
    int m,j;

    tv.tv_sec = msecs/1000;
    tv.tv_usec = msecs*1000;
    FD_ZERO(&r_fds);
    FD_SET(fd,&r_fds);

    m=0;
    j=0;
    do
    {


        FD_SET(fd,&r_fds);
        if(msecs<=0)
        {
            n = select(fd+1, &r_fds, NULL, NULL,  NULL );
        }
        {
            n = select(fd+1, &r_fds, NULL, NULL,  &tv );
        }

        debug000(printf("Read [%d] %d:%d usecs\n",n, tv.tv_sec,tv.tv_usec));
        switch(n)
        {
            case 0:

                errno = ETIMEDOUT;
                debug003(printf("Read timeout!\n"));

                return j; //20191215

                break;
            case -1:
                return -1;

                default:
                if( FD_ISSET(fd,&r_fds))
                {
                    m=read(fd,buf+j,len-j);
                    j=j+m;
                }

                break;
        }

    }
    while(m>0);

    return j;
}

static int default_read(CServer *pServer,char *buf,int len)
{
    //default read op

    int n;
    debug000(printf("Enter Default Read...\n"));
    n=read( pServer->active_fd,buf,len);  //for each connection

    return n;

}

static void closefd(CServer *pServer,int fd)
{

    int i;

    for(i=0;i<MAXCLIENTNO;i++)
    {
        if(fd==pServer->client_fd[i])
        {
            close(pServer->client_fd[i]);

            pServer->client_fd[i]=0;
            pServer->client_num--;
            pServer->active_fd=0;   //set zero

            break;
        }

    }


}


unsigned int chkfile(char *fname)
{
    unsigned int ssize;
    struct stat ssb;
    stat(fname, &ssb);
    ssize=(unsigned int)ssb.st_size;

    return ssize;
}

int read_send(int fd,char *fname)
{

    int rfd;
    void *mem;
    unsigned int ssize;

    ssize=chkfile(fname);

    if(ssize>0)
    {
        rfd=open(fname, O_RDONLY);
        if(rfd>0)
        {

            mem=malloc(ssize);

            if(mem)
            {
                readt(rfd,mem,ssize,DEFAULTMSECS);
                writet(fd,mem,ssize,DEFAULTMSECS);
                free(mem);
            }
            close(rfd);

        }
    }
    return ssize;

}

static void urldecode2(char *dst, const char *src)
{
    char a, b;
    while(*src)
    {
        if((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b)))
        {
            if(a >= 'a')
                a -= 'a'-'A';
            if(a >= 'A')
                a -= ('A' - 10);
            else
                a -= '0';
            if(b >= 'a')
                b -= 'a'-'A';
            if(b >= 'A')
                b -= ('A' - 10);
            else
                b -= '0';
            *dst++ = 16*a+b;
            src+=3;
        }
        else if(*src == '+')
        {
            *dst++ = ' ';
            src++;
        }
        else
        {
            *dst++ = *src++;
        }
    }
    *dst++ = '\0';
}



static int callback(void *item,int argc, char* argv[])

{

    int fd,n, *p;

    char buf[HTTPLEN];
    char obuf[HTTPLEN];
    char url[1000];
    int len;
    int ispost;

    unsigned char *pget,*q,*query;

    CServer *pServer;

    pServer=( CServer  *)item;

    len=HTTPLEN-1;

    ispost=0;
    /* store it to local fd ASAP

       pServer->active_fd may be changed! (if high-speed!)

    */

    fd=pServer->active_fd;

    debug000(printf("Enter Default Thread...fd=%d\n",fd));

    do
    {
        n=readt(fd ,buf,len,DEFAULTMSECS);  //for each connection
        if(n>0)
        {

            buf[n]=0;
            debug000(printf("%s\n",buf));

            if(strstr(buf,"HTTP/"))
            {

                pget=strstr(buf,"GET ");
                if(pget)
                {
                    pget=pget+5;

                }
                else
                {
                    pget= strstr(buf,"POST ");
                    if(pget)
                    {

                        printf("POST method\n");
                        pget=pget+6;
                        ispost=1;

                        query=strstr(pget,"\r\n\r\n");
                        query=query+2;

                        printf("Query string=%s",query);

                    }

                }

                for(q=pget;*q>' ' && *q!='?';q++)
                {

                }
                *q=0;

                if(ispost==0)
                {

                    query=q+1;

                    for(q=query;*q>' ' && *q!='\r';q++)
                    {
                        *q=0;

                    }
                }

                url[0]=0;
                if(strlen(query)>0)urldecode2(url,query);
                printf("GET(POST) [%s] Query:[%s]\n",pget,url);

                write(fd,HTTPheader,strlen(HTTPheader));

                if(read_send(fd,pget)<0)
                {

                    sprintf(obuf,HTTPbody,buf);
                    write(fd,obuf,strlen(obuf));
                }
                else
                {
                 
                    break;
                }
            }

        }
        else
        {

            debug000(printf("Get ret=%d\n",n));
        }
    }
    while(n>=0);




}

static void *pserver(void *pthreadid)
{
    CServer *pServer;
    int fd;

    int i,n;
    int active_num;
    int (*on_callback)(void *item,int argc, char* argv[]);


    //FIXME: Un-stable for high-speed connection!

    debug001(printf("Thread start!\n"));

    pServer  = (CServer *)pthreadid;


    pthread_mutex_lock(&pServer->demomutex);
    //idp = (int *) pthreadid;
    fd = pServer->active_fd;   //FIXME: maybe in-correct! get temporary fd for each thread

    debug000(printf("active fd=%d\n",fd));

    on_callback=pServer->on_callback;

    for(i=0;i<MAXCLIENTNO;i++)
    {
        if(fd==pServer->client_fd[i])
        {
            if((pServer->callback[i])!=NULL)on_callback=pServer->callback[i]; //set on_callback for each thread.

            active_num=i;
            break;
        }

    }



    pthread_mutex_unlock(&pServer->demomutex);

    //server start! on_callback is called outside the lock!

    debug000( printf("star action active=%d\n",fd));
    on_callback(pServer,0,NULL);


    pthread_mutex_lock(&pServer->demomutex);

    //maybe too fast!
    pServer->closefd(pServer,fd);



    pthread_mutex_unlock(&pServer->demomutex);

    debug001(printf("Thread fd=[%d] exists!\n",fd));

    pthread_exit((void*) pthreadid);


}



static int default_accept(CServer *pServer)
{
    //default read op

    int len;
    int fd;
    int i;
    int found;
    pthread_t child;
    found=0;
    len=sizeof(struct sockaddr);

    debug000(printf("Wait new connection\n"));

    fd=accept(pServer->fd ,(struct sockaddr *)&(pServer->client_addr) ,(socklen_t*)&len );
    if ((fd <0))
    {
        perror("accept");
        return -1;
    }

    debug000(printf("Accept new connection %d\n",fd));


    for(i=0;i<MAXCLIENTNO;i++)
    {
        if(pServer->client_fd[i]<=0)
        {

            //create one thread!


            pthread_create(&child, NULL, pserver, (void *) pServer);



            pthread_mutex_lock(&pServer->demomutex);

            pServer->active_fd=fd;   //for each connection (temporary!)

            pServer->client_fd[i]=fd;


            pthread_mutex_unlock(&pServer->demomutex);

            pthread_detach(child);
            found=1;
            break;
        }
    }

    if(found)
    {


        pServer->client_num++;
        debug000(printf("New TCP connection...fd=%d, total=%d\n",fd,pServer->client_num));
        return fd;

    }
    else
    {

        debug000(printf("Cannnt accept the TCP connection...[MAX=%d]\n",MAXCLIENTNO));

        close(fd);


    }

    return -1;

}


//Generate CServer Object
CServer *CServer_init(unsigned short int port)
{
    CServer *pthis;

    pthis=(CServer *) malloc(sizeof(CServer));
    if (pthis==NULL)
    {
        return NULL;
    }
    memset(pthis,0,sizeof(CServer)) ;
    pthis->fd=bind_server(port);
    pthis->port=port;
    pthis->on_read=default_read;
    pthis->on_accept=default_accept;

    pthis->on_callback=callback;
    pthis->closefd=closefd;

    pthis->demomutex=PTHREAD_MUTEX_INITIALIZER;
    printf("Create TCP Server on %d fd=%d\n",port,  pthis->fd);
    return pthis;


}

//Destory  CServer Object
void CServer_exit(CServer *pServer)
{

    int i;
    if(pServer)
    {

        for(i=0;i<MAXCLIENTNO;i++)
        {

            if(pServer->client_fd[i]>0) close(pServer->client_fd[i]);

        }


        close(pServer->fd);
        free( (void *)pServer);

    }
}

#ifdef TEST
    int main(int argc,char *argv[])
    {
        CServer *p;
        char buf[80];
        int n;
        p=CServer_init(9999);
 
 
        //p->on_callback=callback_echo; //change the default!
 
 
        if(p)
        {
 
            do
            {
                p->on_accept(p);
 
            }
            while(1);
 
 
            CServer_exit(p);
 
        }
 
    }
 
#endif
---------------------------------------------------------------------------
```

/*
DEMO CServer class, callback function, pthread, simple and basic HTTP server (GET/POST)...

gcc servercpp.c app.c -o app -lpthread



Author: WhoAmI(cddb.tw@gmail.com)
*/

:::

:::info
app.c
```c=
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <pthread.h>

#include <sys/stat.h>
#include <fcntl.h>

#include <time.h>

#include <sys/wait.h>
#include "servercpp.h"

#define ECHOMSECS 500
#define debug000(x) x
#define debug001(x)

static void sig_handler(int signo)
{
    pid_t pid;
    int stat;


    switch(signo)
    {

        case SIGCHLD:
            printf("SIGCHLD=%d\n",signo);
            pid=waitpid(0,&stat,WNOHANG);
            break;

        case SIGINT:
            printf("SIGINT=%d\n",signo);
            break;
        case SIGALRM:
            printf("Got SIGALRM=%d, exit!\n",signo);
            exit( EXIT_FAILURE);

            break;

        case SIGPIPE:
             printf("Got SIGPIPE=%d, exit!\n",signo);
             //exit( EXIT_FAILURE);
         
        default:
            printf("signal=%d\n",signo);

        }
   

    return;
}
static int callback_echo(void *item,int argc, char* argv[])

{

    int fd,n, *p;

    char buf[256];
    char obuf[256];
    int len;
    CServer *pServer;

    pServer=( CServer  *)item;

    len=256-1;

    /* store it to local fd ASAP

       pServer->active_fd may be changed! (if high-speed!)

       BUG! Cannot handle PUTTY close connection!

    */

    fd=pServer->active_fd;

    debug000(printf("Enter Default Echo Thread...fd=%d\n",fd));

    do
    {
        n=readt(fd ,buf,len,ECHOMSECS);  //for each connection
        if(n>0)
        {

            buf[n]=0;
            printf("%s\n",buf);

            debug000( writet(fd,buf,n,ECHOMSECS));

        }
        else
        {
           
            debug001( printf("Get ret=%d,errno=%d\n",n,errno));
            if(n<0)break;

        }
    }
    while(n>=0); //for PUTTY




}

int main(int argc,char *argv[])
{
    CServer *p;
    char buf[80];
    int n;
    int port;

    port=9999;


    if(argc>1)port=atoi(argv[1]);


    signal (SIGPIPE, sig_handler);
    p=CServer_init(port);


    //Replace default on_callback by callback_echo
    if(port==9998){
          printf("Demo simple ECHO server(port=%d)!\n",port);
          p->on_callback=callback_echo; //change the default!
    }else{

         printf("Demo simple HTTP server(port=%d)!Try http://127.0.0.1:%d/index.html\n",port,port);
       
    }


    if(p)
    {

        do
        {
            n=p->on_accept(p);

        }
        while(n>0);


        CServer_exit(p);

    }


    exit(EXIT_SUCCESS);
}
```
:::


:::info

shttp.c
```c=
/*

Simple send http header
non-blocking connect

Author: WhoAmI(cddb.tw@gmail.com)
DATE:   2019/12/19

DNS 相關請自行參考 Beej's Guide to Network Programming

You may need getaddrinfo (Linux man 3), please STUDY DNS and the following four files



/etc/host.conf
resolver configuration file
/etc/hosts
host database file
/etc/nsswitch.conf
name service switch configuration

/etc/gai.conf (configuration file)



*/


#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <signal.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>

//errno
#include <errno.h>



/*

HTTP client


GET /index.html?ip=1234567&name=jone HTTP/1.1
Host: 127.0.0.1:9999
*/
int setnonblock (int fd) {

  int flags;
  int n;

  flags = fcntl(fd, F_GETFL);
//O_NONBLOCK=0x4000
  fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  n= fcntl(fd, F_GETFL) & O_NONBLOCK;
  return n;
}
int setblock (int fd) {

  int flags;
  int n;

  flags = fcntl(fd, F_GETFL);
  fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);

  n= fcntl(fd, F_GETFL) & O_NONBLOCK;

  return n;
}

#define debugerr(x) x




int connecttcp(char *ip,int port,int msecs){


//EAGAIN,
//EINTR:The system call was interrupted by a signal that was caught;

 int sockfd;
 struct sockaddr_in server_addr;
 socklen_t addrlen;
 int ret;
 int n;
 fd_set rfds,wfds;
 struct timeval tv;
 //int msecs=1000;
 int retval;






 if((sockfd = socket(PF_INET, SOCK_STREAM, 0))<0){
       perror("socket()");   
       exit(EXIT_FAILURE);
 }


 setnonblock(sockfd);

 memset(&server_addr,0, sizeof(server_addr));
 server_addr.sin_family = AF_INET;
 server_addr.sin_port = htons(port);
 server_addr.sin_addr.s_addr = inet_addr(ip);

 addrlen=sizeof(server_addr);

 n=1;

 do{
 

   FD_ZERO(&wfds);

   FD_SET(sockfd, &wfds);

   tv.tv_sec = msecs/1000;
   tv.tv_usec =(tv.tv_sec*1000-msecs)*1000;

   retval = select(sockfd+1,NULL,&wfds, NULL, &tv);//for writing

   //printf("retval=%d",retval);
   errno=0;
   ret=connect(sockfd, (struct sockaddr *)&server_addr,addrlen);
   if(ret<0){
     //sleep(1);
     perror("connect");
     debugerr(fprintf(stderr,"errno=%d, EINPROGRESS=%d,EISCONN=%d\n",errno,EINPROGRESS,EISCONN));

//connect: Operation now in progress
//retval=1errno=119, 119


     //EINPROGRESS
     //EISCONN =127
     //Need select

     if(errno == EISCONN)break;

     if (errno == EINTR){
                continue;
     }

     if (errno == EAGAIN){
                continue;
     }

   }else break;

 }while(n>0);


 setblock(sockfd);

 debugerr(fprintf(stderr,"Connect to %s port %d, fd=%d\n",ip,port,sockfd));

 return sockfd;


}


int main(int argc,char *argv[])
{
   int fd;
   char buf[128]="GET /demox.html?ip=31.31.520.99&name=Sugar31 HTTP/1.1\r\nHost: 127.0.0.1:9999\r\n\r\n\r\n";
   char ret[1204];
   int n;

   char *ip="127.0.0.1";
   int port=9999;

   int msec=1000;


   printf("Connect to %s:%d [msec:%d] and\nSend:\n%s\n",ip,port,msec,buf);






   fd=connecttcp(ip,port,msec);

   setnonblock(fd);
   setblock(fd);

   if(fd>0){
 
   

      write(fd,buf,strlen(buf));
      do{
       n=read(fd,ret,1000);
       if(n>0){
          ret[n]=0;
          printf("--->%s\n",ret);


       }
      }while(n>0);

      //sleep(10);
   
      close(fd);


      exit(EXIT_SUCCESS);
   }
   exit(EXIT_FAILURE );

}

```
:::
