Difference between revisions of "Server.c"
(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 | + | #define svrPAKSIZE 0x10 // keep packet-size small for non-multithreaded design |
| − | #define | + | #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); | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
// 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 == 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->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 (( | + | |
| − | printf("%d bytes read to | + | 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 | ||
| − | + | while (i--) | |
| − | if (info->iPtr[ | + | if ((iBuf[info->iPtr++] = pBuf[j++]) != iTerm[info->tPtr++]) info->tPtr = 0; |
| − | + | else if (*info->tPtr == 0) process(info); | |
| − | |||
| − | |||
| − | |||
} | } | ||
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) { | ||
| − | + | info->iPtr = 0; | |
| − | if ( | + | 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 ( | + | 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
- define svrDELAY 0 // normal operation is 0 for no delay
- define svrPAKSIZE 0x10 // keep packet-size small for non-multithreaded design
- define svrBUFSIZE 0x80 // dictates max message size
- define svrMAXCLIENTS 1000 // used by listen()
- ifndef __WIN32__
- define WSAGetLastError() errno
- 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!"); } }



