/* returns 0 on success, -1 on error */ int ft_swap_buf_to_native(UINT16_T command, UINT32_T bufsize, void *buf) { datadef_t *ddef; switch(command) { case GET_HDR: /* This should not have a buf attached */ return 0; case GET_DAT: /* buf contains a datsel_t = 2x UINT32_T */ if (bufsize == 8) ft_swap32(2, buf); return 0; case GET_EVT: /* buf contains a datsel_t = 2x UINT32_T */ if (bufsize == 8) ft_swap32(2, buf); return 0; case WAIT_DAT: /* buf contains a waitdef_t = 3x UINT32_T */ ft_swap32(3, buf); return 0; case PUT_DAT: /* buf contains a datadef_t and after that the data */ ddef = (datadef_t *) buf; ft_swap32(4, ddef); /* this is for datadef_t */ ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */ return 0; case PUT_HDR: /* buf contains a headerdef_t and optionally chunks */ ft_swap32(6, buf); /* all fields are 32-bit values */ return ft_swap_chunks_to_native(bufsize - sizeof(headerdef_t), ((headerdef_t *) buf)->nchans, (char *) buf + sizeof(headerdef_t)); case PUT_EVT: /* buf contains multiple eventdef_t and buf's */ return ft_swap_events_to_native(bufsize, buf); } return -1; }
int ft_swap_from_native(UINT16_T orgCommand, message_t *msg) { datadef_t *ddef; UINT32_T nchans; UINT32_T bufsize = msg->def->bufsize; ft_swap16(1, &msg->def->version); ft_swap16(1, &msg->def->command); ft_swap32(1, &msg->def->bufsize); if (bufsize == 0) return 0; switch(orgCommand) { case GET_HDR: nchans = ((headerdef_t *) msg->buf)->nchans; ft_swap32(6, msg->buf); /* all fields are 32-bit values */ return ft_swap_chunks_from_native(bufsize - sizeof(headerdef_t), nchans, (char *) msg->buf + sizeof(headerdef_t)); case GET_DAT: ddef = (datadef_t *) msg->buf; ft_swap_data(ddef->nchans*ddef->nsamples, ddef->data_type, ddef + 1); /* ddef+1 points to first data byte */ ft_swap32(5, ddef); /* all fields are 32-bit */ return 0; case GET_EVT: return ft_swap_events_from_native(bufsize, msg->buf); case WAIT_DAT: ft_swap32(2, msg->buf); /* nsamples + nevents = 32bit */ return 0; } return -1; }
int ft_swap_events_from_native(UINT32_T size, void *buf) { UINT32_T offset = 0; while (offset + sizeof(eventdef_t) <= size) { unsigned int wst; eventdef_t *edef = (eventdef_t *) ((char *) buf + offset); offset += sizeof(eventdef_t) + edef->bufsize; wst = wordsize_from_type(edef->type_type); ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset); ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel); ft_swap32(8, edef); /* all fields are 32-bit */ } return 0; }
void ft_swap_data(UINT32_T numel, UINT32_T datatype, void *data) { switch(datatype) { case DATATYPE_CHAR: case DATATYPE_UINT8: case DATATYPE_INT8: return; case DATATYPE_UINT16: case DATATYPE_INT16: ft_swap16(numel, data); return; case DATATYPE_UINT32: case DATATYPE_INT32: case DATATYPE_FLOAT32: ft_swap32(numel, data); return; case DATATYPE_UINT64: case DATATYPE_INT64: case DATATYPE_FLOAT64: ft_swap64(numel, data); return; } }
int ft_swap_chunks_from_native(UINT32_T size, UINT32_T nchans, void *buf) { UINT32_T offset = 0; while (offset + sizeof(ft_chunkdef_t) <= size) { ft_chunk_t *chunk = (ft_chunk_t *) ((char *) buf + offset); offset += sizeof(ft_chunkdef_t) + chunk->def.size; /* chunk definition fault (=too big) ? */ if (offset > size) return -1; switch(chunk->def.type) { case FT_CHUNK_RESOLUTIONS: if (chunk->def.size >= nchans*sizeof(FLOAT64_T)) { ft_swap64(nchans, chunk->data); } break; /* Add other cases here as needed */ } ft_swap32(2, &(chunk->def)); } return 0; }
/* returns 0 on success, -1 on error */ int ft_swap_events_to_native(UINT32_T size, void *buf) { UINT32_T offset = 0; while (offset + sizeof(eventdef_t) <= size) { unsigned int wst, wsv; eventdef_t *edef = (eventdef_t *) ((char *) buf + offset); ft_swap32(8, edef); /* all fields are 32-bit */ /* Increase offset to beginning of next event */ offset += sizeof(eventdef_t) + edef->bufsize; if (offset > size) return -1; /* this event is too big for "buf" */ wst = wordsize_from_type(edef->type_type); wsv = wordsize_from_type(edef->value_type); /* check if type and value fit into this event's local buffer */ if (wst*edef->type_numel + wsv*edef->value_numel > edef->bufsize) return -1; ft_swap_data(edef->type_numel, edef->type_type, (char *) buf + offset); ft_swap_data(edef->value_numel, edef->value_type, (char *) buf + offset + wst*edef->type_numel); } return 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; 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; }
/** Prepares "start" RDA packet with channel names determined from the corresponding chunk (or empty) Also converts to little-endian if this machine is big endian @param hdr Points to headerdef_t structure, will be filled, may not be NULL @return created start item, or NULL on error (connection / out of memory) */ rda_buffer_item_t *rda_aux_get_hdr_prep_start(int ft_buffer, headerdef_t *hdr) { rda_buffer_item_t *item = NULL; const ft_chunk_t *chunk; rda_msg_start_t *R; char *str; double *dRes; const void *dResSource; size_t bytesTotal; int i,r,numExtraZeros,sizeOrgNames; message_t req, *resp = NULL; messagedef_t msg_def; req.def = &msg_def; req.buf = NULL; msg_def.version = VERSION; msg_def.command = GET_HDR; msg_def.bufsize = 0; r = clientrequest(ft_buffer, &req, &resp); if (r<0 || resp == NULL || resp->def == NULL) { goto cleanup; } if (resp->def->command != GET_OK || resp->def->bufsize < sizeof(headerdef_t) || resp->buf == NULL) { goto cleanup; } memcpy(hdr, resp->buf, sizeof(headerdef_t)); /* Ok, we have the basic header, now look for proper FT_CHUNK_RESOLUTIONS */ chunk = find_chunk(resp->buf, sizeof(headerdef_t), resp->def->bufsize, FT_CHUNK_RESOLUTIONS); if (chunk != NULL && chunk->def.size == hdr->nchans*sizeof(double)) { dResSource = chunk->data; /* fine - we just need a memcpy later on */ } else { dResSource = NULL; /* no suitable chunk found - set defaults later on */ } /* Now see if we can find channel names */ chunk = find_chunk(resp->buf, sizeof(headerdef_t), resp->def->bufsize, FT_CHUNK_CHANNEL_NAMES); if (chunk != NULL && chunk->def.size >= hdr->nchans) { /* The chunk seems ok - check whether we really have N (0-terminated) strings */ int k,nz = 0; for (k = 0; k<chunk->def.size && nz<=hdr->nchans; k++) { if (chunk->data[k] == 0) nz++; } /* Okay, either k is at the end and we have nz<=N, or we have reached N=nz before the end of the chunk. In both cases, it's safe to transmit the first 'k' bytes and add (N-nz) trailing zeros */ numExtraZeros = hdr->nchans - nz; sizeOrgNames = k; } else { sizeOrgNames = 0; numExtraZeros = hdr->nchans; } bytesTotal = sizeof(rda_msg_start_t) + hdr->nchans*(sizeof(double)) + sizeOrgNames + numExtraZeros; item = rda_aux_alloc_item(bytesTotal); if (item == NULL) goto cleanup; R = (rda_msg_start_t *) item->data; memcpy(R->hdr.guid, _rda_guid, sizeof(_rda_guid)); R->hdr.nSize = bytesTotal; R->hdr.nType = RDA_START_MSG; R->nChannels = hdr->nchans; R->dSamplingInterval = 1.0e6/(double) hdr->fsample; /* should be in microseconds */ if (_i_am_big_endian_) { /* take care of hdr.nSize, hdr.nType, nChannels */ ft_swap32(3, &(R->hdr.nSize)); ft_swap64(1, &(R->dSamplingInterval)); } /* R+1 points to first byte after header info */ dRes = (double *) ((void *)(R+1)); if (dResSource == NULL) { /* Fill with resolution = 1.0 -- we have nothing better */ for (i=0;i<hdr->nchans;i++) dRes[i]=1.0; } else { memcpy(dRes, dResSource, hdr->nchans * sizeof(double)); } /* swap byte order if necessary */ if (_i_am_big_endian_) { ft_swap64(hdr->nchans, dRes); } /* Let 'str' point to first byte after the resolution values */ str = (char *) ((void *)(dRes + hdr->nchans)); if (sizeOrgNames > 0) { memcpy(str, chunk->data, sizeOrgNames); } for (i=0;i<numExtraZeros;i++) str[sizeOrgNames + i] = 0; /* done */ cleanup: if (resp) { if (resp->def) free(resp->def); if (resp->buf) free(resp->buf); free(resp); } return item; }
/** Retrieves samples and markers and returns them in as a new 'item', or NULL on errors. */ rda_buffer_item_t *rda_aux_get_samples_and_markers(int ft_buffer, const samples_events_t *last, const samples_events_t *cur, int numBlock, int use16bit) { rda_buffer_item_t *item = NULL; int numEvt = 0,numChans = 0,numSmp = 0; message_t req, *respSmp = NULL, *respEvt = NULL; messagedef_t msg_def; datadef_t *ddef; size_t bytesSamples = 0, bytesMarkers = 0; /* First, try to grab the samples */ if (cur->nsamples > last->nsamples) { datasel_t dat_sel; msg_def.version = VERSION; msg_def.command = GET_DAT; msg_def.bufsize = sizeof(dat_sel); dat_sel.begsample = last->nsamples; dat_sel.endsample = cur->nsamples-1; req.def = &msg_def; req.buf = &dat_sel; if (clientrequest(ft_buffer, &req, &respSmp)<0) { goto cleanup; } numSmp = cur->nsamples - last->nsamples; if (respSmp == NULL || respSmp->def == NULL || respSmp->buf == NULL || respSmp->def->command != GET_OK) { goto cleanup; } else { ddef = (datadef_t *) respSmp->buf; if (ddef->nsamples != numSmp) goto cleanup; numChans = ddef->nchans; bytesSamples = (use16bit ? sizeof(INT16_T) : sizeof(float)) * numSmp * numChans; } } /* Now, try to grab the markers */ if (cur->nevents > last->nevents) { eventsel_t evt_sel; int offset = 0; msg_def.version = VERSION; msg_def.command = GET_EVT; msg_def.bufsize = sizeof(evt_sel); evt_sel.begevent = last->nevents; evt_sel.endevent = cur->nevents-1; req.def = &msg_def; req.buf = &evt_sel; if (clientrequest(ft_buffer, &req, &respEvt) < 0) { goto cleanup; } if (respEvt == NULL || respEvt->def == NULL || respEvt->buf == NULL || respEvt->def->command != GET_OK) { goto cleanup; } /* count the number of events, increase bytesTotal as required */ while (offset + sizeof(eventdef_t) <= respEvt->def->bufsize) { eventdef_t *evdef = (eventdef_t *) ((char *)respEvt->buf + offset); offset += sizeof(eventdef_t) + evdef->bufsize; if (evdef->bufsize < evdef->type_numel*wordsize_from_type(evdef->type_type) + evdef->value_numel*wordsize_from_type(evdef->value_type)) { fprintf(stderr,"Invalid event received: Buffer to small for given value/type description\n"); continue; /* Skip to next event */ } bytesMarkers += sizeof(rda_marker_t); if (evdef->type_type == DATATYPE_CHAR) { if (evdef->value_type == DATATYPE_CHAR) { /* Transform into TYPE:VALUE\0 */ bytesMarkers += evdef->type_numel + evdef->value_numel + 2; } else { /* Transform into TYPE:-\0 */ bytesMarkers += evdef->type_numel + 3; } } else { if (evdef->value_type == DATATYPE_CHAR) { /* Transform into FT:VALUE\0 */ bytesMarkers += evdef->value_numel + 4; } else { /* Transform into FT:-\0 */ bytesMarkers += 5; } } numEvt++; } } /* Now, allocate an item with enough space for both samples and markers */ item = rda_aux_alloc_item(sizeof(rda_msg_data_t) + bytesSamples + bytesMarkers); if (item == NULL) { fprintf(stderr, "Out of memory\n"); goto cleanup; } item->blockNumber = numBlock; /* Okay, we've got the samples in respSmp, events in respEvt, and a big enough 'item'. First fill in the header. */ { rda_msg_data_t *R = (rda_msg_data_t *) item->data; memcpy(R->hdr.guid, _rda_guid, sizeof(_rda_guid)); R->hdr.nType = use16bit ? RDA_INT_MSG : RDA_FLOAT_MSG; R->hdr.nSize = item->size; R->nBlock = numBlock; R->nPoints = numSmp; R->nMarkers = numEvt; if (_i_am_big_endian_) { /* take care of hdr.nSize, hdr.nType, nBlocks, nPoints, nMarkers */ ft_swap32(5, &(R->hdr.nSize)); } } /* Now, fill in the samples, possibly using conversion */ if (numSmp > 0) { char *dataDest = ((char *) item->data + sizeof(rda_msg_data_t)); char *dataSrc = ((char *) respSmp->buf + sizeof(datadef_t)); int numTotal = numSmp * numChans; if (use16bit) { if (_i_am_big_endian_) { /* copy + swap the 16 bit samples */ int i; for (i=0;i<numTotal;i++) { dataDest[0] = dataSrc[1]; dataDest[1] = dataSrc[0]; dataDest+=2; dataSrc+=2; } } else { /* just copy the samples */ memcpy(dataDest, dataSrc, bytesSamples); } } else { rda_aux_convert_to_float(numTotal, dataDest, ddef->data_type, dataSrc); if (_i_am_big_endian_) ft_swap32(numTotal, dataDest); } } /* Finally, fill in the events */ if (numEvt>0) { char *ptr = (char *) item->data + sizeof(rda_msg_data_t) + bytesSamples; int offset = 0; /* count the number of events, increase bytesTotal as required */ while (offset + sizeof(eventdef_t) <= respEvt->def->bufsize) { eventdef_t *evdef = (eventdef_t *) ((char *)respEvt->buf + offset); char *evbuf = (char *)respEvt->buf + offset + sizeof(eventdef_t); rda_marker_t *marker = (rda_marker_t *) ptr; int i, markerPos; offset += sizeof(eventdef_t) + evdef->bufsize; if (evdef->bufsize < evdef->type_numel*wordsize_from_type(evdef->type_type) + evdef->value_numel*wordsize_from_type(evdef->value_type)) { continue; /* skip to next event */ } markerPos = evdef->sample - (last->nsamples +1); /* relative to first sample in this block */ marker->nPosition = (markerPos > 0) ? markerPos : 0; /* needs to be unsigned! */ marker->nChannel = -1; /* All channels, FieldTrip doesn't have this*/ marker->nPoints = evdef->duration; ptr += sizeof(rda_marker_t); if (evdef->type_type == DATATYPE_CHAR) { /* copy type */ for (i=0;i<evdef->type_numel;i++) { *ptr++ = *evbuf++; } } else { *ptr++ = 'F'; *ptr++ = 'T'; /* Skip bytes in event->buffer */ evbuf += evdef->type_numel; } *ptr++ = ':'; if (evdef->value_type == DATATYPE_CHAR) { /* copy value */ for (i=0;i<evdef->value_numel;i++) { *ptr++ = *evbuf++; } } else { *ptr++ = '-'; } /* add trailing 0, see how big the complete marker got */ *ptr++ = 0; marker->nSize = (ptr - (char *) marker); if (_i_am_big_endian_) { /* convert the 4 int32's in the marker definition */ ft_swap32(4, (void *) marker); } } } /* Done */ cleanup: if (respSmp) { FREE(respSmp->buf); FREE(respSmp->def); free(respSmp); } if (respEvt) { FREE(respEvt->buf); FREE(respEvt->def); free(respEvt); } return item; }
/************************************************************************ * 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; }