Example #1
0
/*******************************************************************************
 * this function is called by the client
 * it takes care that the request is processed by the buffer
 *******************************************************************************/
int clientrequest(int server, const message_t *request, message_t **response_ptr) {
    int verbose = 0;

	if (verbose>0) fprintf(stderr, "clientrequest: server = %d\n", server);
	if (verbose>0) print_request(request->def);

	if (server<0) {
		fprintf(stderr, "clientrequest: invalid value for server (%d)\n", server);
		return -1;
	}

	else if (server==0) {
		/* use direct memory acces to the buffer */
		if (dmarequest(request, response_ptr)!=0)
			return -2;
	}

	else if (server>0) {
		/* use TCP connection to the buffer */
		if (tcprequest(server, request, response_ptr)!=0)
			return -3;
	}

	if (verbose>0) print_response((*response_ptr)->def);

	/* everything went fine */
	return 0;
}
Example #2
0
int write_header_to_disk() {
	char name[512];
	messagedef_t reqdef = {VERSION, GET_HDR, 0};
	message_t request;
	message_t *response;
	headerdef_t *hdef;
	int r;
	
	request.def = &reqdef;
	request.buf = NULL;
	
	r = dmarequest(&request, &response);
	if (r!=0 || response == NULL || response->def == NULL || response->buf == NULL) {
		fprintf(stderr, "ERROR: Cannot retrieve header for writing to disk\n");
		goto cleanup;
	}
	
	hdef = (headerdef_t *) response->buf;
	
	setCounter++;
	sampleCounter = eventCounter = 0;
	snprintf(name, sizeof(name), "%s/%04i", baseDirectory, setCounter);
	
	OS = ft_storage_create(name, hdef, hdef+1, &r);
	
cleanup:
	if (response!=NULL) {
		if (response->def != NULL) free(response->def);
		if (response->buf != NULL) free(response->buf);
		free(response);
	}
	return r;
}
Example #3
0
int write_samples_to_disk(int nsamps, double t) {
	messagedef_t reqdef;
	datasel_t ds;
	message_t request;
	message_t *response;
	datadef_t *ddef;
	int r;
		
	ds.begsample = sampleCounter;
	ds.endsample = sampleCounter + nsamps - 1;
	reqdef.version = VERSION;
	reqdef.command = GET_DAT;
	reqdef.bufsize = sizeof(ds);
	request.def = &reqdef;
	request.buf = &ds;
	
	r = dmarequest(&request, &response);
	if (r!=0 || response == NULL || response->def == NULL || response->buf == NULL) {
		fprintf(stderr, "ERROR: Cannot retrieve samples for writing to disk\n");
		goto cleanup;
	}
	
	sampleCounter += nsamps;

	ddef = (datadef_t *) response->buf;
	r = ft_storage_add_samples(OS, nsamps, ddef+1);
	if (r==0) {
		ft_timing_element_t te;
		te.numSamples = nsamps;
		te.numEvents = 0;
		te.time = t;
		r = ft_storage_add_timing(OS, &te);
	}
			
cleanup:
	if (response!=NULL) {
		if (response->def != NULL) free(response->def);
		if (response->buf != NULL) free(response->buf);
		free(response);
	}
	return r;
}
Example #4
0
int write_events_to_disk(int nevs, double t) {
	messagedef_t reqdef;
	eventsel_t es;
	message_t request;
	message_t *response;
	int r;
	
	es.begevent = eventCounter;
	es.endevent = eventCounter + nevs - 1;
	reqdef.version = VERSION;
	reqdef.command = GET_EVT;
	reqdef.bufsize = sizeof(es);
	request.def = &reqdef;
	request.buf = &es;
	
	r = dmarequest(&request, &response);
	if (r!=0 || response == NULL || response->def == NULL || response->buf == NULL) {
		fprintf(stderr, "ERROR: Cannot retrieve events for writing to disk\n");
		goto cleanup;
	}
	
	eventCounter += nevs;
	
	r = ft_storage_add_events(OS, response->def->bufsize, response->buf);
	if (r==0) {
		ft_timing_element_t te;
		te.numSamples = 0;
		te.numEvents = nevs;
		te.time = t;
		r = ft_storage_add_timing(OS, &te);
	}
cleanup:
	if (response!=NULL) {
		if (response->def != NULL) free(response->def);
		if (response->buf != NULL) free(response->buf);
		free(response);
	}
	return r;
}
Example #5
0
/* this function deals with the incoming client request */
void *tcpsocket(void *arg) {
	int n;
	int status = 0, verbose = 0;
	int oldcancelstate, oldcanceltype;

#ifdef ENABLE_POLLING
	struct pollfd fds;
#endif

	// these are used for communication over the TCP socket
	int client = 0;
	message_t *request = NULL, *response = NULL;

	/* the connection to the client has been made by the server */
	client = (int)arg;

    /* this is for debugging */
    pthread_mutex_lock(&mutexsocketcount);
    socketcount++;
    pthread_mutex_unlock(&mutexsocketcount);

	/* this is to prevent closing the thread at an unwanted moment and memory from leaking */
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldcanceltype);
	pthread_cleanup_push(cleanup_message, &request);
	pthread_cleanup_push(cleanup_message, &response)
	pthread_cleanup_push(cleanup_socket, &client);

    if (verbose>1) fprintf(stderr, "tcpsocket: client = %d, socketcount = %d, threadcount = %d\n", client, socketcount, threadcount);

	/* keep processing messages untill the connection is closed */
	while (1) {

	request       = (message_t*)malloc(sizeof(message_t));
	request->def  = (messagedef_t*)malloc(sizeof(messagedef_t));
	request->buf  = NULL;

#ifdef ENABLE_POLLING
		/* wait for data to become available or until the connection is closed */
		/* thohar: i think this is not neccessary as we dont need a timeout. */
		/* roboos: we need it to detect when the socket is closed by the client */
		while (1) {
			fds.fd      = client;
			fds.events  = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLOUT | POLLWRNORM | POLLWRBAND | POLLERR | POLLNVAL;
			fds.revents = 0;

			if (poll(&fds, 1, 1)==-1) {
				perror("poll");
				goto cleanup;
			}

			if (fds.revents & POLLHUP)
				goto cleanup;				// the connection has been closed
			else if (fds.revents & POLLERR)
				goto cleanup;				// the connection has been closed
			else if (fds.revents & POLLIN)
				break;						// data is available, process the message
			else
				usleep(POLLSLEEP);			// wait for data or closed connection
		}
#endif

		if ((n = bufread(client, request->def, sizeof(messagedef_t))) != sizeof(messagedef_t)) {
			if (verbose>0) fprintf(stderr, "tcpsocket: packet size = %d, should be %d\n", n, sizeof(messagedef_t));
			goto cleanup;
		}

		if (request->def->version!=VERSION) {
			if (verbose>0) fprintf(stderr, "tcpsocket: incorrect request version\n");
			goto cleanup;
		}

		if (request->def->bufsize>0) {
			request->buf = malloc(request->def->bufsize);
			if ((n = bufread(client, request->buf, request->def->bufsize)) != request->def->bufsize) {
				if (verbose>0) fprintf(stderr, "tcpsocket: read size = %d, should be %d\n", n, request->def->bufsize);
				goto cleanup;
			}
		}

		if (verbose>1) print_request(request->def);
		if (verbose>1) print_buf(request->buf, request->def->bufsize);

		if ((status = dmarequest(request, &response)) != 0) {
			if (verbose>0) fprintf(stderr, "tcpsocket: an unexpected error occurred\n");
			goto cleanup;
		}

		if (verbose>1) print_response(response->def);
		if (verbose>1) print_buf(request->buf, request->def->bufsize);

		if ((n = bufwrite(client, response->def, sizeof(messagedef_t)))!=sizeof(messagedef_t)) {
			if (verbose>0) fprintf(stderr, "tcpsocket: write size = %d, should be %d\n", n, sizeof(messagedef_t));
			goto cleanup;
		}
		if ((n = bufwrite(client, response->buf, response->def->bufsize))!=response->def->bufsize) {
			if (verbose>0) fprintf(stderr, "tcpsocket: write size = %d, should be %d\n", n, response->def->bufsize);
			goto cleanup;
		}

		cleanup_message(&request);
		cleanup_message(&response);
        request = NULL;
        response = NULL;

	} /* while (1) */

cleanup:
	/* from now on it is safe to cancel the thread */
	pthread_setcancelstate(oldcancelstate, NULL);
	pthread_setcanceltype(oldcanceltype, NULL);

	pthread_cleanup_pop(1); // request
	pthread_cleanup_pop(1); // response
	pthread_cleanup_pop(1); // socket

    /* this is for debugging */
    pthread_mutex_lock(&mutexsocketcount);
    socketcount--;
    pthread_mutex_unlock(&mutexsocketcount);

    /* this is for debugging */
    pthread_mutex_lock(&mutexthreadcount);
    threadcount--;
    pthread_mutex_unlock(&mutexthreadcount);

	pthread_exit(NULL);
	return NULL;
}
/* this function deals with the incoming client request */
void *tcpsocket(void *arg) {
	int n;
	int status = 0, verbose = 0;
	int oldcancelstate, oldcanceltype;

#ifdef ENABLE_POLLING
	struct pollfd fds;
#endif

	/* these are used for communication over the TCP socket */
	int client = 0;
	message_t *request = NULL, *response = NULL;

    threadlocal_t threadlocal;
    threadlocal.message = NULL;
    threadlocal.fd = -1;

	/* the connection to the client has been made by the server */
	client = (int)arg;

    /* this will be closed at cleanup */
    threadlocal.fd = client;

    pthread_cleanup_push(cleanup_tcpsocket, &threadlocal);

    /* this is for debugging */
    pthread_mutex_lock(&mutexsocketcount);
    socketcount++;
    pthread_mutex_unlock(&mutexsocketcount);

    if (verbose>1) fprintf(stderr, "tcpsocket: client = %d, socketcount = %d, threadcount = %d\n", client, socketcount, threadcount);

	/* keep processing messages untill the connection is closed */
	while (1) {
		int swap = 0;
		UINT16_T reqCommand;
		UINT32_T respBufSize;

		request       = (message_t*)malloc(sizeof(message_t));
		DIE_BAD_MALLOC(request);
		
		request->def  = (messagedef_t*)malloc(sizeof(messagedef_t));
		DIE_BAD_MALLOC(request->def);
		request->buf  = NULL;

#ifdef ENABLE_POLLING
		/* wait for data to become available or until the connection is closed */
		/* thohar: i think this is not neccessary as we dont need a timeout. */
		/* roboos: we need it to detect when the socket is closed by the client */
		while (1) {
			fds.fd      = client;
			fds.events  = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLOUT | POLLWRNORM | POLLWRBAND | POLLERR | POLLNVAL;
			fds.revents = 0;

			if (poll(&fds, 1, 1)==-1) {
				perror("poll");
				goto cleanup;
			}

			if (fds.revents & POLLHUP)
				goto cleanup;				/* the connection has been closed */
			else if (fds.revents & POLLERR)
				goto cleanup;				/* the connection has been closed */
			else if (fds.revents & POLLIN)
				break;						/* data is available, process the message */
			else
				usleep(POLLSLEEP);			/* wait for data or closed connection */
		}
#endif

		if ((n = bufread(client, request->def, sizeof(messagedef_t))) != sizeof(messagedef_t)) {
			if (verbose>0) fprintf(stderr, "tcpsocket: packet size = %d, should be %d\n", n, sizeof(messagedef_t));
			goto cleanup;
		}
		
		if (request->def->version==VERSION_OE) {
			swap = 1;
			ft_swap16(2, &request->def->version); /* version + command */
			ft_swap32(1, &request->def->bufsize);
			reqCommand = request->def->command;		
		}

		if (request->def->version!=VERSION) {
			if (verbose>0) fprintf(stderr, "tcpsocket: incorrect request version\n");
			goto cleanup;
		}
		
		if (request->def->bufsize>0) {
			request->buf = malloc(request->def->bufsize);
			DIE_BAD_MALLOC(request->buf);
			if ((n = bufread(client, request->buf, request->def->bufsize)) != request->def->bufsize) {
				if (verbose>0) fprintf(stderr, "tcpsocket: read size = %d, should be %d\n", n, request->def->bufsize);
				goto cleanup;
			}
		}
		
		if (swap && request->def->bufsize > 0) ft_swap_buf_to_native(reqCommand, request->def->bufsize, request->buf);

		if (verbose>1) print_request(request->def);
		if (verbose>1) print_buf(request->buf, request->def->bufsize);

		if ((status = dmarequest(request, &response)) != 0) {
			if (verbose>0) fprintf(stderr, "tcpsocket: an unexpected error occurred\n");
			goto cleanup;
		}
		
		DIE_BAD_MALLOC(response);
		DIE_BAD_MALLOC(response->def);
		
		if (verbose>1) print_response(response->def);
		if (verbose>1) print_buf(request->buf, request->def->bufsize);
		
		respBufSize = response->def->bufsize;
		if (swap) ft_swap_from_native(reqCommand, response);

		/* we don't need the request anymore */
		cleanup_message((void**)&request);
		request = NULL;
		
		/* merge response->def and response->buf if they are small, so we can send it in one go over TCP */
		if (respBufSize + sizeof(messagedef_t) <= MERGE_THRESHOLD) {
			int msize = respBufSize + sizeof(messagedef_t);
			void *merged = NULL;
			
			append(&merged, 0, response->def, sizeof(messagedef_t));
			DIE_BAD_MALLOC(merged);
			append(&merged, sizeof(messagedef_t), response->buf, respBufSize);
			DIE_BAD_MALLOC(merged);
						
			if ((n=bufwrite(client, merged, msize) != msize)) {
				if (verbose>0) fprintf(stderr, "tcpsocket: write size = %d, should be %d\n", n, msize);
				FREE(merged);
				goto cleanup;
			}
			FREE(merged);
		} else {
			if ((n = bufwrite(client, response->def, sizeof(messagedef_t)))!=sizeof(messagedef_t)) {
				if (verbose>0) fprintf(stderr, "tcpsocket: write size = %d, should be %d\n", n, sizeof(messagedef_t));
				goto cleanup;
			}
			if ((n = bufwrite(client, response->buf, respBufSize))!=respBufSize) {
				if (verbose>0) fprintf(stderr, "tcpsocket: write size = %d, should be %d\n", n, respBufSize);
				goto cleanup;
			}
		}

		cleanup_message((void**)&response);
        response = NULL;

	} /* while (1) */

cleanup:
    printf(""); /* otherwise the pthread_cleanup_pop won't compile */

	if (response!=NULL) 
		cleanup_message((void**)&response);
	response = NULL;	/* SK: prevent double free in following pthread_cleanup_pop */

	pthread_cleanup_pop(1);

    /* this is for debugging */
    pthread_mutex_lock(&mutexsocketcount);
    socketcount--;
    pthread_mutex_unlock(&mutexsocketcount);

    /* this is for debugging */
    pthread_mutex_lock(&mutexthreadcount);
    threadcount--;
    pthread_mutex_unlock(&mutexthreadcount);

	pthread_exit(NULL);
	return NULL;
}
Example #7
0
int my_request_handler(const message_t *request, message_t **response, void *user_data) {
	struct timeval tv;
	double tAbs, tRel;
	int res;
	
	gettimeofday(&tv, NULL);
	tAbs = tv.tv_sec + tv.tv_usec*1e-6;
	tRel = tAbs - timePutHeader;
	
	printf("t=%8.3f ", tRel);
	switch(request->def->command) {
		case PUT_HDR:
			printf("Put header, bufsize = %i ... ", request->def->bufsize);
			timePutHeader = tAbs;
			break;
		case GET_HDR:
			printf("Get header ...");
			break;
		case FLUSH_HDR:
			printf("Flush header ...");
			break;
		case PUT_DAT:
			{
				const datadef_t *ddef = (const datadef_t *) request->buf;
				printf("Put data, %i channels, %i samples, type=%i, size=%i ... ", ddef->nchans, ddef->nsamples, ddef->data_type, ddef->bufsize);
			}
			break;
		case GET_DAT:
			if (request->def->bufsize >= sizeof(datasel_t)) {
				const datasel_t *ds = (const datasel_t *) request->buf;
				printf("Get data, start=%i, end=%i ... ", ds->begsample, ds->endsample);
			} else {
				printf("Get all data ... ");
			}
			break;
		case FLUSH_DAT:
			printf("Flush data ... ");
			break;
		case PUT_EVT:
			printf("Put events, bufsize = %i ... ", request->def->bufsize);
			break;
		case GET_EVT:
			if (request->def->bufsize >= sizeof(eventsel_t)) {
				const eventsel_t *es = (const eventsel_t *) request->buf;
				printf("Get events, start=%i, end=%i ... ", es->begevent, es->endevent);
			} else {
				printf("Get all events ... ");
			}
			break;
		case FLUSH_EVT:
			printf("Flush events ... ");
			break;
		case WAIT_DAT:
			if (request->def->bufsize >= sizeof(waitdef_t)) {
				const waitdef_t *wd = (const waitdef_t *) request->buf;
				printf("Wait data, nsamples=%i, nevents=%i, timeout=%i ... \n", wd->threshold.nsamples, wd->threshold.nevents, wd->milliseconds);
			} else {
				printf("Wait data, malformed! ... \n");
			}
			break;
	}
	
	res = dmarequest(request, response);
	if (res != 0) {
		printf("ERROR\n");
	} else {
		switch((*response)->def->command) {
			case WAIT_OK:
				gettimeofday(&tv, NULL);
				tAbs = tv.tv_sec + tv.tv_usec*1e-6;
				tRel = tAbs - timePutHeader;
				printf("t=%8.3f WAIT_OK\n", tRel);
				break;
			case WAIT_ERR:				
				printf("WAIT_ERR\n");
				break;			
			case PUT_OK:
			case GET_OK:
			case FLUSH_OK:
				printf("OK\n");
				break;
			case PUT_ERR:
			case GET_ERR:
			case FLUSH_ERR:
				printf("FAILED\n");
				break;
			default:
				printf("UNRECOGNIZED\n");
		}
	}
	return res;
}
Example #8
0
int my_request_handler(const message_t *request, message_t **response, void *user_data) {
	
	double tAbs, tRel;
	int res;
	int quantity;
	
	tAbs = getCurrentTime();
	tRel = tAbs - timePutHeader;
	
	printf("t=%8.3f ", tRel);
	switch(request->def->command) {
		case PUT_HDR:
			printf("Put header, bufsize = %i ... ", request->def->bufsize);
			timePutHeader = tAbs;
			break;
		case GET_HDR:
			printf("Get header ...");
			break;
		case FLUSH_HDR:
			printf("Flush header ...");
			break;
		case PUT_DAT:
			{
				const datadef_t *ddef = (const datadef_t *) request->buf;
				quantity = ddef->nsamples;
				printf("Put data, %i channels, %i samples, type=%i ... ", ddef->nchans, ddef->nsamples, ddef->data_type);
			}
			break;
		case GET_DAT:
			if (request->def->bufsize >= sizeof(datasel_t)) {
				const datasel_t *ds = (const datasel_t *) request->buf;
				printf("Get data, start=%i, end=%i ... ", ds->begsample, ds->endsample);
			} else {
				printf("Get all data ... ");
			}
			break;
		case FLUSH_DAT:
			printf("Flush data ... ");
			break;
		case PUT_EVT:
			quantity = check_event_array(request->def->bufsize, request->buf);
			printf("Put events, number = %i, bufsize = %i ... ", quantity, request->def->bufsize);
			break;
		case GET_EVT:
			if (request->def->bufsize >= sizeof(eventsel_t)) {
				const eventsel_t *es = (const eventsel_t *) request->buf;
				printf("Get events, start=%i, end=%i ... ", es->begevent, es->endevent);
			} else {
				printf("Get all events ... ");
			}
			break;
		case FLUSH_EVT:
			printf("Flush events ... ");
			break;
		case WAIT_DAT:
			if (request->def->bufsize >= sizeof(waitdef_t)) {
				const waitdef_t *wd = (const waitdef_t *) request->buf;
				printf("Wait data, nsamples=%i, nevents=%i, timeout=%i ... \n", wd->threshold.nsamples, wd->threshold.nevents, wd->milliseconds);
			} else {
				printf("Wait data, malformed! ... \n");
			}
			break;
	}
	
	res = dmarequest(request, response);
	if (res != 0) {
		printf("ERROR\n");
	} else {
		switch((*response)->def->command) {
			case WAIT_OK:
				tAbs = getCurrentTime();
				tRel = tAbs - timePutHeader;
				printf("t=%8.3f WAIT_OK\n", tRel);
				break;
			case WAIT_ERR:				
				printf("WAIT_ERR\n");
				break;			
			case PUT_OK:
				printf("OK\n");
				pthread_mutex_lock(&qMutex);
				if (queue[qWritePos].command == 0) {
					queue[qWritePos].command = request->def->command;
					queue[qWritePos].quantity = quantity;
					queue[qWritePos].t = tRel;
					if (++qWritePos == QUEUE_SIZE) qWritePos = 0;
				} else {
					printf("WARNING - saving thread does not keep up!\n");
				}
				pthread_mutex_unlock(&qMutex);
				break;
			case GET_OK:
			case FLUSH_OK:
				printf("OK\n");
				break;
			case PUT_ERR:
			case GET_ERR:
			case FLUSH_ERR:
				printf("FAILED\n");
				break;
			default:
				printf("UNRECOGNIZED\n");
		}
	}
	return res;
}
Example #9
0
/************************************************************************
 * This function deals with the incoming client requests in a loop until
 * the user requests to stop the server, or until the remote side closes
 * the connection. The implementation follows the idea of a state machine
 * with the four different states:
 *   state = 0 means we are waiting for a request to come in, or we are
 *             in the process of reading the first 8 bytes (the "def" part)
 *   state = 1 means we are in the process of reading the remainder of
 *             the request (the "buf" part")
 *   state = 2 means we are in the process of writing the response (def)
 *   state = 3 means ... writing the 2nd. part of the response ("buf")
 *
 * On top of those 4 states, we maintain two variables "bytesDone" and
 * "bytesTotal" that determine how many bytes we've read/written within
 * the current state, and how many bytes we need to process in total,
 * and a variable "curPtr" which points to the memory region we currently
 * need to read into, or write from. Whether any action is actually taken
 * inside the while loop also depends on the state of the socket which
 * we find out using "select" (and then set "canRead" + "canWrite" flags).
 *
 * Depending on the nature of the request, we might skip states 1 and 3.
 * This is the case if there is no "buf" attached to the message, or if
 * an outgoing message can be merged in to a single packet.
 * 
 * The actual processing of the message happens before moving to state 2
 * and consists of 
 *   1) possibly swapping the message to native endianness
 *   2) calling dmarequest or the user-supplied callback function
 *   3) possibly swapping back to remote endianness
 ************************************************************************/
void *_buffer_socket_func(void *arg) {
	SOCKET sock;
	ft_buffer_server_t *SC;
	int mergePackets;
	messagedef_t reqdef;
	message_t request;
	message_t *response = NULL;
	int state=0; /* 0 = reading def, 1=reading buf, 2=writing def, 3=writing buf */
	int bytesDone, bytesTotal;
	char mergeBuffer[MERGE_THRESHOLD];
	char *curPtr;	/* points at buffer that needs to be filled or written out */
	int swap = 0;	
	int canRead, canWrite;
	UINT16_T reqCommand;
	UINT32_T respBufSize;
	fd_set readSet, writeSet;

	if (arg==NULL) return NULL;
		
	/* copy over necessary variables and free the given structure */
	SC 			 = ((ft_buffer_socket_t *) arg)->server;
	sock 		 = ((ft_buffer_socket_t *) arg)->clientSocket;
	mergePackets = ((ft_buffer_socket_t *) arg)->mergePackets;
	free(arg);
	
	if (SC->verbosity > 0) {
		printf("Started new client thread with packet merging = %i\n", mergePackets);
	}
	
    pthread_mutex_lock(&SC->lock);
    SC->numClients++;
    pthread_mutex_unlock(&SC->lock);

	request.def = &reqdef;
	request.buf = NULL;
	bytesDone = 0;
	bytesTotal = sizeof(messagedef_t);
	curPtr = (char *) request.def;

	while (SC->keepRunning) {
		int sel, res, n;
		struct timeval tv = {0, 10000}; /* 10ms */
	
		FD_ZERO(&readSet);
		FD_ZERO(&writeSet);
		if (state < 2) {
			FD_SET(sock, &readSet);
		} else {
			FD_SET(sock, &writeSet);
		}
		sel = select((int) sock+1, &readSet, &writeSet, NULL, &tv);
		if (sel == 0) continue;
		if (sel < 0) {
			fprintf(stderr, "Error in 'select' operation - closing client connection.\n");
			break;
		}
		canRead = FD_ISSET(sock, &readSet);
		canWrite = FD_ISSET(sock, &writeSet);
		
		if (canRead) {
			n = recv(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
			if (n<=0) {
				/* socket was closed */
				if (SC->verbosity>0) {
					printf("Remote side closed client connection\n");
				}
				break;
			}
			bytesDone+=n;
			if (bytesDone<bytesTotal) continue;
			
			if (state == 0) {
				/* we've read the request.def completely */
				if (reqdef.version==VERSION_OE) {
					swap = 1;
					ft_swap16(2, &reqdef.version); /* version + command */
					ft_swap32(1, &reqdef.bufsize);
					reqCommand = reqdef.command;		
				}
				if (reqdef.version!=VERSION) {
					fprintf(stderr,"Incorrect version requested - closing socket.\n");
					break;
				}
				if (reqdef.bufsize > 0) {
					request.buf = malloc(reqdef.bufsize);
					if (request.buf == NULL) {
						fprintf(stderr, "Out of memory\n");
						break;
					}
					curPtr = request.buf;
					bytesDone = 0;
					bytesTotal = reqdef.bufsize;
					state = 1;
					continue;
				} 
			} else {
				/* Reaching this point means that the state=1, and that we've 
				   read request.buf completely, so swap the endianness if 
				   necessary, and then move on to handling the request.
				*/	
				if (swap) ft_swap_buf_to_native(reqCommand, reqdef.bufsize, request.buf);
			}
			
			/* Request has been read completely, now deal with it */
			if (SC->callback != NULL) {
				/* User supplied a callback function in ft_start_buffer_server */
				res = SC->callback(&request, &response, SC->user_data);
				if (res != 0 || response == NULL || response->def == NULL) {
					fprintf(stderr, "buffer_socket_func: an unexpected error occurred in user-defined request handler\n");
					break;
				}
			} else {
				/* No callback, use normal dmarequest */
				res = dmarequest(&request, &response);
				if (res != 0 || response == NULL || response->def == NULL) {
					fprintf(stderr, "buffer_socket_func: an unexpected error occurred in dmarequest\n");
					break;
				}
			}
			
			/* Ok, the request has been handled, results are in response.
			   We can free the memory pointed to by request.buf ...
			*/
			if (request.buf != NULL) {
				free(request.buf);
				request.buf = NULL;
			}
			
			/* ... swap the response to the remote endianness, if necessary ... */
			respBufSize = response->def->bufsize;
			if (swap) ft_swap_from_native(reqCommand, response);
		
			/* ... and then start writing back the response. To reduce latency,
			   we try to merge response->def and response->buf if they are small, 
			   so we can send it in one go over TCP. To fit the merged packet into 
			   our state machine logic, we apply a trick and jump to state=3 directly,
			   where "curPtr" points to the merged packet.
			   Otherwise, we move to state=2, transmit response->def, move to state=3,
			   and there transmit response->buf.
			*/
			if (mergePackets && respBufSize > 0 && respBufSize + sizeof(messagedef_t) <= MERGE_THRESHOLD) {
				memcpy(mergeBuffer, response->def, sizeof(messagedef_t));
				memcpy(mergeBuffer + sizeof(messagedef_t), response->buf, respBufSize);
				
				curPtr = mergeBuffer;
				bytesDone = 0;
				bytesTotal = respBufSize + sizeof(messagedef_t);
				state = 3;
			} else {
				curPtr = (char *) response->def;
				bytesDone = 0;
				bytesTotal = sizeof(messagedef_t);
				state = 2;
			}
			canWrite = 1;
		}
		
		if (state >= 2 && canWrite) {
			n = send(sock, curPtr + bytesDone, bytesTotal - bytesDone, 0);
			if (n<=0) {
				/* socket was closed */
				fprintf(stderr, "Cannot write to socket -- closing client connection.\n");
				break;
			}
			bytesDone+=n;
			if (bytesDone < bytesTotal) continue;
			if (state==2 && respBufSize > 0) {
				curPtr = (char *) response->buf;
				bytesDone = 0;
				bytesTotal = respBufSize;
				state = 3;
				continue;
			}
			/* Reaching this point means we are done with writing out the response,
			   so we will now free the allocated memory, and reset to state=0.
			*/
			if (response->buf) free(response->buf);
			free(response->def);
			free(response);
			response = NULL;
			state = 0;
			curPtr = (char *) request.def;
			bytesDone = 0;
			bytesTotal = sizeof(messagedef_t);
		}
	}
	
    pthread_mutex_lock(&SC->lock);
    SC->numClients--;
    pthread_mutex_unlock(&SC->lock);
	
	closesocket(sock);
	if (request.buf!=NULL) free(request.buf);
    if (response!=NULL) {
		if (response->buf!=NULL) free(response->buf);
		if (response->def!=NULL) free(response->def);
		free(response);
	}
	
	return NULL;
}