/** * Handles ok message */ static int handleOk(sender_state_t sendst, struct slice *slice, int clNo) { if(slice == NULL) return 0; if(!udpc_isParticipantValid(sendst->rc.participantsDb, clNo)) { udpc_flprintf("Invalid participant %d\n", clNo); return 0; } if (BIT_ISSET(clNo, slice->sl_reqack.readySet)) { /* client is already marked ready */ #if DEBUG flprintf("client %d is already ready\n", clNo); #endif } else { SET_BIT(clNo, slice->sl_reqack.readySet); slice->nrReady++; #if DEBUG flprintf("client %d replied ok for %p %d ready=%d\n", clNo, slice, slice->sliceNo, slice->nrReady); #endif senderSetAnswered(sendst->stats, clNo); markParticipantAnswered(slice, clNo); } return 0; }
static int handleRetransmit(sender_state_t sendst, struct slice *slice, int clNo, unsigned char *map, int rxmit) { unsigned int i; #if DEBUG flprintf("Handle retransmit %d @%d\n", slice->sliceNo, clNo); #endif if(!udpc_isParticipantValid(sendst->rc.participantsDb, clNo)) { udpc_flprintf("Invalid participant %d\n", clNo); return 0; } if(slice == NULL) return 0; if (rxmit < slice->rxmitId) { #if 0 flprintf("Late answer\n"); #endif /* late answer to previous Req Ack */ return 0; } #if DEBUG logprintf(udpc_log, "Received retransmit request for slice %d from client %d\n", slice->sliceNo,clNo); #endif for(i=0; i <sizeof(slice->rxmitMap) / sizeof(char); i++) { slice->rxmitMap[i] |= ~map[i]; } slice->needRxmit = 1; markParticipantAnswered(slice, clNo); return 0; }
/** * bring stored packet back up... */ int RecvMsg(int s, struct msghdr *msg, int flags) { if(read_swap && stashed) { if(random() / stashed < read_swap) { int slot = random() % stashed; int iovnr; char *data = packetStash[slot].data; int totalLen = packetStash[slot].size; int retBytes=0; flprintf("Bringing out %d\n", slot); for(iovnr=0; iovnr < msg->msg_iovlen; iovnr++) { int len = msg->msg_iov[iovnr].iov_len; if(len > totalLen) len = totalLen; memcpy(msg->msg_iov[iovnr].iov_base, data, len); totalLen -= len; data += len; retBytes += len; if(totalLen == 0) break; } packetStash[slot]=packetStash[stashed]; stashed--; return retBytes; } } return recvmsg(s, msg, flags); }
int addParticipant(participantsDb_t db, struct sockaddr_in *addr, int capabilities, unsigned int rcvbuf, int pointopoint) { int i; if((i = lookupParticipant(db, addr)) >= 0) return i; for (i=0; i < MAX_CLIENTS; i++) { if (!db->clientTable[i].used) { char ipBuffer[16]; db->clientTable[i].addr = *addr; db->clientTable[i].used = 1; db->clientTable[i].capabilities = capabilities; db->clientTable[i].rcvbuf = rcvbuf; db->nrParticipants++; flprintf("New connection from %s (#%d) %08x\n", getIpString(addr, ipBuffer), i, capabilities); #ifdef USE_SYSLOG syslog(LOG_INFO, "New connection from %s (#%d)\n", getIpString(addr, ipBuffer), i); #endif return i; } else if(pointopoint) return -1; } return -1; /* no space left in participant's table */ }
void srandomTime(int printSeed) { struct timeval tv; long seed; gettimeofday(&tv, 0); seed = (tv.tv_usec * 2000) ^ tv.tv_sec; if(printSeed) flprintf("seed=%ld\n", seed); srandom(seed); }
static int handleNextMessage(sender_state_t sendst, struct slice *xmitSlice, struct slice *rexmitSlice) { int pos = pc_getConsumerPosition(sendst->rc.incoming); union message *msg = &sendst->rc.q[pos].msg; int clNo = sendst->rc.q[pos].clNo; #if DEBUG flprintf("handle next message\n"); #endif pc_consumeAny(sendst->rc.incoming); switch(ntohs(msg->opCode)) { case CMD_OK: handleOk(sendst, findSlice(xmitSlice, rexmitSlice, ntohl(msg->ok.sliceNo)), clNo); break; case CMD_DISCONNECT: handleDisconnect(sendst->rc.participantsDb, xmitSlice, rexmitSlice, clNo); break; case CMD_RETRANSMIT: #if DEBUG flprintf("Received retransmittal request for %ld from %d:\n", (long) xtohl(msg->retransmit.sliceNo), clNo); #endif handleRetransmit(sendst, findSlice(xmitSlice, rexmitSlice, ntohl(msg->retransmit.sliceNo)), clNo, msg->retransmit.map, msg->retransmit.rxmit); break; default: udpc_flprintf("Bad command %04x\n", (unsigned short) msg->opCode); break; } pc_consumed(sendst->rc.incoming, 1); pc_produce(sendst->rc.freeSpace, 1); return 0; }
/** * Lose a packet */ void loseRecvPacket(int s) { if(read_loss) { while(random() < read_loss) { int x; flprintf("Losing packet\n"); recv(s, (void *) &x, sizeof(x),0); } } if(read_swap) { while(stashed < STASH_SIZE && random() < read_swap) { int size; flprintf("Stashing packet %d\n", stashed); size = recv(s, packetStash[stashed].data, sizeof(packetStash[stashed].data),0); packetStash[stashed].size = size; stashed++; } } }
static int isSliceAcked(struct slice *slice) { #if DEBUG flprintf("Is slice %d acked? %d\n", slice->sliceNo, slice->state); #endif if(slice->state == SLICE_ACKED) { return 1; } else { return 0; } }
int removeParticipant(struct participantsDb *db, int i) { if(db->clientTable[i].used) { char ipBuffer[16]; flprintf("Disconnecting #%d (%s)\n", i, getIpString(&db->clientTable[i].addr, ipBuffer)); #ifdef USE_SYSLOG syslog(LOG_INFO, "Disconnecting #%d (%s)\n", i, getIpString(&db->clientTable[i].addr, ipBuffer)); #endif db->clientTable[i].used = 0; db->nrParticipants--; } return 0; }
static int doRetransmissions(sender_state_t sendst, struct slice *slice) { if(slice->state == SLICE_ACKED) return 0; /* nothing to do */ #if DEBUG flprintf("Do retransmissions\n"); #endif /* FIXME: reduce slice size if needed */ if(slice->needRxmit) { /* do some retransmissions */ sendSlice(sendst, slice, 1); } return 0; }
static int freeSlice(sender_state_t sendst, struct slice *slice) { int i; i = slice - sendst->slices; #if DEBUG flprintf("Freeing slice %p %d %d\n", slice, slice->sliceNo, i); #endif slice->state = SLICE_PRE_FREE; while(1) { int pos = pc_getProducerPosition(sendst->free_slices_pc); if(sendst->slices[pos].state == SLICE_PRE_FREE) sendst->slices[pos].state = SLICE_FREE; else break; pc_produce(sendst->free_slices_pc, 1); } return 0; }
static struct slice *makeSlice(sender_state_t sendst, int sliceNo) { struct net_config *config = sendst->config; struct fifo *fifo = sendst->fifo; int i; struct slice *slice=NULL; pc_consume(sendst->free_slices_pc, 1); i = pc_getConsumerPosition(sendst->free_slices_pc); slice = &sendst->slices[i]; assert(slice->state == SLICE_FREE); BZERO(*slice); pc_consumed(sendst->free_slices_pc, 1); slice->base = pc_getConsumerPosition(sendst->fifo->data); slice->sliceNo = sliceNo; slice->bytes = pc_consume(fifo->data, 10*config->blockSize); /* fixme: use current slice size here */ if(slice->bytes > config->blockSize * config->sliceSize) slice->bytes = config->blockSize * config->sliceSize; if(slice->bytes > config->blockSize) slice->bytes -= slice->bytes % config->blockSize; pc_consumed(fifo->data, slice->bytes); slice->nextBlock = 0; slice->state = SLICE_NEW; #if 0 flprintf("Made slice %p %d\n", slice, slice->sliceNo); #endif BZERO(slice->sl_reqack.readySet); slice->nrReady = 0; #ifdef BB_FEATURE_UDPCAST_FEC slice->fec_data = sendst->fec_data + (i * config->fec_stripes * config->fec_redundancy * config->blockSize); #endif return slice; }
static THREAD_RETURN netSenderMain(void *args0) { sender_state_t sendst = (sender_state_t) args0; struct net_config *config = sendst->config; struct timeval tv; struct timespec ts; int atEnd = 0; int nrWaited=0; unsigned long waitAverage=10000; /* Exponential average of last wait times */ struct slice *xmitSlice=NULL; /* slice being transmitted a first time */ struct slice *rexmitSlice=NULL; /* slice being re-transmitted */ int sliceNo = 0; /* transmit the data */ if(config->default_slice_size == 0) { #ifdef BB_FEATURE_UDPCAST_FEC if(config->flags & FLAG_FEC) { config->sliceSize = config->fec_stripesize * config->fec_stripes; } else #endif if(config->flags & FLAG_ASYNC) config->sliceSize = 1024; else if (sendst->config->flags & FLAG_SN) { sendst->config->sliceSize = 112; } else sendst->config->sliceSize = 130; sendst->config->discovery = DSC_DOUBLING; } else { config->sliceSize = config->default_slice_size; #ifdef BB_FEATURE_UDPCAST_FEC if((config->flags & FLAG_FEC) && (config->sliceSize > 128 * config->fec_stripes)) config->sliceSize = 128 * config->fec_stripes; #endif } #ifdef BB_FEATURE_UDPCAST_FEC if( (sendst->config->flags & FLAG_FEC) && config->max_slice_size > config->fec_stripes * 128) config->max_slice_size = config->fec_stripes * 128; #endif if(config->sliceSize > config->max_slice_size) config->sliceSize = config->max_slice_size; assert(config->sliceSize <= MAX_SLICE_SIZE); do { /* first, cleanup rexmit Slice if needed */ if(rexmitSlice != NULL) { if(rexmitSlice->nrReady == udpc_nrParticipants(sendst->rc.participantsDb)){ #if DEBUG flprintf("slice is ready\n"); #endif ackSlice(rexmitSlice, sendst->config, sendst->fifo, sendst->stats); } if(isSliceAcked(rexmitSlice)) { freeSlice(sendst, rexmitSlice); rexmitSlice = NULL; } } /* then shift xmit slice to rexmit slot, if possible */ if(rexmitSlice == NULL && xmitSlice != NULL && isSliceXmitted(xmitSlice)) { rexmitSlice = xmitSlice; xmitSlice = NULL; sendReqack(rexmitSlice, sendst->config, sendst->fifo, sendst->stats, sendst->socket); } /* handle any messages */ if(pc_getWaiting(sendst->rc.incoming)) { #if DEBUG flprintf("Before message %d\n", pc_getWaiting(sendst->rc.incoming)); #endif handleNextMessage(sendst, xmitSlice, rexmitSlice); /* restart at beginning of loop: we may have acked the rxmit * slice, makeing it possible to shift the pipe */ continue; } /* do any needed retransmissions */ if(rexmitSlice != NULL && rexmitSlice->needRxmit) { doRetransmissions(sendst, rexmitSlice); /* restart at beginning: new messages may have arrived during * retransmission */ continue; } /* if all participants answered, send req ack */ if(rexmitSlice != NULL && rexmitSlice->nrAnswered == udpc_nrParticipants(sendst->rc.participantsDb)) { rexmitSlice->rxmitId++; sendReqack(rexmitSlice, sendst->config, sendst->fifo, sendst->stats, sendst->socket); } if(xmitSlice == NULL && !atEnd) { #if DEBUG flprintf("SN=%d\n", sendst->config->flags & FLAG_SN); #endif if((sendst->config->flags & FLAG_SN) || rexmitSlice == NULL) { #ifdef BB_FEATURE_UDPCAST_FEC if(sendst->config->flags & FLAG_FEC) { int i; pc_consume(sendst->fec_data_pc, 1); i = pc_getConsumerPosition(sendst->fec_data_pc); xmitSlice = &sendst->slices[i]; pc_consumed(sendst->fec_data_pc, 1); } else #endif { xmitSlice = makeSlice(sendst, sliceNo++); } if(xmitSlice->bytes == 0) atEnd = 1; } } if(xmitSlice != NULL && xmitSlice->state == SLICE_NEW) { sendSlice(sendst, xmitSlice, 0); #if DEBUG flprintf("%d Interrupted at %d/%d\n", xmitSlice->sliceNo, xmitSlice->nextBlock, getSliceBlocks(xmitSlice, sendst->config)); #endif continue; } if(atEnd && rexmitSlice == NULL && xmitSlice == NULL) break; if(sendst->config->flags & FLAG_ASYNC) break; #if DEBUG flprintf("Waiting for timeout...\n"); #endif gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec; ts.tv_nsec = (tv.tv_usec + 1.1*waitAverage) * 1000; #ifdef WINDOWS /* Windows has a granularity of 1 millisecond in its timer. Take this * into account here */ #define GRANULARITY 1000000 ts.tv_nsec += 3*GRANULARITY/2; ts.tv_nsec -= ts.tv_nsec % GRANULARITY; #endif #define BILLION 1000000000 while(ts.tv_nsec >= BILLION) { ts.tv_nsec -= BILLION; ts.tv_sec++; } if(rexmitSlice->rxmitId > 10) /* after tenth retransmission, wait minimum one second */ ts.tv_sec++; if(pc_consumeAnyWithTimeout(sendst->rc.incoming, &ts) != 0) { #if DEBUG flprintf("Have data\n"); #endif { struct timeval tv2; unsigned long timeout; gettimeofday(&tv2, 0); timeout = (tv2.tv_sec - tv.tv_sec) * 1000000+ tv2.tv_usec - tv.tv_usec; if(nrWaited) timeout += waitAverage; waitAverage += 9; /* compensate against rounding errors */ waitAverage = (0.9 * waitAverage + 0.1 * timeout); } nrWaited = 0; continue; } if(rexmitSlice == NULL) { udpc_flprintf("Weird. Timeout and no rxmit slice"); break; } if(nrWaited > 5){ #ifndef WINDOWS /* on Cygwin, we would get too many of those messages... */ udpc_flprintf("Timeout notAnswered="); udpc_printNotSet(sendst->rc.participantsDb, rexmitSlice->answeredSet); udpc_flprintf(" notReady="); udpc_printNotSet(sendst->rc.participantsDb, rexmitSlice->sl_reqack.readySet); udpc_flprintf(" nrAns=%d nrRead=%d nrPart=%d avg=%ld\n", rexmitSlice->nrAnswered, rexmitSlice->nrReady, udpc_nrParticipants(sendst->rc.participantsDb), waitAverage); nrWaited=0; #endif } nrWaited++; if(rexmitSlice->rxmitId > config->retriesUntilDrop) { int i; for(i=0; i < MAX_CLIENTS; i++) { if(udpc_isParticipantValid(sendst->rc.participantsDb, i) && !BIT_ISSET(i, rexmitSlice->sl_reqack.readySet)) { udpc_flprintf("Dropping client #%d because of timeout\n", i); #ifdef USE_SYSLOG syslog(LOG_INFO, "dropped client #%d because of timeout", i); #endif udpc_removeParticipant(sendst->rc.participantsDb, i); if(nrParticipants(sendst->rc.participantsDb) == 0) exit(0); } } continue; } rexmitSlice->rxmitId++; sendReqack(rexmitSlice, sendst->config, sendst->fifo, sendst->stats, sendst->socket); } while(udpc_nrParticipants(sendst->rc.participantsDb)|| (config->flags & FLAG_ASYNC)); cancelReturnChannel(&sendst->rc); return 0; }
static int sendReqack(struct slice *slice, struct net_config *net_config, struct fifo *fifo, sender_stats_t stats, int sock) { /* in async mode, just confirm slice... */ if((net_config->flags & FLAG_ASYNC) && slice->bytes != 0) { ackSlice(slice, net_config, fifo, stats); return 0; } if((net_config->flags & FLAG_ASYNC) #ifdef BB_FEATURE_UDPCAST_FEC && (net_config->flags & FLAG_FEC) #endif ) { return 0; } if(!(net_config->flags & FLAG_SN) && slice->rxmitId != 0) { int nrBlocks; nrBlocks = getSliceBlocks(slice, net_config); #if DEBUG flprintf("nrBlocks=%d lastGoodBlock=%d\n", nrBlocks, slice->lastGoodBlock); #endif if(slice->lastGoodBlock != 0 && slice->lastGoodBlock < nrBlocks) { net_config->discovery = DSC_REDUCING; if (slice->lastGoodBlock < net_config->sliceSize / 2) { net_config->sliceSize = net_config->sliceSize / 2; } else { net_config->sliceSize = slice->lastGoodBlock; } if(net_config->sliceSize < 32) { /* a minimum of 32 */ net_config->sliceSize = 32; } udpc_logprintf(udpc_log, "Slice size=%d\n", net_config->sliceSize); } } slice->lastGoodBlock = 0; #if DEBUG flprintf("Send reqack %d.%d\n", slice->sliceNo, slice->rxmitId); #endif slice->sl_reqack.ra.opCode = htons(CMD_REQACK); slice->sl_reqack.ra.sliceNo = htonl(slice->sliceNo); slice->sl_reqack.ra.bytes = htonl(slice->bytes); slice->sl_reqack.ra.reserved = 0; memcpy((void*)&slice->answeredSet,(void*)&slice->sl_reqack.readySet, sizeof(slice->answeredSet)); slice->nrAnswered = slice->nrReady; /* not everybody is ready yet */ slice->needRxmit = 0; memset(slice->rxmitMap, 0, sizeof(slice->rxmitMap)); memset(slice->isXmittedMap, 0, sizeof(slice->isXmittedMap)); slice->sl_reqack.ra.rxmit = htonl(slice->rxmitId); doRateLimit(net_config->rateLimit, sizeof(slice->sl_reqack)); #ifdef FLAG_AUTORATE if(net_config->flags & FLAG_AUTORATE) doAutoRateLimit(sock, net_config->dir, net_config->sendbuf, sizeof(slice->sl_reqack)); #endif #if DEBUG flprintf("sending reqack for slice %d\n", slice->sliceNo); #endif BCAST_DATA(sock, slice->sl_reqack); return 0; }
static int sendSlice(sender_state_t sendst, struct slice *slice, int retransmitting) { struct net_config *config = sendst->config; int nrBlocks, i; #ifdef BB_FEATURE_UDPCAST_FEC int fecBlocks; #endif int retransmissions=0; if(retransmitting) { slice->nextBlock = 0; if(slice->state != SLICE_XMITTED) return 0; } else { if(slice->state != SLICE_NEW) return 0; } nrBlocks = getSliceBlocks(slice, config); #ifdef BB_FEATURE_UDPCAST_FEC if((config->flags & FLAG_FEC) && !retransmitting) { fecBlocks = config->fec_redundancy * config->fec_stripes; } else { fecBlocks = 0; } #endif #if DEBUG if(retransmitting) { flprintf("%s slice %d from %d to %d (%d bytes) %d\n", retransmitting ? "Retransmitting" : "Sending", slice->sliceNo, slice->nextBlock, nrBlocks, slice->bytes, config->blockSize); } #endif /* transmit the data */ for(i = slice->nextBlock; i < nrBlocks #ifdef BB_FEATURE_UDPCAST_FEC + fecBlocks #endif ; i++) { if(retransmitting) { if(!BIT_ISSET(i, slice->rxmitMap) || BIT_ISSET(i, slice->isXmittedMap)) { /* if slice is not in retransmit list, or has _already_ * been retransmitted, skip it */ if(i > slice->lastGoodBlock) slice->lastGoodBlock = i; continue; } SET_BIT(i, slice->isXmittedMap); retransmissions++; #if DEBUG flprintf("Retransmitting %d.%d\n", slice->sliceNo, i); #endif } if(i < nrBlocks) transmitDataBlock(sendst, slice, i); #ifdef BB_FEATURE_UDPCAST_FEC else transmitFecBlock(sendst, slice, i - nrBlocks); #endif if(!retransmitting && pc_getWaiting(sendst->rc.incoming)) { i++; break; } } if(retransmissions) senderStatsAddRetransmissions(sendst->stats, retransmissions); slice->nextBlock = i; if(i == nrBlocks #ifdef BB_FEATURE_UDPCAST_FEC + fecBlocks #endif ) { slice->needRxmit = 0; if(!retransmitting) slice->state = SLICE_XMITTED; #if DEBUG flprintf("Done: at block %d %d %d\n", i, retransmitting, slice->state); #endif return 2; } #if DEBUG flprintf("Done: at block %d %d %d\n", i, retransmitting, slice->state); #endif return 1; }