Difference between revisions of "Server.c"

From Organic Design wiki
(have to do it slightly differently)
(circular buf failed - doing another way)
Line 5: Line 5:
  
 
#define svrDELAY 0        // normal operation is 0 for no delay
 
#define svrDELAY 0        // normal operation is 0 for no delay
#define svrPAKSIZE 0x10    // keep packet-size 2^n and small for non-multithreaded design
+
#define svrPAKSIZE 0x10    // keep packet-size small for non-multithreaded design
#define svrBUFMASK 0x7f   // dictates max message size-1 (2^n-1 because used as & mask)
+
#define svrBUFSIZE 0x80   // dictates max message size
 
#define svrMAXCLIENTS 1000 // used by listen()
 
#define svrMAXCLIENTS 1000 // used by listen()
 
#ifndef __WIN32__
 
#ifndef __WIN32__
Line 18: Line 18:
 
unsigned long int sockopt_on;
 
unsigned long int sockopt_on;
 
int sock;
 
int sock;
 +
const char *iTerm = "\r\n\r\n";
 +
 +
// Stream info structure and globals
 +
typedef struct siStruct {
 +
int fd, iPtr, tPtr, oPtr;
 +
char *iBuf, *oBuf;
 +
} streamInfo;
  
 
// Function prototypes
 
// Function prototypes
Line 27: Line 34:
 
int nonblocking(int socket);
 
int nonblocking(int socket);
  
 
// Stream info structure and globals
 
char *iTerm = "\r\n\r\n";
 
typedef struct siStruct {
 
int fd, iPtr, tPtr, iMsg, oPtr;
 
char *iBuf, *oBuf;
 
} streamInfo;
 
 
// iBuf is circular, so best to use a wrapping cmp instead of copying/moving characters
 
// - returns true if string pointed to by str is same as that in iBuf at offset i
 
int siCmp(char *str, int i, streamInfo *info) {
 
i--;
 
char *buf = info->iBuf;
 
while(*str) if (*str++ != buf[i=i+1&svrBUFMASK]) return 0;
 
return 1;
 
}
 
  
 
// Initialise socket listening on specified port
 
// Initialise socket listening on specified port
Line 68: Line 59:
 
if (bind(nonblocking(sock),(struct sockaddr*)&addr,szAddr)<0) logErr("bind() failed!");
 
if (bind(nonblocking(sock),(struct sockaddr*)&addr,szAddr)<0) logErr("bind() failed!");
 
if (listen(sock,svrMAXCLIENTS)<0) logErr("listen() failed!");
 
if (listen(sock,svrMAXCLIENTS)<0) logErr("listen() failed!");
if (errno) logAdd("Server failed to start!");
 
 
 
if (errno == 0) {
 
if (errno == 0) {
 
char *msg = malloc(100);
 
char *msg = malloc(100);
Line 76: Line 65:
 
free(msg);
 
free(msg);
 
}
 
}
return errno;
+
else {
 +
logAdd("Server failed to start!");
 +
return errno;
 +
}
 +
 
 +
char *pBuf = malloc(svrPAKSIZE);
 +
 
 +
return 0;
 
}
 
}
  
Line 117: Line 113:
 
newsi->iBuf = malloc(svrBUFMASK+1);
 
newsi->iBuf = malloc(svrBUFMASK+1);
 
newsi->oBuf = malloc(svrBUFMASK+1);
 
newsi->oBuf = malloc(svrBUFMASK+1);
newsi->iPtr = newsi->iMsg = newsi->oPtr = newsi->tPtr = 0;
+
newsi->iPtr = newsi->oPtr = newsi->tPtr = 0;
 
node n = nodeGetValue(nodeCLIENTS,nodeLOOP);
 
node n = nodeGetValue(nodeCLIENTS,nodeLOOP);
 
n = nodeLoopInsert(n,nodeNULL);
 
n = nodeLoopInsert(n,nodeNULL);
Line 135: Line 131:
 
streamInfo *info = *nodeState(this, nodeSTREAMINFO);
 
streamInfo *info = *nodeState(this, nodeSTREAMINFO);
 
char *iBuf = info->iBuf, *oBuf = info->oBuf;
 
char *iBuf = info->iBuf, *oBuf = info->oBuf;
int i,j,fd = info->fd;
+
int i = 0, j = 0, fd = info->fd;
  
 
// If any data to recieve, read a packet
 
// If any data to recieve, read a packet
Line 142: Line 138:
 
to.tv_sec = to.tv_usec = svrDELAY;
 
to.tv_sec = to.tv_usec = svrDELAY;
 
if (select(fd+1,&fdset,NULL,NULL,&to)>0) {
 
if (select(fd+1,&fdset,NULL,NULL,&to)>0) {
if ((j = recv(fd,iBuf+(info->iPtr&svrBUFMASK),svrPAKSIZE,0)) > 0) {
+
printf("%d bytes read to %d (iMsg=%d)\n",i,info->iPtr&svrBUFMASK,info->iMsg);
+
if (info->iPtr > svrBUFMASK-svrPAKSIZE) info->iptr // loop if too big
 +
 
 +
if ((i = recv(fd,pBuf,svrPAKSIZE,0)) > 0) {
 +
printf("%d bytes read to pBuf\n",i);
 
// Scan new data for completed messages and process each
 
// Scan new data for completed messages and process each
for (i=0; i<j; i++) {
+
while (i--)
if (info->iPtr[i] == iTerm[info->tPtr]) {
+
if ((iBuf[info->iPtr++] = pBuf[j++]) != iTerm[info->tPtr++]) info->tPtr = 0;
if ((info->tPtr = info->tPtr+1&3)==0) process(info);
+
else if (*info->tPtr == 0) process(info);
}
 
else info->tPtr = 0;
 
}
 
 
}
 
}
 
else if (i == 0) {
 
else if (i == 0) {
Line 177: Line 173:
  
 
// Process message in iBuf which should be a complete HTTP GET request
 
// Process message in iBuf which should be a complete HTTP GET request
 +
// - iPtr is put back to 0 so next message fills from start of iBuf
 
void process(streamInfo *info) {
 
void process(streamInfo *info) {
int msg = info->iMsg;
+
info->iPtr = 0;
if (siCmp("GET /",msg,info)) {
+
char *msg = info->iBuf;
 +
if (strcmp("GET /",msg)==0) {
  
 
// Extract request path & query-string
 
// Extract request path & query-string
Line 189: Line 187:
  
 
// Exit if /stop requested
 
// Exit if /stop requested
if (siCmp("/stop",msg,info)) {
+
if (strcmp("/stop",msg)) {
 
logAdd("/stop requested, stopping.");
 
logAdd("/stop requested, stopping.");
 
exit(EXIT_SUCCESS);
 
exit(EXIT_SUCCESS);

Revision as of 07:36, 2 August 2006

// This article and all its includes are licenced under LGPL // GPL: http://www.gnu.org/copyleft/lesser.html // SRC: http://www.organicdesign.co.nz/server.c


  1. define svrDELAY 0 // normal operation is 0 for no delay
  2. define svrPAKSIZE 0x10 // keep packet-size small for non-multithreaded design
  3. define svrBUFSIZE 0x80 // dictates max message size
  4. define svrMAXCLIENTS 1000 // used by listen()
  5. ifndef __WIN32__
  6. define WSAGetLastError() errno
  7. endif

// Socket structures and variables struct sockaddr_in addr; struct timeval to; fd_set fdset; unsigned long int sockopt_on; int sock; const char *iTerm = "\r\n\r\n";

// Stream info structure and globals typedef struct siStruct { int fd, iPtr, tPtr, oPtr; char *iBuf, *oBuf; } streamInfo;

// Function prototypes //void server(); // declared earlier in nodalHusk since called in root loop //void client(); void process(); int server_init(); int server_exit(); int nonblocking(int socket);


// Initialise socket listening on specified port int server_init() {

#if __WIN32__ WSADATA wsaData; if (WSAStartup(MAKEWORD(2,0),&wsaData)!=0) logErr("WSAStartup() failed!"); #endif

// Set up structures for socket & select sockopt_on = 1; int szAddr = sizeof(struct sockaddr_in); memset((char*)&addr, 0, szAddr); // zero the struct addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); errno = 0;

// Do the usual socket polava: create,options,bind,listen if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) logErrNum("socket() failed returning %d",WSAGetLastError()); if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char*)&sockopt_on,sizeof(int))<0) logErr("setsockopt() failed!"); if (bind(nonblocking(sock),(struct sockaddr*)&addr,szAddr)<0) logErr("bind() failed!"); if (listen(sock,svrMAXCLIENTS)<0) logErr("listen() failed!"); if (errno == 0) { char *msg = malloc(100); sprintf(msg,"Daemon \"%s\" started successfully and serving on port %d.",peer,port); logAdd(msg); free(msg); } else { logAdd("Server failed to start!"); return errno; }

char *pBuf = malloc(svrPAKSIZE);

return 0; }


// Perform any cleanup for socket int server_exit() { // todo: close streams and free buffers etc #if __WIN32__ WSACleanup(); #endif return WSAGetLastError(); }


// make the passed socket non-blocking so accept() returns straight away for multiplexed model // - if no incoming requests, returns EAGAIN or EWOULDBLOCK state int nonblocking(int socket) { #if __WIN32__ ioctlsocket(socket,FIONBIO,&sockopt_on); #else fcntl(socket,F_SETFL,fcntl(socket,F_GETFL)|O_NONBLOCK); #endif return socket; }


// Function called by each stream-node in network's reduction loop // - there is also a streams-container node containing zero or more stream nodes void server() { FD_ZERO(&fdset); FD_SET(sock,&fdset); to.tv_sec = to.tv_usec = svrDELAY; if (select(sock+1,&fdset,NULL,NULL,&to)>0) { // New connection request, accpet and create new stream-node int fd = nonblocking(accept(sock,NULL,NULL)); if (fd>0) { printf("New connection: Stream%d.\n",fd); streamInfo *newsi = malloc(sizeof(streamInfo)); newsi->fd = fd; newsi->iBuf = malloc(svrBUFMASK+1); newsi->oBuf = malloc(svrBUFMASK+1); newsi->iPtr = newsi->oPtr = newsi->tPtr = 0; node n = nodeGetValue(nodeCLIENTS,nodeLOOP); n = nodeLoopInsert(n,nodeNULL); nodeSetValue(nodeCLIENTS,nodeLOOP,n); nodeSetValue(n,nodeCODE,nodeTRUE); *nodeState(n,nodeNULL) = &client; *nodeState(n,nodeSTREAMINFO) = newsi; } else logErr("accept(): failed!"); } }


// Each of the stream nodes points to this function in its State void client() { // Get the info for this stream ready for reading/writing a packet if necessary streamInfo *info = *nodeState(this, nodeSTREAMINFO); char *iBuf = info->iBuf, *oBuf = info->oBuf; int i = 0, j = 0, fd = info->fd;

// If any data to recieve, read a packet FD_ZERO(&fdset); FD_SET(fd,&fdset); to.tv_sec = to.tv_usec = svrDELAY; if (select(fd+1,&fdset,NULL,NULL,&to)>0) {

if (info->iPtr > svrBUFMASK-svrPAKSIZE) info->iptr // loop if too big

if ((i = recv(fd,pBuf,svrPAKSIZE,0)) > 0) { printf("%d bytes read to pBuf\n",i); // Scan new data for completed messages and process each while (i--) if ((iBuf[info->iPtr++] = pBuf[j++]) != iTerm[info->tPtr++]) info->tPtr = 0; else if (*info->tPtr == 0) process(info); } else if (i == 0) { // zero bytes to read, do orderly termination printf("Stream%d closed.\n",fd); //nodeLoopRemove(this); // NOTE: reduction not handling PREV free(iBuf); free(oBuf); free(info); } else logErrNum("recv() failed returning %d",WSAGetLastError()); } return; // Data to send? FD_ZERO(&fdset); FD_SET(fd,&fdset); to.tv_sec = to.tv_usec = svrDELAY; if (select(fd+1,NULL,&fdset,NULL,&to)>0) { // write a packet from outbuf //int len = svrPAKSIZE; // or less //if (send(fd,oBuf+oPtr,len,0)<0) logErr("send() failed!"); }

}


// Process message in iBuf which should be a complete HTTP GET request // - iPtr is put back to 0 so next message fills from start of iBuf void process(streamInfo *info) { info->iPtr = 0; char *msg = info->iBuf; if (strcmp("GET /",msg)==0) {

// Extract request path & query-string msg += 4; char *s1 = malloc(50), *rqst = s1, *end = info->iBuf+msg; while((*s1++ = *++end) > ' '); *--s1 = '\0'; printf("Request:\"%s\"\n",rqst);

// Exit if /stop requested if (strcmp("/stop",msg)) { logAdd("/stop requested, stopping."); exit(EXIT_SUCCESS); }

// Send test response back send(info->fd,"HTTP/1.1 200 OK\r\nDate: Mon, 31 Jul 2006 22:27:14 NZST\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nContent-Length: 9\r\n\r\nStink eh?",134,0); free(rqst); } else { send(info->fd,"HTTP/1.1 200 OK\r\nDate: Mon, 31 Jul 2006 22:27:14 NZST\r\nContent-Type: text/html\r\nConnection: keep-alive\r\nContent-Length: 9\r\n\r\nNo GET!!!",134,0); logAdd("Peerd only supports GET requests currently!"); } }