// Receive collects packets. When exactly enough packets have been recovered to // reconstruct the message, the message is returned as a byte slice. Otherwise // nil is returned. Receive can continue to collect packets after the message // has been constructed for reliability statistics. func (p Packeter) Receive(b []byte) []byte { var pk Packet if err.Log(proto.Unmarshal(b, &pk)) { return nil } mid, pid, dataShards, parityShards := pk.MetaData() clctr, ok := p[mid] if !ok { clctr = &collector{ data: make([][]byte, dataShards+parityShards), collected: make(map[uint32]bool), complete: false, } p[mid] = clctr } clctr.collected[pid] = true if clctr.complete { return nil } clctr.data[pid] = pk.Data if uint32(len(clctr.collected)) >= dataShards { clctr.complete = true if enc, e := reedsolomon.New(int(dataShards), int(parityShards)); err.Warn(e) { if err.Log(enc.Reconstruct(clctr.data)) { //maybe should take out as an arg ln := int(clctr.data[0][0]) + int(clctr.data[0][1])<<8 + int(clctr.data[0][2])<<16 + int(clctr.data[0][2])<<24 var out bytes.Buffer err.Warn(enc.Join(&out, clctr.data, ln)) clctr.data = nil return out.Bytes()[4:] } } } return nil }
// Make takes a message as a byte slice, along with the expected loss rate and // target reliability and produces the packets for that message. The packets are // returned as a slice of byte-slices. func (p *Packeter) Make(msg []byte, loss, reliability float64) [][]byte { l := uint32(len(msg) + 4) msk := uint32(255) lb := []byte{ byte(l & msk), byte((l >> 8) & msk), byte((l >> 16) & msk), byte((l >> 24) & msk), } msg = append(lb, msg...) dataShards, parityShards := findRedundancy(len(msg), Packetlength, loss, reliability) if parityShards < 1 { // reedsolomon.Encoder cannot have 0 parity shards // at some point I want to change this so it doesn't use reedsolomon in this // case parityShards = 1 } shards := dataShards + parityShards var data [][]byte if enc, e := reedsolomon.New(dataShards, parityShards); err.Warn(e) { if data, e = enc.Split(msg); err.Warn(e) { err.Warn(enc.Encode(data)) } } else { return nil } idArr := make([]byte, 4) rand.Read(idArr) id := uint32(idArr[0]) + uint32(idArr[1])<<8 + uint32(idArr[2])<<16 + uint32(idArr[3])<<24 pk := Packet{} pks := make([][]byte, shards) pk.MessageId = id pk.ParityShards = uint32(parityShards) for i := 0; i < shards; i++ { pk.PacketId = uint32(i<<16) + uint32(shards) pk.Data = data[i] if d, e := proto.Marshal(&pk); err.Log(e) { pks[i] = d } else { return nil } } return pks }
// Run is the servers listen loop. When it receives a message it will pass that // message into the packetHandler func (s *UDPServer) Run() { if s.running || s.conn == nil { return } s.running = true buf := make([]byte, MaxUdpPacketLength) for { l, addr, e := s.conn.ReadFromUDP(buf) if s.stop { break } if err.Log(e) { packet := make([]byte, l) copy(packet, buf[:l]) go s.packetHandler.Receive(packet, addr) } } s.running = false }