1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-04-18 21:44:02 +03:00
2016-01-06 14:08:59 -06:00

3418 lines
89 KiB
C++

/* Copyright (C) 2014 InfiniDB, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
// $Id: impl.cpp 3495 2013-01-21 14:09:51Z rdempsey $
/* This code is based on udpcast-20090830. Most of the source code in that release
contains no copyright or licensing notices at all. The exception is fec.c, which
is not used here. The udpcast website, http://udpcast.linux.lu/, implies that
the source is covered under GPL. */
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <cerrno>
#include <pthread.h>
using namespace std;
#include "bytestream.h"
using namespace messageqcpp;
#include "udp-sender.h"
#include "udpc-protoc.h"
#include "util.h"
#include "mc_fifo.h"
#include "impl.h"
struct participantsDb {
int nrParticipants;
struct clientDesc {
struct sockaddr_in addr;
int used;
int capabilities;
unsigned int rcvbuf;
} clientTable[MAX_CLIENTS];
};
struct produconsum {
unsigned int size;
volatile unsigned int produced;
unsigned int consumed;
volatile int atEnd;
pthread_mutex_t mutex;
volatile int consumerIsWaiting;
pthread_cond_t cond;
const char *name;
};
struct stats {
int fd;
struct timeval lastPrinted;
long statPeriod;
int printUncompressedPos;
};
struct sender_stats {
FILE *log;
unsigned long long totalBytes;
unsigned long long retransmissions;
int clNo;
unsigned long periodBytes;
struct timeval periodStart;
long bwPeriod;
struct stats s;
};
struct receiver_stats {
struct timeval tv_start;
int bytesOrig;
long long totalBytes;
int timerStarted;
struct stats s;
};
#define SLICEMAGIC 0x41424344
typedef struct slice {
int base; /* base address of slice in buffer */
int sliceNo;
int bytes; /* bytes in slice */
int nextBlock; /* index of next buffer to be transmitted */
enum slice_state {
SLICE_FREE, /* free slice, and in the queue of free slices */
SLICE_NEW, /* newly allocated. FEC calculation and first
* transmission */
SLICE_XMITTED, /* transmitted */
SLICE_ACKED, /* acknowledged (if applicable) */
SLICE_PRE_FREE, /* no longer used, but not returned to queue */
SLICE_RECEIVING,
SLICE_DONE,
};
volatile enum slice_state state;
char rxmitMap[MAX_SLICE_SIZE / BITS_PER_CHAR];
/* blocks to be retransmitted */
char isXmittedMap[MAX_SLICE_SIZE / BITS_PER_CHAR];
/* blocks which have already been retransmitted during this round*/
int rxmitId; /* used to distinguish among several retransmission
* requests, so that we can easily discard answers to "old"
* requests */
/* This structure is used to keep track of clients who answered, and
* to make the reqack message
*/
struct reqackBm {
struct reqack ra;
char readySet[MAX_CLIENTS / BITS_PER_CHAR]; /* who is already ok? */
} sl_reqack;
char answeredSet[MAX_CLIENTS / BITS_PER_CHAR]; /* who answered at all? */
int nrReady; /* number of participants who are ready */
int nrAnswered; /* number of participants who answered; */
int needRxmit; /* does this need retransmission? */
int lastGoodBlock; /* last good block of slice (i.e. last block having not
* needed retransmission */
int lastReqack; /* last req ack sent (debug) */
#ifdef BB_FEATURE_UDPCAST_FEC
unsigned char *fec_data;
#endif
int magic;
int blocksTransferred; /* blocks transferred during this slice */
int dataBlocksTransferred; /* data blocks transferred during this slice */
struct retransmit retransmit;
int freePos; /* where the next data part will be stored to */
int bytesKnown; /* is number of bytes known yet? */
short missing_data_blocks[MAX_FEC_INTERLEAVE];
} *slice_t;
#define QUEUE_SIZE 256
struct returnChannel {
pthread_t thread; /* message receiving thread */
int rcvSock; /* socket on which we receive the messages */
produconsum_t incoming; /* where to enqueue incoming messages */
produconsum_t freeSpace; /* free space */
struct {
int clNo; /* client number */
union message msg; /* its message */
} q[QUEUE_SIZE];
struct net_config *config;
participantsDb_t participantsDb;
};
#define NR_SLICES 2
typedef struct senderState {
struct returnChannel rc;
struct fifo *fifo;
struct net_config *config;
sender_stats_t stats;
int socket;
struct slice slices[NR_SLICES];
produconsum_t free_slices_pc;
unsigned char *fec_data;
pthread_t fec_thread;
produconsum_t fec_data_pc;
} *sender_state_t;
struct clientState {
struct fifo *fifo;
struct client_config *client_config;
struct net_config *net_config;
union serverDataMsg Msg;
struct msghdr data_hdr;
/* pre-prepared messages */
struct iovec data_iov[2];
struct slice *currentSlice;
int currentSliceNo;
receiver_stats_t stats;
produconsum_t free_slices_pc;
struct slice slices[NR_SLICES];
/* Completely received slices */
int receivedPtr;
int receivedSliceNo;
#ifdef BB_FEATURE_UDPCAST_FEC
int use_fec; /* do we use forward error correction ? */
#endif
produconsum_t fec_data_pc;
struct slice *fec_slices[NR_SLICES];
pthread_t fec_thread;
/* A reservoir of free blocks for FEC */
produconsum_t freeBlocks_pc;
unsigned char **blockAddresses; /* adresses of blocks in local queue */
unsigned char **localBlockAddresses;
/* local blocks: freed FEC blocks after we
* have received the corresponding data */
int localPos;
unsigned char *blockData;
unsigned char *nextBlock;
int endReached; /* end of transmission reached:
0: transmission in progress
2: network transmission _and_ FEC
processing finished
*/
int netEndReached; /* In case of a FEC transmission; network
* transmission finished. This is needed to avoid
* a race condition, where the receiver thread would
* already prepare to wait for more data, at the same
* time that the FEC would set endReached. To avoid
* this, we do a select without timeout before
* receiving the last few packets, so that if the
* race condition strikes, we have a way to protect
* against
*/
int selectedFd;
int promptPrinted; /* Has "Press any key..." prompt already been printed */
#ifdef BB_FEATURE_UDPCAST_FEC
fec_code_t fec_code;
#endif
};
#define S_UCAST socks[0]
#define S_BCAST socks[1]
#define S_MCAST_CTRL socks[2]
#define S_MCAST_DATA socks[3]
#define SSEND(x) SEND(client_config->S_UCAST, x, client_config->serverAddr)
/**
* Receiver will passively listen to sender. Works best if sender runs
* in async mode
*/
#define FLAG_PASSIVE 0x0010
/**
* Do not write file synchronously
*/
#define FLAG_NOSYNC 0x0040
/*
* Don't ask for keyboard input on receiver end.
*/
#define FLAG_NOKBD 0x0080
/**
* Do write file synchronously
*/
#define FLAG_SYNC 0x0100
namespace
{
int udpc_isFullDuplex(int s, const char *ifname) {
#ifdef ETHTOOL_GLINK
struct ifreq ifr;
struct ethtool_cmd ecmd;
ecmd.cmd = ETHTOOL_GSET;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);
ifr.ifr_data = (char *) &ecmd;
if(ioctl(s, SIOCETHTOOL, &ifr) == -1) {
/* Operation not supported */
return -1;
} else {
return ecmd.duplex;
}
#else
return -1;
#endif
}
#define getSinAddr(addr) (((struct sockaddr_in *) addr)->sin_addr)
int udpc_ipIsZero(struct sockaddr_in *ip) {
return getSinAddr(ip).s_addr == 0;
}
int hasLink(int s, const char *ifname) {
#ifdef ETHTOOL_GLINK
struct ifreq ifr;
struct ethtool_value edata;
edata.cmd = ETHTOOL_GLINK;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);
ifr.ifr_data = (char *) &edata;
if(ioctl(s, SIOCETHTOOL, &ifr) == -1) {
/* Operation not supported */
return -1;
} else {
return edata.data;
}
#else
return -1;
#endif
}
#define INET_ATON(a,i) inet_aton(a,i)
int udpc_doSend(int s, void *message, size_t len, struct sockaddr_in *to) {
/* flprintf("sent: %08x %d\n", *(int*) message, len);*/
#ifdef LOSSTEST
loseSendPacket();
#endif
return sendto(s, message, len, 0, (struct sockaddr*) to, sizeof(*to));
}
void udpc_copyToMessage(unsigned char *dst, struct sockaddr_in *src) {
memcpy(dst, (char *) &((struct sockaddr_in *)src)->sin_addr,
sizeof(struct in_addr));
}
void udpc_sendHello(struct net_config *net_config, int sock,
int streaming) {
//cerr << "sending hello..." << endl;
struct hello hello;
/* send hello message */
if(streaming)
hello.opCode = htons(CMD_HELLO_STREAMING);
else
hello.opCode = htons(CMD_HELLO);
hello.reserved = 0;
hello.capabilities = htonl(net_config->capabilities);
udpc_copyToMessage(hello.mcastAddr,&net_config->dataMcastAddr);
hello.blockSize = htons(net_config->blockSize);
//TODO: FIXME
//rgWaitAll(net_config, sock, net_config->controlMcastAddr.sin_addr.s_addr, sizeof(hello));
BCAST_CONTROL(sock, hello);
}
char *udpc_getIpString(struct sockaddr_in *addr, char *buffer) {
long iaddr = htonl(getSinAddr(addr).s_addr);
sprintf(buffer,"%ld.%ld.%ld.%ld",
(iaddr >> 24) & 0xff,
(iaddr >> 16) & 0xff,
(iaddr >> 8) & 0xff,
iaddr & 0xff);
return buffer;
}
net_if_t *udpc_getNetIf(const char *wanted) {
#ifndef __MINGW32__
struct ifreq ibuf[100];
struct ifreq *ifrp, *ifend, *chosen;
struct ifconf ifc;
int s;
#else /* __MINGW32__ */
int i;
int etherNo=-1;
int wantedEtherNo=-2; /* Wanted ethernet interface */
MIB_IPADDRTABLE *iptab=NULL;
MIB_IFTABLE *iftab=NULL;
MIB_IPADDRROW *iprow, *chosen=NULL;
MIB_IFROW *chosenIf=NULL;
WORD wVersionRequested; /* Version of Winsock to load */
WSADATA wsaData; /* Winsock implementation details */
ULONG a;
int r;
#endif /* __MINGW32__ */
int lastGoodness=0;
struct in_addr wantedAddress;
int isAddress=0;
int wantedLen=0;
net_if_t *net_if;
if(wanted == NULL) {
wanted = getenv("IFNAME");
}
if(wanted && INET_ATON(wanted, &wantedAddress))
isAddress=1;
else
wantedAddress.s_addr=0;
if(wanted)
wantedLen=strlen(wanted);
net_if = MALLOC(net_if_t);
//TODO: FIXME
//if(net_if == NULL)
// udpc_fatal(1, "Out of memory error");
#ifndef __MINGW32__
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("make socket");
exit(1);
}
ifc.ifc_len = sizeof(ibuf);
ifc.ifc_buf = (caddr_t) ibuf;
if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0 ||
ifc.ifc_len < (signed int)sizeof(struct ifreq)) {
perror("udpcast: SIOCGIFCONF: ");
exit(1);
}
ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
chosen=NULL;
for (ifrp = ibuf ; ifrp < ifend;
#ifdef IFREQ_SIZE
ifrp = IFREQ_SIZE(*ifrp) + (char *)ifrp
#else
ifrp++
#endif
) {
unsigned long iaddr = getSinAddr(&ifrp->ifr_addr).s_addr;
int goodness;
if(ifrp->ifr_addr.sa_family != PF_INET)
continue;
if(wanted) {
if(isAddress && iaddr == wantedAddress.s_addr) {
goodness=8;
} else if(strcmp(wanted, ifrp->ifr_name) ==0) {
/* perfect match on interface name */
goodness=12;
} else if(wanted != NULL &&
strncmp(wanted, ifrp->ifr_name, wantedLen) ==0) {
/* prefix match on interface name */
goodness=7;
} else {
/* no match, try next */
continue;
}
} else {
if(iaddr == 0) {
/* disregard interfaces whose address is zero */
goodness = 1;
} else if(iaddr == htonl(0x7f000001)) {
/* disregard localhost type devices */
goodness = 2;
} else if(strcmp("eth0", ifrp->ifr_name) == 0 ||
strcmp("en0", ifrp->ifr_name) == 0) {
/* prefer first ethernet interface */
goodness = 6;
} else if(strncmp("eth0:", ifrp->ifr_name, 5) == 0) {
/* second choice: any secondary addresses of first ethernet */
goodness = 5;
} else if(strncmp("eth", ifrp->ifr_name, 3) == 0 ||
strncmp("en", ifrp->ifr_name, 2) == 0) {
/* and, if not available, any other ethernet device */
goodness = 4;
} else {
goodness = 3;
}
}
if(hasLink(s, ifrp->ifr_name))
/* Good or unknown link status privileged over known
* disconnected */
goodness += 3;
/* If all else is the same, prefer interfaces that
* have broadcast */
goodness = goodness * 2;
if(goodness >= lastGoodness) {
/* Privilege broadcast-enabled interfaces */
if(ioctl(s, SIOCGIFBRDADDR, ifrp) < 0)
{
//TODO: FIXME
//udpc_fatal(-1, "Error getting broadcast address for %s: %s", ifrp->ifr_name, strerror(errno));
}
if(getSinAddr(&ifrp->ifr_ifru.ifru_broadaddr).s_addr)
goodness++;
}
if(goodness > lastGoodness) {
chosen = ifrp;
lastGoodness = goodness;
net_if->addr.s_addr = iaddr;
}
}
if(!chosen) {
fprintf(stderr, "No suitable network interface found\n");
fprintf(stderr, "The following interfaces are available:\n");
for (ifrp = ibuf ; ifrp < ifend;
#ifdef IFREQ_SIZE
ifrp = IFREQ_SIZE(*ifrp) + (char *)ifrp
#else
ifrp++
#endif
) {
char buffer[16];
if(ifrp->ifr_addr.sa_family != PF_INET)
continue;
fprintf(stderr, "\t%s\t%s\n",
ifrp->ifr_name,
udpc_getIpString((struct sockaddr_in *)&ifrp->ifr_addr, buffer));
}
exit(1);
}
net_if->name = strdup(chosen->ifr_name);
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
/* Index for multicast subscriptions */
if(ioctl(s, SIOCGIFINDEX, chosen) < 0)
{
//TODO: FIXME
//udpc_fatal(-1, "Error getting index for %s: %s", net_if->name, strerror(errno));
}
net_if->index = chosen->ifr_ifindex;
#endif
/* Broadcast */
if(ioctl(s, SIOCGIFBRDADDR, chosen) < 0)
{
//TODO: FIXME
//udpc_fatal(-1, "Error getting broadcast address for %s: %s", net_if->name, strerror(errno));
}
net_if->bcast = getSinAddr(&chosen->ifr_ifru.ifru_broadaddr);
close(s);
#else /* __MINGW32__ */
/* WINSOCK initialization */
wVersionRequested = MAKEWORD(2, 0); /* Request Winsock v2.0 */
if (WSAStartup(wVersionRequested, &wsaData) != 0) /* Load Winsock DLL */ {
fprintf(stderr,"WSAStartup() failed");
exit(1);
}
/* End WINSOCK initialization */
a=0;
r=GetIpAddrTable(iptab, &a, TRUE);
iptab=malloc(a);
r=GetIpAddrTable(iptab, &a, TRUE);
a=0;
r=GetIfTable(iftab, &a, TRUE);
iftab=malloc(a);
r=GetIfTable(iftab, &a, TRUE);
if(wanted && !strncmp(wanted, "eth", 3) && wanted[3]) {
char *ptr;
int n = strtoul(wanted+3, &ptr, 10);
if(!*ptr)
wantedEtherNo=n;
}
for(i=0; i<iptab->dwNumEntries; i++) {
int goodness=-1;
unsigned long iaddr;
int isEther=0;
MIB_IFROW *ifrow;
iprow = &iptab->table[i];
iaddr = iprow->dwAddr;
ifrow = getIfRow(iftab, iprow->dwIndex);
if(ifrow && ifrow->dwPhysAddrLen == 6 && iprow->dwBCastAddr) {
isEther=1;
etherNo++;
}
if(wanted) {
if(isAddress && iaddr == wantedAddress.s_addr) {
goodness=8;
} else if(isEther && wantedEtherNo == etherNo) {
goodness=9;
} else if(ifrow->dwPhysAddrLen) {
int j;
const char *ptr=wanted;
for(j=0; *ptr && j<ifrow->dwPhysAddrLen; j++) {
int digit = strtoul(ptr, (char**)&ptr, 16);
if(digit != ifrow->bPhysAddr[j])
break; /* Digit mismatch */
if(*ptr == '-' || *ptr == ':') {
ptr++;
}
}
if(!*ptr && j == ifrow->dwPhysAddrLen) {
goodness=9;
}
}
} else {
if(iaddr == 0) {
/* disregard interfaces whose address is zero */
goodness = 1;
} else if(iaddr == htonl(0x7f000001)) {
/* disregard localhost type devices */
goodness = 2;
} else if(isEther) {
/* prefer ethernet */
goodness = 6;
} else if(ifrow->dwPhysAddrLen) {
/* then prefer interfaces which have a physical address */
goodness = 4;
} else {
goodness = 3;
}
}
goodness = goodness * 2;
/* If all else is the same, prefer interfaces that
* have broadcast */
if(goodness >= lastGoodness) {
/* Privilege broadcast-enabled interfaces */
if(iprow->dwBCastAddr)
goodness++;
}
if(goodness > lastGoodness) {
chosen = iprow;
chosenIf = ifrow;
lastGoodness = goodness;
}
}
if(!chosen) {
fprintf(stderr, "No suitable network interface found%s%s\n",
wanted ? " for " : "", wanted ? wanted : "");
fprintf(stderr, "The following interfaces are available:\n");
for(i=0; i<iptab->dwNumEntries; i++) {
char buffer[16];
struct sockaddr_in addr;
MIB_IFROW *ifrow;
char *name=NULL;
iprow = &iptab->table[i];
addr.sin_addr.s_addr = iprow->dwAddr;
ifrow = getIfRow(iftab, iprow->dwIndex);
name = fmtName(ifrow);
fprintf(stderr, " %15s %s\n",
udpc_getIpString(&addr, buffer),
name ? name : "");
if(name)
free(name);
}
exit(1);
}
net_if->bcast.s_addr = net_if->addr.s_addr = chosen->dwAddr;
if(chosen->dwBCastAddr)
net_if->bcast.s_addr |= ~chosen->dwMask;
if(chosenIf) {
net_if->name = fmtName(chosenIf);
} else {
net_if->name = "*";
}
free(iftab);
free(iptab);
#endif /* __MINGW32__ */
return net_if;
}
#define IP_MREQN ip_mreqn
int fillMreq(net_if_t *net_if, struct in_addr addr,
struct IP_MREQN *mreq) {
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
mreq->imr_ifindex = net_if->index;
mreq->imr_address.s_addr = 0;
#else
mreq->imr_interface = net_if->addr;
#endif
mreq->imr_multiaddr = addr;
return 0;
}
int mcastOp(int sock, net_if_t *net_if, struct in_addr addr,
int code, const char *message) {
struct IP_MREQN mreq;
int r;
fillMreq(net_if, addr, &mreq);
r = setsockopt(sock, SOL_IP, code, (char*)&mreq, sizeof(mreq));
if(r < 0) {
perror(message);
exit(1);
}
return 0;
}
int udpc_setMcastDestination(int sock, net_if_t *net_if, struct sockaddr_in *addr) {
#ifdef WINDOWS
int r;
struct sockaddr_in interface_addr;
struct in_addr if_addr;
getMyAddress(net_if, &interface_addr);
if_addr = getSinAddr(&interface_addr);
r = setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF,
(char *) &if_addr, sizeof(if_addr));
if(r < 0)
fatal(1, "Set multicast send interface");
return 0;
#else
/* IP_MULTICAST_IF not correctly supported on Cygwin */
return mcastOp(sock, net_if, getSinAddr(addr), IP_MULTICAST_IF,
"Set multicast send interface");
#endif
}
int initSockAddress(addr_type_t addr_type,
net_if_t *net_if,
in_addr_t ip,
unsigned short port,
struct sockaddr_in *addr) {
memset ((char *) addr, 0, sizeof(struct sockaddr_in));
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
//TODO: FIXME
//if(!net_if && addr_type != ADDR_TYPE_MCAST)
//udpc_fatal(1, "initSockAddr without ifname\n");
switch(addr_type) {
case ADDR_TYPE_UCAST:
addr->sin_addr = net_if->addr;
break;
case ADDR_TYPE_BCAST:
addr->sin_addr = net_if->bcast;
break;
case ADDR_TYPE_MCAST:
addr->sin_addr.s_addr = ip;
break;
}
return 0;
}
int mcastListen(int sock, net_if_t *net_if, struct sockaddr_in *addr) {
return mcastOp(sock, net_if, getSinAddr(addr), IP_ADD_MEMBERSHIP,
"Subscribe to multicast group");
}
int udpc_makeSocket(addr_type_t addr_type,
net_if_t *net_if,
struct sockaddr_in *tmpl,
int port) {
int ret, s;
struct sockaddr_in myaddr;
in_addr_t ip=0;
#ifdef WINDOWS
static int lastSocket=-1;
/* Very ugly hack, but hey!, this is for Windows */
if(addr_type == ADDR_TYPE_MCAST) {
mcastListen(lastSocket, net_if, tmpl);
return -1;
} else if(addr_type != ADDR_TYPE_UCAST)
return -1;
#endif
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("make socket");
exit(1);
}
if(addr_type == ADDR_TYPE_MCAST && tmpl != NULL) {
ip = tmpl->sin_addr.s_addr;
}
ret = initSockAddress(addr_type, net_if, ip, port, &myaddr);
//TODO: FIXME
//if(ret < 0)
// udpc_fatal(1, "Could not get socket address fot %d/%s",
// addr_type, net_if->name);
if (addr_type == ADDR_TYPE_BCAST)
{
//cerr << "for addr_type == ADDR_TYPE_BCAST, myaddr.sin_addr.s_addr = 0x" << hex << myaddr.sin_addr.s_addr << dec << endl;
}
if(addr_type == ADDR_TYPE_BCAST && myaddr.sin_addr.s_addr == 0) {
/* Attempting to bind to broadcast address on not-broadcast media ... */
closesocket(s);
return -1;
}
ret = bind(s, (struct sockaddr *) &myaddr, sizeof(myaddr));
//TODO: FIXME
// if (ret < 0) {
// char buffer[16];
// udpc_fatal(1, "bind socket to %s:%d (%s)\n",
// udpc_getIpString(&myaddr, buffer),
// udpc_getPort(&myaddr),
// strerror(errno));
// }
if(addr_type == ADDR_TYPE_MCAST)
mcastListen(s, net_if, &myaddr);
#ifdef WINDOWS
lastSocket=s;
#endif
return s;
}
int udpc_setSocketToBroadcast(int sock) {
/* set the socket to broadcast */
int p = 1;
return setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&p, sizeof(int));
}
int udpc_getBroadCastAddress(net_if_t *net_if, struct sockaddr_in *addr,
short port){
int r= initSockAddress(ADDR_TYPE_BCAST, net_if, INADDR_ANY, port, addr);
if(addr->sin_addr.s_addr == 0) {
/* Quick hack to make it work for loopback */
struct sockaddr_in ucast;
initSockAddress(ADDR_TYPE_UCAST, net_if, INADDR_ANY, port, &ucast);
if((ntohl(ucast.sin_addr.s_addr) & 0xff000000) == 0x7f000000)
addr->sin_addr.s_addr = ucast.sin_addr.s_addr;
}
return r;
}
int safe_inet_aton(const char *address, struct in_addr *ip) {
if(!INET_ATON(address, ip))
{
//TODO: FIXME
//udpc_fatal(-1, "Bad address %s", address);
}
return 0;
}
int udpc_getMcastAllAddress(struct sockaddr_in *addr, const char *address,
short port){
struct in_addr ip;
int ret;
if(address == NULL || address[0] == '\0')
safe_inet_aton("224.0.0.1", &ip);
else {
if((ret=safe_inet_aton(address, &ip))<0)
return ret;
}
return initSockAddress(ADDR_TYPE_MCAST, NULL, ip.s_addr, port, addr);
}
void setPort(struct sockaddr_in *addr, unsigned short port) {
((struct sockaddr_in *) addr)->sin_port = htons(port);
}
int isMcastAddress(struct sockaddr_in *addr) {
int ip = ntohl(addr->sin_addr.s_addr) >> 24;
return ip >= 0xe0 && ip < 0xf0;
}
void udpc_clearIp(struct sockaddr_in *addr) {
addr->sin_addr.s_addr = 0;
addr->sin_family = AF_INET;
}
void udpc_setSendBuf(int sock, unsigned int bufsize) {
if(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&bufsize, sizeof(bufsize))< 0)
perror("Set send buffer");
}
int udpc_setTtl(int sock, int ttl) {
/* set the socket to broadcast */
return setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(int));
}
int udpc_getMyAddress(net_if_t *net_if, struct sockaddr_in *addr) {
return initSockAddress(ADDR_TYPE_UCAST, net_if, INADDR_ANY, 0, addr);
}
void udpc_getDefaultMcastAddress(net_if_t *net_if, struct sockaddr_in *mcast) {
udpc_getMyAddress(net_if, mcast);
mcast->sin_addr.s_addr &= htonl(0x07ffffff);
mcast->sin_addr.s_addr |= htonl(0xe8000000);
}
void udpc_copyIpFrom(struct sockaddr_in *dst, struct sockaddr_in *src) {
dst->sin_addr = src->sin_addr;
dst->sin_family = src->sin_family;
}
int udpc_getSelectedSock(int *socks, int nr, fd_set *read_set) {
int i;
int maxFd;
maxFd=-1;
for(i=0; i<nr; i++) {
if(socks[i] == -1)
continue;
if(FD_ISSET(socks[i], read_set))
return socks[i];
}
return -1;
}
int udpc_ipIsEqual(struct sockaddr_in *left, struct sockaddr_in *right) {
return getSinAddr(left).s_addr == getSinAddr(right).s_addr;
}
void udpc_closeSock(int *socks, int nr, int target) {
int i;
int sock = socks[target];
socks[target]=-1;
for(i=0; i<nr; i++)
if(socks[i] == sock)
return;
closesocket(sock);
}
int prepareForSelect(int *socks, int nr, fd_set *read_set) {
int i;
int maxFd;
FD_ZERO(read_set);
maxFd=-1;
for(i=0; i<nr; i++) {
if(socks[i] == -1)
continue;
FD_SET(socks[i], read_set);
if(socks[i] > maxFd)
maxFd = socks[i];
}
return maxFd;
}
int doReceive(int s, void *message, size_t len,
struct sockaddr_in *from, int portBase) {
socklen_t slen;
int r;
unsigned short port;
slen = sizeof(*from);
#ifdef LOSSTEST
loseRecvPacket(s);
#endif
r = recvfrom(s, message, len, 0, (struct sockaddr *)from, &slen);
if (r < 0)
return r;
port = ntohs(from->sin_port);
if(port != RECEIVER_PORT(portBase) && port != SENDER_PORT(portBase)) {
return -1;
}
/* flprintf("recv: %08x %d\n", *(int*) message, r);*/
return r;
}
participantsDb_t udpc_makeParticipantsDb(void)
{
return MALLOC(struct participantsDb);
}
int udpc_removeParticipant(struct participantsDb *db, int i) {
if(db->clientTable[i].used) {
db->clientTable[i].used = 0;
db->nrParticipants--;
}
return 0;
}
int udpc_lookupParticipant(struct participantsDb *db, struct sockaddr_in *addr) {
int i;
for (i=0; i < MAX_CLIENTS; i++) {
if (db->clientTable[i].used &&
udpc_ipIsEqual(&db->clientTable[i].addr, addr)) {
return i;
}
}
return -1;
}
int udpc_nrParticipants(participantsDb_t db) {
return db->nrParticipants;
}
int udpc_addParticipant(participantsDb_t db,
struct sockaddr_in *addr,
int capabilities,
unsigned int rcvbuf,
int pointopoint) {
//cerr << "adding a participant..." << endl;
int i;
if((i = udpc_lookupParticipant(db, addr)) >= 0)
return i;
for (i=0; i < MAX_CLIENTS; i++) {
if (!db->clientTable[i].used) {
db->clientTable[i].addr = *addr;
db->clientTable[i].used = 1;
db->clientTable[i].capabilities = capabilities;
db->clientTable[i].rcvbuf = rcvbuf;
db->nrParticipants++;
return i;
} else if(pointopoint)
return -1;
}
return -1; /* no space left in participant's table */
}
int selectWithoutConsole(int maxFd,
fd_set *read_set, struct timeval *tv) {
int ret;
ret = select(maxFd, read_set, NULL, NULL, tv);
if(ret < 0)
return -1;
return ret;
}
#if 0
void sendHello(struct net_config *net_config, int sock,
int streaming) {
struct hello hello;
/* send hello message */
if(streaming)
hello.opCode = htons(CMD_HELLO_STREAMING);
else
hello.opCode = htons(CMD_HELLO);
hello.reserved = 0;
hello.capabilities = htonl(net_config->capabilities);
udpc_copyToMessage(hello.mcastAddr,&net_config->dataMcastAddr);
hello.blockSize = htons(net_config->blockSize);
//rgWaitAll(net_config, sock, net_config->controlMcastAddr.sin_addr.s_addr, sizeof(hello));
BCAST_CONTROL(sock, hello);
}
#endif
int checkClientWait(participantsDb_t db,
struct net_config *net_config,
time_t *firstConnected)
{
time_t now;
if (!udpc_nrParticipants(db) || !firstConnected || !*firstConnected)
return 0; /* do not start: no receivers */
now = time(0);
/*
* If we have a max_client_wait, start the transfer after first client
* connected + maxSendWait
*/
if(net_config->max_receivers_wait &&
(now >= *firstConnected + net_config->max_receivers_wait)) {
#ifdef USE_SYSLOG
syslog(LOG_INFO, "max wait[%d] passed: starting",
net_config->max_receivers_wait );
#endif
return 1; /* send-wait passed: start */
}
/*
* Otherwise check to see if the minimum of clients
* have checked in.
*/
else if (udpc_nrParticipants(db) >= net_config->min_receivers &&
/*
* If there are enough clients and there's a min wait time, we'll
* wait around anyway until then.
* Otherwise, we always transfer
*/
(!net_config->min_receivers_wait ||
now >= *firstConnected + net_config->min_receivers_wait)) {
#ifdef USE_SYSLOG
syslog(LOG_INFO, "min receivers[%d] reached: starting",
net_config->min_receivers );
#endif
return 1;
} else
return 0;
}
int sendConnectionReply(participantsDb_t db,
int sock,
struct net_config *config,
struct sockaddr_in *client,
int capabilities,
unsigned int rcvbuf) {
struct connectReply reply;
if(rcvbuf == 0)
rcvbuf = 65536;
if(capabilities & CAP_BIG_ENDIAN) {
reply.opCode = htons(CMD_CONNECT_REPLY);
reply.clNr =
htonl(udpc_addParticipant(db,
client,
capabilities,
rcvbuf,
config->flags & FLAG_POINTOPOINT));
reply.blockSize = htonl(config->blockSize);
} else {
//TODO: FIXME
//udpc_fatal(1, "Little endian protocol no longer supported");
}
reply.reserved = 0;
if(config->flags & FLAG_POINTOPOINT) {
udpc_copyIpFrom(&config->dataMcastAddr, client);
}
/* new parameters: always big endian */
reply.capabilities = ntohl(config->capabilities);
udpc_copyToMessage(reply.mcastAddr,&config->dataMcastAddr);
/*reply.mcastAddress = mcastAddress;*/
//rgWaitAll(config, sock, client->sin_addr.s_addr, sizeof(reply));
if(SEND(sock, reply, *client) < 0) {
perror("reply add new client");
return -1;
}
return 0;
}
int mainDispatcher(int *fd, int nr,
participantsDb_t db,
struct net_config *net_config,
int *tries,
time_t *firstConnected)
{
struct sockaddr_in client;
union message fromClient;
fd_set read_set;
int ret;
int msgLength;
int startNow=0;
int selected;
if (firstConnected && !*firstConnected && udpc_nrParticipants(db)) {
*firstConnected = time(0);
#ifdef USE_SYSLOG
syslog(LOG_INFO,
"first connection: min wait[%d] secs - max wait[%d] - min clients[%d]",
net_config->min_receivers_wait, net_config->max_receivers_wait,
net_config->min_receivers );
#endif
}
while(!startNow) {
struct timeval tv;
struct timeval *tvp;
int nr_desc;
int maxFd = prepareForSelect(fd, nr, &read_set);
if(net_config->rexmit_hello_interval) {
tv.tv_usec = (net_config->rexmit_hello_interval % 1000)*1000;
tv.tv_sec = net_config->rexmit_hello_interval / 1000;
tvp = &tv;
} else if(firstConnected && udpc_nrParticipants(db)) {
tv.tv_usec = 0;
tv.tv_sec = 2;
tvp = &tv;
} else
tvp = 0;
nr_desc = selectWithoutConsole(maxFd+1, &read_set, tvp);
if(nr_desc < 0) {
perror("select");
return -1;
}
if(nr_desc > 0)
/* key pressed, or receiver activity */
break;
if(net_config->rexmit_hello_interval) {
/* retransmit hello message */
udpc_sendHello(net_config, fd[0], 0);
(*tries)++;
if(net_config->autostart != 0 && *tries > net_config->autostart)
startNow=1;
}
if(firstConnected)
startNow =
startNow || checkClientWait(db, net_config, firstConnected);
}
selected = udpc_getSelectedSock(fd, nr, &read_set);
if(selected == -1)
return startNow;
BZERO(fromClient); /* Zero it out in order to cope with short messages
* from older versions */
msgLength = RECV(selected, fromClient, client, net_config->portBase);
if(msgLength < 0) {
perror("problem getting data from client");
return 0; /* don't panic if we get weird messages */
}
if(net_config->flags & FLAG_ASYNC)
return 0;
switch(ntohs(fromClient.opCode)) {
case CMD_CONNECT_REQ:
sendConnectionReply(db, fd[0],
net_config,
&client,
CAP_BIG_ENDIAN |
ntohl(fromClient.connectReq.capabilities),
ntohl(fromClient.connectReq.rcvbuf));
return startNow;
case CMD_GO:
return 1;
case CMD_DISCONNECT:
ret = udpc_lookupParticipant(db, &client);
if (ret >= 0)
udpc_removeParticipant(db, ret);
return startNow;
default:
break;
}
return startNow;
}
static int isPointToPoint(participantsDb_t db, int flags) {
if(flags & FLAG_POINTOPOINT)
return 1;
if(flags & (FLAG_NOPOINTOPOINT | FLAG_ASYNC))
return 0;
return udpc_nrParticipants(db) == 1;
}
int getProducedAmount(produconsum_t pc) {
unsigned int produced = pc->produced;
unsigned int consumed = pc->consumed;
if(produced < consumed)
return produced + 2 * pc->size - consumed;
else
return produced - consumed;
}
int _consumeAny(produconsum_t pc, unsigned int minAmount,
struct timespec *ts) {
unsigned int amount;
#if DEBUG
flprintf("%s: Waiting for %d bytes (%d:%d)\n",
pc->name, minAmount, pc->consumed, pc->produced);
#endif
pc->consumerIsWaiting=1;
amount = getProducedAmount(pc);
if(amount >= minAmount || pc->atEnd) {
pc->consumerIsWaiting=0;
#if DEBUG
flprintf("%s: got %d bytes\n",pc->name, amount);
#endif
return amount;
}
pthread_mutex_lock(&pc->mutex);
while((amount=getProducedAmount(pc)) < minAmount && !pc->atEnd) {
#if DEBUG
flprintf("%s: ..Waiting for %d bytes (%d:%d)\n",
pc->name, minAmount, pc->consumed, pc->produced);
#endif
if(ts == 0)
pthread_cond_wait(&pc->cond, &pc->mutex);
else {
int r;
#if DEBUG
flprintf("Before timed wait\n");
#endif
r=pthread_cond_timedwait(&pc->cond, &pc->mutex, ts);
#if DEBUG
flprintf("After timed wait %d\n", r);
#endif
if(r == ETIMEDOUT) {
amount=getProducedAmount(pc);
break;
}
}
}
pthread_mutex_unlock(&pc->mutex);
#if DEBUG
flprintf("%s: Got them %d (for %d) %d\n", pc->name,
amount, minAmount, pc->atEnd);
#endif
pc->consumerIsWaiting=0;
return amount;
}
produconsum_t pc_makeProduconsum(int size, const char *name)
{
produconsum_t pc = MALLOC(struct produconsum);
pc->size = size;
pc->produced = 0;
pc->consumed = 0;
pc->atEnd = 0;
pthread_mutex_init(&pc->mutex, NULL);
pc->consumerIsWaiting = 0;
pthread_cond_init(&pc->cond, NULL);
pc->name = name;
return pc;
}
int pc_consumeAnyWithTimeout(produconsum_t pc, struct timespec *ts)
{
return _consumeAny(pc, 1, ts);
}
unsigned int pc_getProducerPosition(produconsum_t pc)
{
return pc->produced % pc->size;
}
unsigned int pc_getWaiting(produconsum_t pc)
{
return getProducedAmount(pc);
}
int pc_consumeAny(produconsum_t pc)
{
return _consumeAny(pc, 1, 0);
}
int pc_consume(produconsum_t pc, int amount)
{
return _consumeAny(pc, amount, 0);
}
void wakeConsumer(produconsum_t pc)
{
if(pc->consumerIsWaiting) {
pthread_mutex_lock(&pc->mutex);
pthread_cond_signal(&pc->cond);
pthread_mutex_unlock(&pc->mutex);
}
}
void pc_produceEnd(produconsum_t pc)
{
pc->atEnd = 1;
wakeConsumer(pc);
}
int pc_consumed(produconsum_t pc, int amount)
{
unsigned int consumed = pc->consumed;
if(consumed >= 2*pc->size - amount) {
consumed += amount - 2 *pc->size;
} else {
consumed += amount;
}
pc->consumed = consumed;
return amount;
}
unsigned int pc_getConsumerPosition(produconsum_t pc)
{
return pc->consumed % pc->size;
}
void pc_produce(produconsum_t pc, unsigned int amount)
{
unsigned int produced = pc->produced;
unsigned int consumed = pc->consumed;
/* sanity checks:
* 1. should not produce more than size
* 2. do not pass consumed+size
*/
if(amount > pc->size) {
//TODO: FIXME
//udpc_fatal(1, "Buffer overflow in produce %s: %d > %d \n", pc->name, amount, pc->size);
}
produced += amount;
if(produced >= 2*pc->size)
produced -= 2*pc->size;
if(produced > consumed + pc->size ||
(produced < consumed && produced > consumed - pc->size)) {
//TODO: FIXME
//udpc_fatal(1, "Buffer overflow in produce %s: %d > %d [%d] \n", pc->name, produced, consumed, pc->size);
}
pc->produced = produced;
wakeConsumer(pc);
}
void udpc_initFifo(struct fifo *fifo, int blockSize)
{
fifo->dataBufSize = blockSize * 4096;
fifo->dataBuffer = (unsigned char*)malloc(fifo->dataBufSize+4096);
fifo->dataBuffer += 4096 - (((unsigned long)fifo->dataBuffer) % 4096);
/* Free memory queue is initially full */
fifo->freeMemQueue = pc_makeProduconsum(fifo->dataBufSize, "free mem");
pc_produce(fifo->freeMemQueue, fifo->dataBufSize);
fifo->data = pc_makeProduconsum(fifo->dataBufSize, "receive");
}
THREAD_RETURN returnChannelMain(void *args) {
struct returnChannel *returnChannel = (struct returnChannel *) args;
while(1) {
struct sockaddr_in from;
int clNo;
int pos = pc_getConsumerPosition(returnChannel->freeSpace);
pc_consumeAny(returnChannel->freeSpace);
RECV(returnChannel->rcvSock,
returnChannel->q[pos].msg, from,
returnChannel->config->portBase);
clNo = udpc_lookupParticipant(returnChannel->participantsDb, &from);
if (clNo < 0) {
/* packet from unknown provenance */
continue;
}
returnChannel->q[pos].clNo = clNo;
pc_consumed(returnChannel->freeSpace, 1);
pc_produce(returnChannel->incoming, 1);
}
return 0;
}
void initReturnChannel(struct returnChannel *returnChannel,
struct net_config *config,
int sock) {
returnChannel->config = config;
returnChannel->rcvSock = sock;
returnChannel->freeSpace = pc_makeProduconsum(QUEUE_SIZE,"msg:free-queue");
pc_produce(returnChannel->freeSpace, QUEUE_SIZE);
returnChannel->incoming = pc_makeProduconsum(QUEUE_SIZE,"msg:incoming");
pthread_create(&returnChannel->thread, NULL,
returnChannelMain, returnChannel);
}
void senderStatsAddBytes(sender_stats_t ss, long bytes) {
if(ss != NULL) {
ss->totalBytes += bytes;
if(ss->bwPeriod) {
double tdiff, bw;
struct timeval tv;
gettimeofday(&tv, 0);
ss->periodBytes += bytes;
if(tv.tv_sec - ss->periodStart.tv_sec < ss->bwPeriod-1)
return;
tdiff = (tv.tv_sec-ss->periodStart.tv_sec) * 1000000.0 +
tv.tv_usec - ss->periodStart.tv_usec;
if(tdiff < ss->bwPeriod * 1000000.0)
return;
bw = ss->periodBytes * 8.0 / tdiff;
ss->periodBytes=0;
ss->periodStart = tv;
}
}
}
int ackSlice(struct slice *slice, struct net_config *net_config,
struct fifo *fifo, sender_stats_t stats)
{
if(slice->state == slice::SLICE_ACKED)
/* already acked */
return 0;
if(!(net_config->flags & FLAG_SN)) {
if(net_config->discovery == net_config::DSC_DOUBLING) {
net_config->sliceSize += net_config->sliceSize / 4;
if(net_config->sliceSize >= net_config->max_slice_size) {
net_config->sliceSize = net_config->max_slice_size;
net_config->discovery = net_config::DSC_REDUCING;
}
}
}
slice->state = slice::SLICE_ACKED;
pc_produce(fifo->freeMemQueue, slice->bytes);
/* Statistics */
senderStatsAddBytes(stats, slice->bytes);
/* End Statistics */
return 0;
}
int isSliceAcked(struct slice *slice)
{
if(slice->state == slice::SLICE_ACKED) {
return 1;
} else {
return 0;
}
}
int freeSlice(sender_state_t sendst, struct slice *slice) {
int i;
i = slice - sendst->slices;
slice->state = slice::SLICE_PRE_FREE;
while(1) {
int pos = pc_getProducerPosition(sendst->free_slices_pc);
if(sendst->slices[pos].state == slice::SLICE_PRE_FREE)
sendst->slices[pos].state = slice::SLICE_FREE;
else
break;
pc_produce(sendst->free_slices_pc, 1);
}
return 0;
}
int isSliceXmitted(struct slice *slice) {
if(slice->state == slice::SLICE_XMITTED) {
return 1;
} else {
return 0;
}
}
int getSliceBlocks(struct slice *slice, struct net_config *net_config)
{
return (slice->bytes + net_config->blockSize - 1) / net_config->blockSize;
}
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(slice->lastGoodBlock != 0 && slice->lastGoodBlock < nrBlocks) {
net_config->discovery = net_config::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;
}
}
}
slice->lastGoodBlock = 0;
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);
//rgWaitAll(net_config, sock, net_config->dataMcastAddr.sin_addr.s_addr, sizeof(slice->sl_reqack));
BCAST_DATA(sock, slice->sl_reqack);
return 0;
}
struct slice *findSlice(struct slice *slice1,
struct slice *slice2,
int sliceNo)
{
if(slice1 != NULL && slice1->sliceNo == sliceNo)
return slice1;
if(slice2 != NULL && slice2->sliceNo == sliceNo)
return slice2;
return NULL;
}
void markParticipantAnswered(slice_t slice, int clNo)
{
if(BIT_ISSET(clNo, slice->answeredSet))
/* client already has answered */
return;
slice->nrAnswered++;
SET_BIT(clNo, slice->answeredSet);
}
int udpc_isParticipantValid(struct participantsDb *db, int i) {
return db->clientTable[i].used;
}
void senderSetAnswered(sender_stats_t ss, int clNo) {
if(ss != NULL)
ss->clNo = clNo;
}
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 */
} else {
SET_BIT(clNo, slice->sl_reqack.readySet);
slice->nrReady++;
senderSetAnswered(sendst->stats, clNo);
markParticipantAnswered(slice, clNo);
}
return 0;
}
int handleDisconnect1(struct slice *slice, int clNo)
{
if(slice != NULL) {
if (BIT_ISSET(clNo, slice->sl_reqack.readySet)) {
/* avoid counting client both as left and ready */
CLR_BIT(clNo, slice->sl_reqack.readySet);
slice->nrReady--;
}
if (BIT_ISSET(clNo, slice->answeredSet)) {
slice->nrAnswered--;
CLR_BIT(clNo, slice->answeredSet);
}
}
return 0;
}
int handleDisconnect(participantsDb_t db,
struct slice *slice1,
struct slice *slice2,
int clNo)
{
handleDisconnect1(slice1, clNo);
handleDisconnect1(slice2, clNo);
udpc_removeParticipant(db, clNo);
return 0;
}
int handleRetransmit(sender_state_t sendst,
struct slice *slice,
int clNo, unsigned char *map, int rxmit)
{
unsigned int i;
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) {
/* late answer to previous Req Ack */
return 0;
}
for(i=0; i <sizeof(slice->rxmitMap) / sizeof(char); i++) {
slice->rxmitMap[i] |= ~map[i];
}
slice->needRxmit = 1;
markParticipantAnswered(slice, clNo);
return 0;
}
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;
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:
handleRetransmit(sendst,
findSlice(xmitSlice, rexmitSlice,
ntohl(msg->retransmit.sliceNo)),
clNo,
msg->retransmit.map,
msg->retransmit.rxmit);
break;
default:
//TODO: FIXME
//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;
}
int sendRawData(int sock,
struct net_config *config,
char *header, int headerSize,
unsigned char *data, int dataSize)
{
struct iovec iov[2];
struct msghdr hdr;
int packetSize;
int ret;
iov[0].iov_base = header;
iov[0].iov_len = headerSize;
iov[1].iov_base = data;
iov[1].iov_len = dataSize;
hdr.msg_name = &config->dataMcastAddr;
hdr.msg_namelen = sizeof(struct sockaddr_in);
hdr.msg_iov = iov;
hdr.msg_iovlen = 2;
initMsgHdr(&hdr);
packetSize = dataSize + headerSize;
//rgWaitAll(config, sock, config->dataMcastAddr.sin_addr.s_addr, packetSize);
ret = sendmsg(sock, &hdr, 0);
if (ret < 0) {
//TODO: FIXME
}
return 0;
}
int transmitDataBlock(sender_state_t sendst, struct slice *slice, int i)
{
struct fifo *fifo = sendst->fifo;
struct net_config *config = sendst->config;
struct dataBlock msg;
idbassert(i < MAX_SLICE_SIZE);
msg.opCode = htons(CMD_DATA);
msg.sliceNo = htonl(slice->sliceNo);
msg.blockNo = htons(i);
msg.reserved = 0;
msg.reserved2 = 0;
msg.bytes = htonl(slice->bytes);
sendRawData(sendst->socket, config,
(char *) &msg, sizeof(msg),
fifo->dataBuffer +
(slice->base + i * config->blockSize) % fifo->dataBufSize,
config->blockSize);
return 0;
}
void senderStatsAddRetransmissions(sender_stats_t ss, int retransmissions) {
if(ss != NULL) {
ss->retransmissions += retransmissions;
}
}
int sendSlice(sender_state_t sendst, struct slice *slice,
int retransmitting)
{
struct net_config *config = sendst->config;
int nrBlocks, i, rehello;
#ifdef BB_FEATURE_UDPCAST_FEC
int fecBlocks;
#endif
int retransmissions=0;
if(retransmitting) {
slice->nextBlock = 0;
if(slice->state != slice::SLICE_XMITTED)
return 0;
} else {
if(slice->state != slice::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((sendst->config->flags & FLAG_STREAMING)) {
rehello = nrBlocks - sendst->config->rehelloOffset;
if(rehello < 0)
rehello = 0;
} else {
rehello = -1;
}
/* 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(i == rehello) {
udpc_sendHello(sendst->config, sendst->socket, 1);
}
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::SLICE_XMITTED;
return 2;
}
return 1;
}
int doRetransmissions(sender_state_t sendst,
struct slice *slice)
{
if(slice->state == slice::SLICE_ACKED)
return 0; /* nothing to do */
/* FIXME: reduce slice size if needed */
if(slice->needRxmit) {
/* do some retransmissions */
sendSlice(sendst, slice, 1);
}
return 0;
}
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];
idbassert(slice->state == slice::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;
pc_consumed(fifo->data, slice->bytes);
slice->nextBlock = 0;
slice->state = slice::SLICE_NEW;
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;
}
void cancelReturnChannel(struct returnChannel *returnChannel) {
/* No need to worry about the pthread_cond_wait in produconsum, because
* at the point where we enter here (to cancel the thread), we are sure
* that nobody else uses that produconsum any more
*/
pthread_cancel(returnChannel->thread);
pthread_join(returnChannel->thread, NULL);
}
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 = net_config::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;
idbassert(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::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 = (long int)((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 = (long unsigned int)((0.9 * waitAverage + 0.1 * timeout));
}
nrWaited = 0;
continue;
}
if(rexmitSlice == NULL) {
//TODO: FIXME
//udpc_flprintf("Weird. Timeout and no rxmit slice");
break;
}
if(nrWaited > 5){
#ifndef WINDOWS
/* on Cygwin, we would get too many of those messages... */
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_removeParticipant(sendst->rc.participantsDb, i);
if(udpc_nrParticipants(sendst->rc.participantsDb) == 0)
goto exit_main_loop;
}
}
continue;
}
rexmitSlice->rxmitId++;
sendReqack(rexmitSlice, sendst->config, sendst->fifo, sendst->stats,
sendst->socket);
} while(udpc_nrParticipants(sendst->rc.participantsDb)||
(config->flags & FLAG_ASYNC));
exit_main_loop:
cancelReturnChannel(&sendst->rc);
pc_produceEnd(sendst->fifo->freeMemQueue);
return 0;
}
int spawnNetSender(struct fifo *fifo,
int sock,
struct net_config *config,
participantsDb_t db)
{
int i;
sender_state_t sendst = MALLOC(struct senderState);
sendst->fifo = fifo;
sendst->socket = sock;
sendst->config = config;
//sendst->stats = stats;
#ifdef BB_FEATURE_UDPCAST_FEC
if(sendst->config->flags & FLAG_FEC)
sendst->fec_data = xmalloc(NR_SLICES *
config->fec_stripes *
config->fec_redundancy *
config->blockSize);
#endif
sendst->rc.participantsDb = db;
initReturnChannel(&sendst->rc, sendst->config, sendst->socket);
sendst->free_slices_pc = pc_makeProduconsum(NR_SLICES, "free slices");
pc_produce(sendst->free_slices_pc, NR_SLICES);
for(i = 0; i <NR_SLICES; i++)
sendst->slices[i].state = slice::SLICE_FREE;
#ifdef BB_FEATURE_UDPCAST_FEC
if(sendst->config->flags & FLAG_FEC) {
/* Free memory queue is initially full */
fec_init();
sendst->fec_data_pc = pc_makeProduconsum(NR_SLICES, "fec data");
pthread_create(&sendst->fec_thread, NULL, fecMain, sendst);
}
#endif
pthread_create(&fifo->thread, NULL, netSenderMain, sendst);
return 0;
}
struct sockaddr_in *udpc_getParticipantIp(participantsDb_t db, int i)
{
return &db->clientTable[i].addr;
}
int udpc_getParticipantCapabilities(participantsDb_t db, int i)
{
return db->clientTable[i].capabilities;
}
unsigned int udpc_getParticipantRcvBuf(participantsDb_t db, int i)
{
return db->clientTable[i].rcvbuf;
}
int pc_consumeContiguousMinAmount(produconsum_t pc, int amount)
{
int n = _consumeAny(pc, amount, 0);
int l = pc->size - (pc->consumed % pc->size);
if(n > l)
n = l;
return n;
}
#define BLOCKSIZE 4096
void localReader(struct fifo* fifo, const uint8_t* buf, uint32_t len)
{
//cerr << "starting to send " << len << " bytes" << endl;
uint32_t offset = 0;
while(1) {
int pos = pc_getConsumerPosition(fifo->freeMemQueue);
int bytes =
pc_consumeContiguousMinAmount(fifo->freeMemQueue, BLOCKSIZE);
if(bytes > (pos + bytes) % BLOCKSIZE)
bytes -= (pos + bytes) % BLOCKSIZE;
if (offset + bytes > len) bytes = len - offset;
//cerr << "sending " << bytes << " bytes from bs..." << endl;
//bytes = read(in, fifo->dataBuffer + pos, bytes);
memcpy(fifo->dataBuffer + pos, buf + offset, bytes);
offset += bytes;
if (bytes == 0) {
/* the end */
pc_produceEnd(fifo->data);
break;
} else {
pc_consumed(fifo->freeMemQueue, bytes);
pc_produce(fifo->data, bytes);
}
}
//cerr << "done sending" << endl;
}
unsigned int udpc_getRcvBuf(int sock) {
unsigned int bufsize;
socklen_t len=sizeof(int);
if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, &len) < 0)
return -1;
return bufsize;
}
int sendConnectReq(struct client_config *client_config,
struct net_config *net_config,
int haveServerAddress) {
//cerr << "sending a connect request" << endl;
struct connectReq connectReq;
if(net_config->flags & FLAG_PASSIVE)
return 0;
connectReq.opCode = htons(CMD_CONNECT_REQ);
connectReq.reserved = 0;
connectReq.capabilities = htonl(RECEIVER_CAPABILITIES);
connectReq.rcvbuf = htonl(udpc_getRcvBuf(client_config->S_UCAST));
if(haveServerAddress)
return SSEND(connectReq);
else
return BCAST_CONTROL(client_config->S_UCAST, connectReq);
}
void udpc_setRcvBuf(int sock, unsigned int bufsize) {
if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(char*) &bufsize, sizeof(bufsize))< 0)
{
//TODO: FIXME
//perror("Set receiver buffer");
}
}
void udpc_copyFromMessage(struct sockaddr_in *dst, unsigned char *src) {
memcpy((char *) &dst->sin_addr, src, sizeof(struct in_addr));
}
unsigned short udpc_getPort(struct sockaddr_in *addr) {
return ntohs(((struct sockaddr_in *) addr)->sin_port);
}
int udpc_selectSock(int *socks, int nr, int startTimeout) {
fd_set read_set;
int r;
int maxFd;
struct timeval tv, *tvp;
if(startTimeout) {
tv.tv_sec = startTimeout;
tv.tv_usec = 0;
tvp = &tv;
} else {
tvp = NULL;
}
maxFd = prepareForSelect(socks, nr, &read_set);
r = select(maxFd+1, &read_set, NULL, NULL, tvp);
if(r < 0)
return r;
return udpc_getSelectedSock(socks, nr, &read_set);
}
void udpc_zeroSockArray(int *socks, int nr) {
int i;
for(i=0; i<nr; i++)
socks[i] = -1;
}
unsigned char *getBlockSpace(struct clientState *clst)
{
int pos;
if(clst->localPos) {
clst->localPos--;
return clst->localBlockAddresses[clst->localPos];
}
pc_consume(clst->freeBlocks_pc, 1);
pos = pc_getConsumerPosition(clst->freeBlocks_pc);
pc_consumed(clst->freeBlocks_pc, 1);
return clst->blockAddresses[pos];
}
void setNextBlock(struct clientState *clst)
{
clst->nextBlock = getBlockSpace(clst);
}
int setupMessages(struct clientState *clst) {
/* the messages received from the server */
clst->data_iov[0].iov_base = (void *)&clst->Msg;
clst->data_iov[0].iov_len = sizeof(clst->Msg);
/* namelen set just before reception */
clst->data_hdr.msg_iov = clst->data_iov;
/* iovlen set just before reception */
initMsgHdr(&clst->data_hdr);
return 0;
}
struct slice *initSlice(struct clientState *clst,
struct slice *slice,
int sliceNo)
{
idbassert(slice->state == slice::SLICE_FREE || slice->state == slice::SLICE_RECEIVING);
slice->magic = SLICEMAGIC;
slice->state = slice::SLICE_RECEIVING;
slice->blocksTransferred = 0;
slice->dataBlocksTransferred = 0;
BZERO(slice->retransmit);
slice->freePos = 0;
slice->bytes = 0;
if(clst->currentSlice != NULL && !clst->currentSlice->bytesKnown) {
//udpc_fatal(1, "Previous slice size not known\n");
}
if(clst->currentSliceNo != sliceNo-1) {
//udpc_fatal(1, "Slice no mismatch %d <-> %d\n", sliceNo, clst->currentSliceNo);
}
slice->bytesKnown = 0;
slice->sliceNo = sliceNo;
BZERO(slice->missing_data_blocks);
#ifdef BB_FEATURE_UDPCAST_FEC
BZERO(slice->fec_stripes);
BZERO(slice->fec_blocks);
BZERO(slice->fec_descs);
#endif
clst->currentSlice = slice;
clst->currentSliceNo = sliceNo;
return slice;
}
struct slice *newSlice(struct clientState *clst, int sliceNo)
{
struct slice *slice=NULL;
int i;
pc_consume(clst->free_slices_pc, 1);
i = pc_getConsumerPosition(clst->free_slices_pc);
pc_consumed(clst->free_slices_pc, 1);
slice = &clst->slices[i];
idbassert(slice->state == slice::SLICE_FREE);
/* wait for free data memory */
slice->base = pc_getConsumerPosition(clst->fifo->freeMemQueue);
pc_consume(clst->fifo->freeMemQueue,
clst->net_config->blockSize * MAX_SLICE_SIZE);
initSlice(clst, slice, sliceNo);
return slice;
}
#define ADR(x, bs) (fifo->dataBuffer + \
(slice->base+(x)*bs) % fifo->dataBufSize)
void closeAllExcept(struct clientState *clst, int fd) {
int i;
int *socks = clst->client_config->socks;
if(clst->selectedFd >= 0)
return;
clst->selectedFd = fd;
for(i=1; i<NR_CLIENT_SOCKS; i++)
if(socks[i] != -1 && socks[i] != fd)
udpc_closeSock(socks, NR_CLIENT_SOCKS, i);
}
struct slice *rcvFindSlice(struct clientState *clst, int sliceNo)
{
if(! clst->currentSlice) {
/* Streaming mode? */
clst->currentSliceNo = sliceNo-1;
return newSlice(clst, sliceNo);
}
if(sliceNo <= clst->currentSliceNo) {
struct slice *slice = clst->currentSlice;
int pos = slice - clst->slices;
idbassert(slice == NULL || slice->magic == SLICEMAGIC);
while(slice->sliceNo != sliceNo) {
if(slice->state == slice::SLICE_FREE)
return NULL;
idbassert(slice->magic == SLICEMAGIC);
pos--;
if(pos < 0)
pos += NR_SLICES;
slice = &clst->slices[pos];
}
return slice;
}
if(sliceNo != clst->currentSliceNo + 1) {
//TODO: FIXME
//udpc_flprintf("Slice %d skipped\n", sliceNo-1);
//exit(1);
}
if((clst->net_config->flags & FLAG_STREAMING) &&
sliceNo != clst->currentSliceNo) {
return initSlice(clst, clst->currentSlice, sliceNo);
}
if(sliceNo > clst->receivedSliceNo + 2) {
//TODO: FIXME
#if 0
slice_t slice = rcvFindSlice(clst, clst->receivedSliceNo+1);
udpc_flprintf("Dropped by server now=%d last=%d\n", sliceNo, clst->receivedSliceNo);
if(slice != NULL)
printMissedBlockMap(clst, slice);
exit(1);
#endif
}
return newSlice(clst, sliceNo);
}
void setSliceBytes(struct slice *slice,
struct clientState *clst,
int bytes) {
idbassert(slice->magic == SLICEMAGIC);
if(slice->bytesKnown) {
if(slice->bytes != bytes) {
//TODO: FIXME
//udpc_fatal(1, "Byte number mismatch %d <-> %d\n", bytes, slice->bytes);
}
} else {
slice->bytesKnown = 1;
slice->bytes = bytes;
if(bytes == 0)
clst->netEndReached=1;
}
}
void advanceReceivedPointer(struct clientState *clst) {
int pos = clst->receivedPtr;
while(1) {
slice_t slice = &clst->slices[pos];
if(
#ifdef BB_FEATURE_UDPCAST_FEC
slice->state != SLICE_FEC &&
slice->state != SLICE_FEC_DONE &&
#endif
slice->state != slice::SLICE_DONE)
break;
pos++;
clst->receivedSliceNo = slice->sliceNo;
if(pos >= NR_SLICES)
pos -= NR_SLICES;
}
clst->receivedPtr = pos;
}
void receiverStatsAddBytes(receiver_stats_t rs, long bytes) {
if(rs != NULL)
rs->totalBytes += bytes;
}
void cleanupSlices(struct clientState *clst, unsigned int doneState)
{
while(1) {
int pos = pc_getProducerPosition(clst->free_slices_pc);
int bytes;
slice_t slice = &clst->slices[pos];
if(slice->state != (signed)doneState)
break;
receiverStatsAddBytes(clst->stats, slice->bytes);
bytes = slice->bytes;
/* signal data received */
if(bytes == 0) {
pc_produceEnd(clst->fifo->data);
} else
pc_produce(clst->fifo->data, slice->bytes);
/* free up slice structure */
clst->slices[pos].state = slice::SLICE_FREE;
pc_produce(clst->free_slices_pc, 1);
/* if at end, exit this thread */
if(!bytes) {
clst->endReached = 2;
}
}
}
void checkSliceComplete(struct clientState *clst,
struct slice *slice)
{
int blocksInSlice;
idbassert(slice->magic == SLICEMAGIC);
if(slice->state != slice::SLICE_RECEIVING)
/* bad starting state */
return;
/* is this slice ready ? */
idbassert(clst->net_config->blockSize != 0);
blocksInSlice = (slice->bytes + clst->net_config->blockSize - 1) /
clst->net_config->blockSize;
if(blocksInSlice == slice->blocksTransferred) {
pc_consumed(clst->fifo->freeMemQueue, slice->bytes);
clst->net_config->flags &= ~FLAG_STREAMING;
if(blocksInSlice == slice->dataBlocksTransferred)
slice->state = slice::SLICE_DONE;
else {
#ifdef BB_FEATURE_UDPCAST_FEC
idbassert(clst->use_fec == 1);
slice->state = SLICE_FEC;
#else
idbassert(0);
#endif
}
advanceReceivedPointer(clst);
#ifdef BB_FEATURE_UDPCAST_FEC
if(clst->use_fec) {
int n = pc_getProducerPosition(clst->fec_data_pc);
idbassert(slice->state == SLICE_DONE || slice->state == SLICE_FEC);
clst->fec_slices[n] = slice;
pc_produce(clst->fec_data_pc, 1);
} else
#endif
cleanupSlices(clst, slice::SLICE_DONE);
}
}
int processDataBlock(struct clientState *clst,
int sliceNo,
int blockNo,
int bytes)
{
//cerr << "processDataBlock(): " << sliceNo << ", " << blockNo << ", " << bytes << endl;
struct fifo *fifo = clst->fifo;
struct slice *slice = rcvFindSlice(clst, sliceNo);
unsigned char *shouldAddress, *isAddress;
idbassert(slice == NULL || slice->magic == SLICEMAGIC);
if(slice == NULL ||
slice->state == slice::SLICE_FREE ||
slice->state == slice::SLICE_DONE
#ifdef BB_FEATURE_UDPCAST_FEC
||
slice->state == SLICE_FEC ||
slice->state == SLICE_FEC_DONE
#endif
) {
/* an old slice. Ignore */
//cerr << "ignore" << endl;
return 0;
}
if(sliceNo > clst->currentSliceNo+2)
{
//TODO: FIXME
//udpc_fatal(1, "We have been dropped by sender\n");
}
if(BIT_ISSET(blockNo, slice->retransmit.map)) {
/* we already have this packet, ignore */
#if 0
flprintf("Packet %d:%d not for us\n", sliceNo, blockNo);
#endif
//cerr << "dup" << endl;
return 0;
}
if(slice->base % clst->net_config->blockSize) {
//TODO: FIXME
//udpc_fatal(1, "Bad base %d, not multiple of block size %d\n", slice->base, clst->net_config->blockSize);
//cerr << "bad base" << endl;
}
//cerr << "good slice" << endl;
shouldAddress = ADR(blockNo, clst->net_config->blockSize);
isAddress = (unsigned char*)clst->data_hdr.msg_iov[1].iov_base;
if(shouldAddress != isAddress) {
/* copy message to the correct place */
memcpy(shouldAddress, isAddress, clst->net_config->blockSize);
}
if(clst->client_config->sender_is_newgen && bytes != 0)
setSliceBytes(slice, clst, bytes);
if(clst->client_config->sender_is_newgen && bytes == 0)
clst->netEndReached = 0;
SET_BIT(blockNo, slice->retransmit.map);
#ifdef BB_FEATURE_UDPCAST_FEC
if(slice->fec_stripes) {
int stripe = blockNo % slice->fec_stripes;
slice->missing_data_blocks[stripe]--;
idbassert(slice->missing_data_blocks[stripe] >= 0);
if(slice->missing_data_blocks[stripe] <
slice->fec_blocks[stripe]) {
int blockIdx;
/* FIXME: FEC block should be enqueued in local queue here...*/
slice->fec_blocks[stripe]--;
blockIdx = stripe+slice->fec_blocks[stripe]*slice->fec_stripes;
idbassert(slice->fec_descs[blockIdx].adr != 0);
clst->localBlockAddresses[clst->localPos++] =
slice->fec_descs[blockIdx].adr;
slice->fec_descs[blockIdx].adr=0;
slice->blocksTransferred--;
}
}
#endif
slice->dataBlocksTransferred++;
slice->blocksTransferred++;
while(slice->freePos < MAX_SLICE_SIZE &&
BIT_ISSET(slice->freePos, slice->retransmit.map))
slice->freePos++;
checkSliceComplete(clst, slice);
return 0;
}
int sendOk(struct client_config *client_config, unsigned int sliceNo)
{
struct ok ok;
ok.opCode = htons(CMD_OK);
ok.reserved = 0;
ok.sliceNo = htonl(sliceNo);
return SSEND(ok);
}
int sendRetransmit(struct clientState *clst,
struct slice *slice,
int rxmit) {
struct client_config *client_config = clst->client_config;
idbassert(slice->magic == SLICEMAGIC);
slice->retransmit.opCode = htons(CMD_RETRANSMIT);
slice->retransmit.reserved = 0;
slice->retransmit.sliceNo = htonl(slice->sliceNo);
slice->retransmit.rxmit = htonl(rxmit);
return SSEND(slice->retransmit);
}
int processReqAck(struct clientState *clst,
int sliceNo, int bytes, int rxmit)
{
struct slice *slice = rcvFindSlice(clst, sliceNo);
int blocksInSlice;
char *readySet = (char *) clst->data_hdr.msg_iov[1].iov_base;
idbassert(slice == NULL || slice->magic == SLICEMAGIC);
{
struct timeval tv;
gettimeofday(&tv, 0);
/* usleep(1); DEBUG: FIXME */
}
if(BIT_ISSET(clst->client_config->clientNumber, readySet)) {
/* not for us */
return 0;
}
if(slice == NULL) {
/* an old slice => send ok */
return sendOk(clst->client_config, sliceNo);
}
setSliceBytes(slice, clst, bytes);
idbassert(clst->net_config->blockSize != 0);
blocksInSlice = (slice->bytes + clst->net_config->blockSize - 1) /
clst->net_config->blockSize;
if (blocksInSlice == slice->blocksTransferred) {
/* send ok */
sendOk(clst->client_config, slice->sliceNo);
} else {
sendRetransmit(clst, slice, rxmit);
}
checkSliceComplete(clst, slice); /* needed for the final 0 sized slice */
advanceReceivedPointer(clst);
#ifdef BB_FEATURE_UDPCAST_FEC
if(!clst->use_fec)
cleanupSlices(clst, SLICE_DONE);
#endif
return 0;
}
int udpc_isAddressEqual(struct sockaddr_in *a, struct sockaddr_in *b) {
return !memcmp((char *) a, (char *)b, 8);
}
int dispatchMessage(struct clientState *clst)
{
int ret;
struct sockaddr_in lserver;
struct fifo *fifo = clst->fifo;
int fd = -1;
struct client_config *client_config = clst->client_config;
/* set up message header */
if (clst->currentSlice != NULL &&
clst->currentSlice->freePos < MAX_SLICE_SIZE) {
struct slice *slice = clst->currentSlice;
idbassert(slice == NULL || slice->magic == SLICEMAGIC);
clst->data_iov[1].iov_base =
ADR(slice->freePos, clst->net_config->blockSize);
} else {
clst->data_iov[1].iov_base = clst->nextBlock;
}
clst->data_iov[1].iov_len = clst->net_config->blockSize;
clst->data_hdr.msg_iovlen = 2;
clst->data_hdr.msg_name = &lserver;
clst->data_hdr.msg_namelen = sizeof(struct sockaddr_in);
while(clst->endReached || clst->netEndReached) {
int oldEndReached = clst->endReached;
int nr_desc;
struct timeval tv;
fd_set read_set;
int maxFd = prepareForSelect(client_config->socks,
NR_CLIENT_SOCKS, &read_set);
tv.tv_sec = clst->net_config->exitWait / 1000;
tv.tv_usec = (clst->net_config->exitWait % 1000)*1000;
nr_desc = select(maxFd, &read_set, 0, 0, &tv);
if(nr_desc < 0) {
break;
}
fd = udpc_getSelectedSock(client_config->socks, NR_CLIENT_SOCKS, &read_set);
if(fd >= 0)
break;
/* Timeout expired */
if(oldEndReached >= 2) {
clst->endReached = 3;
return 0;
}
}
if(fd < 0)
fd = clst->selectedFd;
if(fd < 0) {
struct timeval tv, *tvp;
fd_set read_set;
int maxFd = prepareForSelect(client_config->socks,
NR_CLIENT_SOCKS, &read_set);
clst->promptPrinted=1;
if(clst->net_config->startTimeout == 0) {
tvp=NULL;
} else {
tv.tv_sec = clst->net_config->startTimeout;
tv.tv_usec = 0;
tvp = &tv;
}
//cerr << "waiting for data..." << endl;
ret = selectWithoutConsole(maxFd+1, &read_set, tvp);
if(ret < 0) {
perror("Select");
return 0;
}
if(ret == 0) {
clst->endReached=3;
clst->netEndReached=3;
pc_produceEnd(clst->fifo->data);
return 1;
}
fd = udpc_getSelectedSock(clst->client_config->socks,
NR_CLIENT_SOCKS, &read_set);
}
#ifdef LOSSTEST
loseRecvPacket(fd);
ret=RecvMsg(fd, &clst->data_hdr, 0);
#else
ret=recvmsg(fd, &clst->data_hdr, 0);
//cerr << "got some data on fd " << fd << endl;
#endif
if (ret < 0) {
#if DEBUG
flprintf("data recvfrom %d: %s\n", fd, strerror(errno));
#endif
return -1;
}
#if 0
fprintf(stderr, "received packet for slice %d, block %d\n",
ntohl(Msg.sliceNo), ntohs(db.blockNo));
#endif
if(!udpc_isAddressEqual(&lserver,
&clst->client_config->serverAddr)) {
return -1;
}
switch(ntohs(clst->Msg.opCode)) {
case CMD_DATA:
//cerr << "got CMD_DATA" << endl;
closeAllExcept(clst, fd);
//udpc_receiverStatsStartTimer(clst->stats);
clst->client_config->isStarted = 1;
return processDataBlock(clst,
ntohl(clst->Msg.dataBlock.sliceNo),
ntohs(clst->Msg.dataBlock.blockNo),
ntohl(clst->Msg.dataBlock.bytes));
#ifdef BB_FEATURE_UDPCAST_FEC
case CMD_FEC:
//cerr << "got CMD_FEC" << endl;
closeAllExcept(clst, fd);
//receiverStatsStartTimer(clst->stats);
clst->client_config->isStarted = 1;
return processFecBlock(clst,
ntohs(clst->Msg.fecBlock.stripes),
ntohl(clst->Msg.fecBlock.sliceNo),
ntohs(clst->Msg.fecBlock.blockNo),
ntohl(clst->Msg.fecBlock.bytes));
#endif
case CMD_REQACK:
//cerr << "got CMD_REQACK" << endl;
closeAllExcept(clst, fd);
//receiverStatsStartTimer(clst->stats);
clst->client_config->isStarted = 1;
return processReqAck(clst,
ntohl(clst->Msg.reqack.sliceNo),
ntohl(clst->Msg.reqack.bytes),
ntohl(clst->Msg.reqack.rxmit));
case CMD_HELLO_STREAMING:
case CMD_HELLO_NEW:
case CMD_HELLO:
//cerr << "got CMD_HELLO" << endl;
/* retransmission of hello to find other participants ==> ignore */
return 0;
default:
break;
}
return -1;
}
THREAD_RETURN netReceiverMain(void *args0)
{
struct clientState *clst = (struct clientState *) args0;
clst->currentSliceNo = 0;
setupMessages(clst);
clst->currentSliceNo = -1;
clst->currentSlice = NULL;
clst->promptPrinted = 0;
if(! (clst->net_config->flags & FLAG_STREAMING))
newSlice(clst, 0);
else {
clst->currentSlice = NULL;
clst->currentSliceNo = 0;
}
while(clst->endReached < 3) {
dispatchMessage(clst);
}
#ifdef BB_FEATURE_UDPCAST_FEC
if(clst->use_fec)
pthread_join(clst->fec_thread, NULL);
#endif
return 0;
}
int spawnNetReceiver(struct fifo *fifo,
struct client_config *client_config,
struct net_config *net_config)
{
int i;
struct clientState *clst = MALLOC(struct clientState);
clst->fifo = fifo;
clst->client_config = client_config;
clst->net_config = net_config;
//clst->stats = stats;
clst->endReached = 0;
clst->netEndReached = 0;
clst->selectedFd = -1;
clst->free_slices_pc = pc_makeProduconsum(NR_SLICES, "free slices");
pc_produce(clst->free_slices_pc, NR_SLICES);
for(i = 0; i <NR_SLICES; i++)
clst->slices[i].state = slice::SLICE_FREE;
clst->receivedPtr = 0;
clst->receivedSliceNo = 0;
#ifdef BB_FEATURE_UDPCAST_FEC
fec_init(); /* fec new involves memory
* allocation. Better do it here */
clst->use_fec = 0;
clst->fec_data_pc = pc_makeProduconsum(NR_SLICES, "fec data");
#endif
#define NR_BLOCKS 4096
clst->freeBlocks_pc = pc_makeProduconsum(NR_BLOCKS, "free blocks");
pc_produce(clst->freeBlocks_pc, NR_BLOCKS);
clst->blockAddresses = (unsigned char**)calloc(NR_BLOCKS, sizeof(char *));
clst->localBlockAddresses = (unsigned char**)calloc(NR_BLOCKS, sizeof(char *));
clst->blockData = (unsigned char*)malloc(NR_BLOCKS * net_config->blockSize);
for(i = 0; i < NR_BLOCKS; i++)
clst->blockAddresses[i] = clst->blockData + i * net_config->blockSize;
clst->localPos=0;
setNextBlock(clst);
return pthread_create(&client_config->thread, NULL, netReceiverMain, clst);
}
unsigned int pc_getSize(produconsum_t pc) {
return pc->size;
}
int writer(struct fifo *fifo, SBS outbs) {
//cerr << "start appending to outbs" << endl;
outbs->restart();
int fifoSize = pc_getSize(fifo->data);
while(1) {
int pos=pc_getConsumerPosition(fifo->data);
int bytes = pc_consumeContiguousMinAmount(fifo->data, BLOCKSIZE);
if (bytes == 0) {
//cerr << "done appending to outbs: " << outbs->length() << endl;
return 0;
}
/*
* If we have more than blocksize, round down to nearest blocksize
* multiple
*/
if(pos + bytes != fifoSize &&
bytes > (pos + bytes) % BLOCKSIZE)
bytes -= (pos + bytes) % BLOCKSIZE;
/* make sure we don't write to big a chunk... Better to
* liberate small chunks one by one rather than attempt to
* write out a bigger chunk and block reception for too
* long */
if (bytes > 128 * 1024)
bytes = 64 * 1024;
//bytes = write(outFile, fifo->dataBuffer + pos, bytes);
//cerr << "appending " << bytes << " bytes to outbs..." << endl;
outbs->append(fifo->dataBuffer + pos, bytes);
pc_consumed(fifo->data, bytes);
pc_produce(fifo->freeMemQueue, bytes);
}
}
}
namespace multicast
{
MulticastImpl::MulticastImpl(int min_receivers, const string& ifName, int portBase, int bufSize) :
fIfName(ifName),
fDb(0)
{
udpc_clearIp(&fNet_config.dataMcastAddr);
fNet_config.mcastRdv = 0;
fNet_config.blockSize = 1024; //1456;
fNet_config.sliceSize = 16;
fNet_config.portBase = portBase;
fNet_config.nrGovernors = 0;
fNet_config.flags = FLAG_NOKBD;
fNet_config.capabilities = 0;
fNet_config.min_slice_size = 16;
fNet_config.max_slice_size = 1024;
fNet_config.default_slice_size = 0;
fNet_config.ttl = 1;
fNet_config.rexmit_hello_interval = 2000;
fNet_config.autostart = 0;
fNet_config.requestedBufSize = bufSize;
fNet_config.min_receivers = min_receivers;
fNet_config.max_receivers_wait=0;
fNet_config.min_receivers_wait=0;
fNet_config.retriesUntilDrop = 200;
fNet_config.rehelloOffset = 50;
fStat_config.log = 0;
fStat_config.bwPeriod = 0;
fStat_config.printUncompressedPos = -1;
fStat_config.statPeriod = DEFLT_STAT_PERIOD;
fNet_config.net_if = 0;
//full-duplex
fNet_config.flags |= FLAG_SN;
if (fIfName.empty())
fIfName = "eth0";
fSock[0] = -1;
fSock[1] = -1;
fSock[2] = -1;
}
MulticastImpl::~MulticastImpl()
{
delete fDb;
for (int i=0; i<3; i++)
{
if (fSock[i] >= 0)
{
shutdown(fSock[i], SHUT_RDWR);
close(fSock[i]);
}
}
}
void MulticastImpl::startSender()
{
int tries;
time_t firstConnected = 0;
time_t *firstConnectedP;
//participantsDb_t db;
/* make the socket and print banner */
//int fSock[3];
int nr=0;
int fd;
int r;
int j;
fNet_config.net_if = udpc_getNetIf(fIfName.c_str());
fSock[nr++] = udpc_makeSocket(ADDR_TYPE_UCAST,
fNet_config.net_if,
NULL,
SENDER_PORT(fNet_config.portBase));
//cerr << "sock[" << (nr-1) << "] = " << fSock[(nr-1)] << endl;
if(! (fNet_config.flags & (FLAG_SN | FLAG_NOTSN)) ) {
if(udpc_isFullDuplex(fSock[0], fNet_config.net_if->name) == 1) {
fNet_config.flags |= FLAG_SN;
}
}
fd = udpc_makeSocket(ADDR_TYPE_BCAST,
fNet_config.net_if,
NULL,
SENDER_PORT(fNet_config.portBase));
if(fd >= 0)
fSock[nr++] = fd;
//cerr << "sock[" << (nr-1) << "] = " << fSock[(nr-1)] << endl;
if(fNet_config.requestedBufSize)
udpc_setSendBuf(fSock[0], fNet_config.requestedBufSize);
fNet_config.controlMcastAddr.sin_addr.s_addr =0;
if(fNet_config.ttl == 1 && fNet_config.mcastRdv == NULL) {
udpc_getBroadCastAddress(fNet_config.net_if,
&fNet_config.controlMcastAddr,
RECEIVER_PORT(fNet_config.portBase));
udpc_setSocketToBroadcast(fSock[0]);
}
if(fNet_config.controlMcastAddr.sin_addr.s_addr == 0) {
udpc_getMcastAllAddress(&fNet_config.controlMcastAddr,
fNet_config.mcastRdv,
RECEIVER_PORT(fNet_config.portBase));
/* Only do the following if controlMcastAddr is indeed an
mcast address ... */
if(isMcastAddress(&fNet_config.controlMcastAddr)) {
udpc_setMcastDestination(fSock[0], fNet_config.net_if,
&fNet_config.controlMcastAddr);
udpc_setTtl(fSock[0], fNet_config.ttl);
fSock[nr++] = udpc_makeSocket(ADDR_TYPE_MCAST,
fNet_config.net_if,
&fNet_config.controlMcastAddr,
SENDER_PORT(fNet_config.portBase));
//cerr << "sock[" << (nr-1) << "] = " << fSock[(nr-1)] << endl;
}
}
if(!(fNet_config.flags & FLAG_POINTOPOINT) &&
udpc_ipIsZero(&fNet_config.dataMcastAddr)) {
udpc_getDefaultMcastAddress(fNet_config.net_if,
&fNet_config.dataMcastAddr);
}
if(fNet_config.flags & FLAG_POINTOPOINT) {
udpc_clearIp(&fNet_config.dataMcastAddr);
}
setPort(&fNet_config.dataMcastAddr, RECEIVER_PORT(fNet_config.portBase));
fNet_config.capabilities = SENDER_CAPABILITIES;
if(fNet_config.flags & FLAG_ASYNC)
fNet_config.capabilities |= CAP_ASYNC;
udpc_sendHello(&fNet_config, fSock[0], 0);
fDb = udpc_makeParticipantsDb();
tries = 0;
if(fNet_config.min_receivers || fNet_config.min_receivers_wait ||
fNet_config.max_receivers_wait)
firstConnectedP = &firstConnected;
else
firstConnectedP = NULL;
while (!(r = mainDispatcher(fSock, nr, fDb, &fNet_config, &tries, firstConnectedP)))
;
for(j=1; j<nr; j++)
if(fSock[j] != fSock[0])
closesocket(fSock[j]);
if(r == 1) {
int i;
for(i=1; i<nr; i++)
udpc_closeSock(fSock, nr, i);
}
//doTransfer(fSock[0], fDb, &fNet_config, &fStat_config);
}
void MulticastImpl::doTransfer(const uint8_t* buf, uint32_t len)
{
int i;
int ret;
struct fifo fifo;
int isPtP = isPointToPoint(fDb, fNet_config.flags);
fNet_config.rcvbuf=0;
for(i=0; i<MAX_CLIENTS; i++)
if(udpc_isParticipantValid(fDb, i)) {
unsigned int pRcvBuf = udpc_getParticipantRcvBuf(fDb, i);
if(isPtP)
udpc_copyIpFrom(&fNet_config.dataMcastAddr,
udpc_getParticipantIp(fDb,i));
fNet_config.capabilities &=
udpc_getParticipantCapabilities(fDb, i);
if(pRcvBuf != 0 &&
(fNet_config.rcvbuf == 0 || fNet_config.rcvbuf > pRcvBuf))
fNet_config.rcvbuf = pRcvBuf;
}
if(isMcastAddress(&fNet_config.dataMcastAddr))
udpc_setMcastDestination(fSock[0], fNet_config.net_if,
&fNet_config.dataMcastAddr);
if(! (fNet_config.capabilities & CAP_BIG_ENDIAN))
{
//TODO: FIXME
//udpc_fatal(1, "Peer with incompatible endianness");
}
if(! (fNet_config.capabilities & CAP_NEW_GEN)) {
fNet_config.dataMcastAddr = fNet_config.controlMcastAddr;
fNet_config.flags &= ~(FLAG_SN | FLAG_ASYNC);
}
if(fNet_config.flags & FLAG_BCAST)
fNet_config.dataMcastAddr = fNet_config.controlMcastAddr;
udpc_initFifo(&fifo, fNet_config.blockSize);
ret = spawnNetSender(&fifo, fSock[0], &fNet_config, fDb);
localReader(&fifo, buf, len);
pthread_join(fifo.thread, NULL);
}
void MulticastImpl::startReceiver()
{
union serverControlMsg Msg;
int connectReqSent=0;
struct sockaddr_in myIp;
int haveServerAddress;
fClient_config.sender_is_newgen = 0;
fNet_config.net_if = udpc_getNetIf(fIfName.c_str());
{
fprintf(stderr, "net_if:\n\taddr = 0x%x\n\tbcast = 0x%x\n\tname = %s\n\tindex = %d\n", fNet_config.net_if->addr.s_addr, fNet_config.net_if->bcast.s_addr, fNet_config.net_if->name, fNet_config.net_if->index);
}
udpc_zeroSockArray(fClient_config.socks, NR_CLIENT_SOCKS);
fClient_config.S_UCAST = udpc_makeSocket(ADDR_TYPE_UCAST,
fNet_config.net_if,
0, RECEIVER_PORT(fNet_config.portBase));
//cerr << "S_UCAST = " << fClient_config.S_UCAST << endl;
fClient_config.S_BCAST = udpc_makeSocket(ADDR_TYPE_BCAST,
fNet_config.net_if,
0, RECEIVER_PORT(fNet_config.portBase));
//cerr << "S_BCAST = " << fClient_config.S_BCAST << endl;
if(fNet_config.ttl == 1 && fNet_config.mcastRdv == NULL) {
udpc_getBroadCastAddress(fNet_config.net_if,
&fNet_config.controlMcastAddr,
SENDER_PORT(fNet_config.portBase));
udpc_setSocketToBroadcast(fClient_config.S_UCAST);
} else {
udpc_getMcastAllAddress(&fNet_config.controlMcastAddr,
fNet_config.mcastRdv,
SENDER_PORT(fNet_config.portBase));
if(isMcastAddress(&fNet_config.controlMcastAddr)) {
udpc_setMcastDestination(fClient_config.S_UCAST, fNet_config.net_if,
&fNet_config.controlMcastAddr);
udpc_setTtl(fClient_config.S_UCAST, fNet_config.ttl);
fClient_config.S_MCAST_CTRL =
udpc_makeSocket(ADDR_TYPE_MCAST,
fNet_config.net_if,
&fNet_config.controlMcastAddr,
RECEIVER_PORT(fNet_config.portBase));
//cerr << "S_MCAST_CTRL = " << fClient_config.S_MCAST_CTRL << endl;
// TODO: subscribe address as receiver to!
}
}
udpc_clearIp(&fNet_config.dataMcastAddr);
connectReqSent = 0;
haveServerAddress = 0;
fClient_config.clientNumber= 0; /*default number for asynchronous transfer*/
while(1) {
// int len;
int msglen;
int sock;
if (!connectReqSent) {
if (sendConnectReq(&fClient_config, &fNet_config,
haveServerAddress) < 0) {
//TODO: FIXME
//perror("sendto to locate server");
}
connectReqSent = 1;
}
haveServerAddress=0;
//cerr << "waiting for msg..." << flush << endl;
sock = udpc_selectSock(fClient_config.socks, NR_CLIENT_SOCKS,
fNet_config.startTimeout);
//cerr << "got something" << endl;
if(sock < 0) {
//TODO: FIXME
}
// len = sizeof(server);
msglen=RECV(sock,
Msg, fClient_config.serverAddr, fNet_config.portBase);
if (msglen < 0) {
//TODO: FIXME
//perror("recvfrom to locate server");
//exit(1);
}
if(udpc_getPort(&fClient_config.serverAddr) !=
SENDER_PORT(fNet_config.portBase))
/* not from the right port */
continue;
switch(ntohs(Msg.opCode)) {
case CMD_CONNECT_REPLY:
//cerr << "got conrep" << endl;
fClient_config.clientNumber = ntohl(Msg.connectReply.clNr);
fNet_config.blockSize = ntohl(Msg.connectReply.blockSize);
if(ntohl(Msg.connectReply.capabilities) & CAP_NEW_GEN) {
fClient_config.sender_is_newgen = 1;
udpc_copyFromMessage(&fNet_config.dataMcastAddr,
Msg.connectReply.mcastAddr);
}
if (fClient_config.clientNumber == -1) {
//TODO: FIXME
//udpc_fatal(1, "Too many clients already connected\n");
}
goto break_loop;
case CMD_HELLO_STREAMING:
case CMD_HELLO_NEW:
case CMD_HELLO:
//cerr << "got hello" << endl;
connectReqSent = 0;
if(ntohs(Msg.opCode) == CMD_HELLO_STREAMING)
fNet_config.flags |= FLAG_STREAMING;
if(ntohl(Msg.hello.capabilities) & CAP_NEW_GEN) {
fClient_config.sender_is_newgen = 1;
udpc_copyFromMessage(&fNet_config.dataMcastAddr,
Msg.hello.mcastAddr);
fNet_config.blockSize = ntohs(Msg.hello.blockSize);
if(ntohl(Msg.hello.capabilities) & CAP_ASYNC)
fNet_config.flags |= FLAG_PASSIVE;
if(fNet_config.flags & FLAG_PASSIVE)
goto break_loop;
}
haveServerAddress=1;
continue;
case CMD_CONNECT_REQ:
case CMD_DATA:
case CMD_FEC:
continue;
default:
break;
}
//TODO: FIXME
//udpc_fatal(1, "Bad server reply %04x. Other transfer in progress?\n", (unsigned short) ntohs(Msg.opCode));
}
break_loop:
udpc_getMyAddress(fNet_config.net_if, &myIp);
if(!udpc_ipIsZero(&fNet_config.dataMcastAddr) &&
!udpc_ipIsEqual(&fNet_config.dataMcastAddr, &myIp) &&
(udpc_ipIsZero(&fNet_config.controlMcastAddr) ||
!udpc_ipIsEqual(&fNet_config.dataMcastAddr, &fNet_config.controlMcastAddr)
)) {
fClient_config.S_MCAST_DATA =
udpc_makeSocket(ADDR_TYPE_MCAST, fNet_config.net_if,
&fNet_config.dataMcastAddr,
RECEIVER_PORT(fNet_config.portBase));
//cerr << "S_MCAST_DATA = " << fClient_config.S_MCAST_DATA << endl;
}
if(fNet_config.requestedBufSize) {
int i;
for(i=0; i<NR_CLIENT_SOCKS; i++)
if(fClient_config.socks[i] != -1)
udpc_setRcvBuf(fClient_config.socks[i],fNet_config.requestedBufSize);
}
}
void MulticastImpl::receive(SBS outbs)
{
struct fifo fifo;
udpc_initFifo(&fifo, fNet_config.blockSize);
fifo.data = pc_makeProduconsum(fifo.dataBufSize, "receive");
fClient_config.isStarted = 0;
spawnNetReceiver(&fifo, &fClient_config, &fNet_config);
writer(&fifo, outbs);
pthread_join(fClient_config.thread, NULL);
}
}