Beispiel #1
0
 static void AsyncReturn(byte[] identity, Queue<byte[]> msgParts)
 {
     using (var skt = new Socket(SocketType.PUSH)) {
         skt.Connect("inproc://asyncReturn");
         skt.SendMore(identity);
         skt.SendMore(msgParts.Dequeue());
         skt.Send("Replying to \"" + ZHelpers.DecodeUUID(identity) + "\" with \"" +
             Encoding.Unicode.GetString(msgParts.Dequeue()) + "\"", Encoding.Unicode);
     }
 }
Beispiel #2
0
        public void Send(Socket socket)
        {
            for (int index = frames.Count - 1; index > 0; index--)
            {
                socket.SendMore(frames[index]);
            }

            socket.Send(frames[0]);
        }
Beispiel #3
0
        public static void Send(IDomainMapper domainMapper, Socket publisher, RuntimeTypeModel typeModel, MemoryStream stream, DomainBase instance)
        {
            var status = publisher.SendMore(domainMapper.Type2Bytes(instance.GetType()));
            if (status != SendStatus.Sent) throw new InvalidOperationException("Key not sent!");

            stream.SetLength(0);
            typeModel.Serialize(stream, instance);
            status = publisher.Send(stream.ToArray());
            if (status != SendStatus.Sent) throw new InvalidOperationException("Instance not sent!");
        }
Beispiel #4
0
        public SendStatus SendPacket(IPacket envelope)
        {
            var parts = new Queue <byte[]>(envelope.Serialize());

            // send Packet as multipart message
            while (parts.Count != 1)
            {
                _zmqSocket.SendMore(parts.Dequeue());
            }

            return(_zmqSocket.Send(parts.Dequeue()));
        }
Beispiel #5
0
        public void Send(Socket socket)
        {
            for (int index = frames.Count - 1; index > 0; index--)
            {
                socket.SendMore(frames[index]);
            }

            socket.Send(frames[0]);
        }
Beispiel #6
0
 public void Send(Socket publisher)
 {
     publisher.SendMore(Key, Encoding.Unicode);
     publisher.SendMore(BitConverter.GetBytes(Sequence));
     publisher.Send(Body, Encoding.Unicode);
 }
Beispiel #7
0
 public void Send(Socket socket)
 {
     for (int index = _msgParts.Count - 1; index > 0; index--) {
         socket.SendMore(_msgParts[index]);
     }
     socket.Send(_msgParts[0]);
 }
Beispiel #8
0
        /// <summary>
        /// Main thread to host the client process.
        /// </summary>
        /// <param name="threadContext">A ZMQ.Context instance.</param>
        public void Run(object threadContext)
        {
            try
            {
                Context ctx = (Context)threadContext;

                // This would be a using statement on data and ctl but we need to close and reopen sockets, which means
                // we have to Dispose() of a socket early and create a new instance. C#, wisely so, prevents you from
                // reassigning a using variable.
                Socket data = null, ctl = null, disk = null, diskACK = null;
                try
                {
                    data = ctx.Socket(SocketType.SUB);
                    ctl = ctx.Socket(SocketType.REQ);
                    disk = new Socket(SocketType.PUSH);
                    diskACK = new Socket(SocketType.PULL);

                    // Set the HWM for the disk PUSH so that the PUSH blocks if the PULL can't keep up writing:
                    if (diskHWM > 0) disk.SNDHWM = diskHWM;
                    //disk.SndBuf = 1048576 * diskHWM * 4;

                    // inproc:// binds are immediate.
                    disk.Bind("inproc://disk");
                    diskACK.Bind("inproc://diskack");

                    // Create the disk PULL thread:
                    Thread diskWriterThread = new Thread(new ParameterizedThreadStart(DiskWriterThread));
                    diskWriterThread.Start(ctx);

                    data.RCVHWM = networkHWM;

                    //data.RcvBuf = 1048576 * hwm * 4;
                    // NOTE: work-around for MS bug in WinXP's networking stack. See http://support.microsoft.com/kb/201213 for details.
                    data.RcvBuf = 0;

                    if (tsp == Transport.EPGM || tsp == Transport.PGM)
                    {
                        data.Rate = pgmRate;
                    }

                    string address = tsp.ToString().ToLower() + "://" + deviceData + ":" + portData;
                    Console.WriteLine("Connect(\"{0}\")", address);
                    data.Connect(address);
                    data.Subscribe(subscription, Encoding.Unicode);

                    // Connect to the control request socket:
                    ctl.RcvBuf = 1048576 * 4;
                    ctl.SndBuf = 1048576 * 4;
                    address = "tcp://" + deviceCtl + ":" + portCtl.ToString();
                    Console.WriteLine("Connect(\"{0}\")", address);
                    ctl.Connect(address);

                    Guid myIdentity = Guid.NewGuid();
                    ctl.Identity = new byte[1] { (byte)'@' }.Concat(myIdentity.ToByteArray()).ToArray();

                    Thread.Sleep(500);

                    // Begin client logic:

                    naks = null;
                    numBytes = 0;
                    int ackCount = -1;
                    byte[] nakBuf = null;

                    bool shuttingDown = false;

                    Stopwatch recvTimer = Stopwatch.StartNew();
                    long lastElapsedMilliseconds = recvTimer.ElapsedMilliseconds;
                    int msgsRecv = 0, msgsWritten = 0;

                    try
                    {
                        // Poll for incoming messages on the data SUB socket:
                        PollItem[] pollItems = new PollItem[3];
                        pollItems[0] = data.CreatePollItem(IOMultiPlex.POLLIN);
                        pollItems[1] = ctl.CreatePollItem(IOMultiPlex.POLLIN /* | IOMultiPlex.POLLOUT */);
                        pollItems[2] = diskACK.CreatePollItem(IOMultiPlex.POLLIN);

                        ZMQStateMasheen<DataSUBState> dataFSM;
                        ZMQStateMasheen<ControlREQState> controlFSM;
                        Queue<QueuedControlMessage> controlStateQueue = new Queue<QueuedControlMessage>();

                        DateTimeOffset lastSentNAKs = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromSeconds(60));

                        // We create a state machine to handle our send/recv state:
                        dataFSM = new ZMQStateMasheen<DataSUBState>(
                            // Set the initial state:
                            DataSUBState.Recv,
                            // Initial state handler:
                            new ZMQStateMasheen<DataSUBState>.State(DataSUBState.Recv, (sock, revents) =>
                            {
                                Queue<byte[]> packet = sock.RecvAll();

                                if (packet.Count == 0) { trace("FAIL!"); return DataSUBState.Recv; }
                                string sub = Encoding.Unicode.GetString(packet.Dequeue());
                                if (packet.Count == 0) { trace("FAIL!"); return DataSUBState.Recv; }
                                string cmd = Encoding.Unicode.GetString(packet.Dequeue());

                                if ((naks != null) && (cmd == "DATA"))
                                {
                                    if (packet.Count == 0) { trace("FAIL!"); return DataSUBState.Recv; }
                                    int chunkIdx = BitConverter.ToInt32(packet.Dequeue(), 0);

                                    // Count the number of messages received from the network:
                                    ++msgsRecv;
                                    if (ChunkReceived != null) ChunkReceived(this, chunkIdx);

                                    // Already received this chunk?
                                    if (!naks[chunkIdx])
                                    {
                                        trace("ALREADY RECV {0}", chunkIdx);
                                        return DataSUBState.Recv;
                                    }

                                    trace("RECV {0}", chunkIdx);

                                    // Queue up the disk writes with PUSH/PULL and an HWM on a separate thread to maintain as
                                    // constant disk write throughput as we can get... The HWM will enforce the PUSHer to block
                                    // when the PULLer cannot receive yet.
                                    if (packet.Count == 0) return DataSUBState.Recv;
                                    byte[] chunk = packet.Dequeue();
                                    disk.SendMore(cmdWrite);
                                    disk.SendMore(BitConverter.GetBytes(chunkIdx));
                                    // This will block if the disk queue gets full:
                                    disk.Send(chunk);
                                    chunk = null;

                                    return DataSUBState.Recv;
                                }
                                else if (cmd == "PING")
                                {
                                    if (shuttingDown) return DataSUBState.Recv;

                                    controlStateQueue.Enqueue(new QueuedControlMessage(ControlREQState.SendALIVE, null));
                                    return DataSUBState.Recv;
                                }
                                else
                                {
                                    return DataSUBState.Recv;
                                }
                            })
                        );

                        // We create a state machine to handle our send/recv state:
                        object tmpControlState = null;
                        controlFSM = new ZMQStateMasheen<ControlREQState>(
                            ControlREQState.Nothing,
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.Nothing, (sock, revents) =>
                            {
                                // Do nothing state.
                                return ControlREQState.Nothing;
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.SendALIVE, (sock, revents) =>
                            {
                                if (shuttingDown) return ControlREQState.Nothing;

                                ctl.SendMore(ctl.Identity);
                                ctl.Send("ALIVE", Encoding.Unicode);

                                return new ZMQStateMasheen<ControlREQState>.MoveOperation(ControlREQState.RecvALIVE, false);
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.RecvALIVE, (sock, revents) =>
                            {
                                Queue<byte[]> packet = ctl.RecvAll();

                                if (packet.Count == 0) return ControlREQState.Nothing;
                                string cmd = Encoding.Unicode.GetString(packet.Dequeue());
                                //trace("Server: '{0}'", cmd);

                                if (cmd == "") return ControlREQState.Nothing;
                                else if (cmd == "WHOAREYOU") return new ZMQStateMasheen<ControlREQState>.MoveOperation(ControlREQState.SendJOIN, true);

                                return ControlREQState.Nothing;
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.SendJOIN, (sock, revents) =>
                            {
                                if (shuttingDown) return ControlREQState.Nothing;

                                // Send a JOIN request:
                                ctl.SendMore(ctl.Identity);
                                ctl.Send("JOIN", Encoding.Unicode);

                                return ControlREQState.RecvJOIN;
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.RecvJOIN, (sock, revents) =>
                            {
                                Queue<byte[]> packet = ctl.RecvAll();
                                if (packet.Count == 0) return ControlREQState.Nothing;

                                string resp = Encoding.Unicode.GetString(packet.Dequeue());
                                if (resp != "JOINED")
                                {
                                    // TODO: handle this failure.
                                    trace("FAIL!"); return ControlREQState.Nothing;
                                }

                                // TODO: make this atomically succeed or fail without corrupting state
                                if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                numChunks = BitConverter.ToInt32(packet.Dequeue(), 0);
                                numBytes = ((numChunks + 7) & ~7) >> 3;
                                nakBuf = new byte[numBytes];
                                if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                chunkSize = BitConverter.ToInt32(packet.Dequeue(), 0);
                                if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                int numFiles = BitConverter.ToInt32(packet.Dequeue(), 0);

                                List<TarballEntry> tbes = new List<TarballEntry>(numFiles);
                                for (int i = 0; i < numFiles; ++i)
                                {
                                    if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                    string fiName = Encoding.Unicode.GetString(packet.Dequeue());
                                    if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                    long length = BitConverter.ToInt64(packet.Dequeue(), 0);
                                    if (packet.Count == 0) { trace("FAIL!"); return ControlREQState.Nothing; }
                                    byte[] hash = packet.Dequeue();

                                    TarballEntry tbe = new TarballEntry(fiName, length, hash);
                                    tbes.Add(tbe);
                                }

                                lock (tarballLock)
                                {
                                    if (tarball != null)
                                    {
                                        tarball.Close();
                                        tarball.Dispose();
                                        tarball = null;
                                    }
                                }

                                lock (tarballLock)
                                {
                                    // Create the tarball reader that writes the files locally:
                                    tarball = new TarballStreamReader(downloadDirectory, tbes);

                                    // Get our local download state:
                                    naks = getClientState(this, tarball);
                                }
                                ackCount = naks.Cast<bool>().Take(numChunks).Count(b => !b);

                                return new ZMQStateMasheen<ControlREQState>.MoveOperation(ControlREQState.SendNAKS, true);
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.SendNAKS, (sock, revents) =>
                            {
                                if (shuttingDown) return ControlREQState.Nothing;

                                if (nakBuf == null) return ControlREQState.Nothing;

                                // Send our NAKs:
                                ctl.SendMore(ctl.Identity);
                                ctl.SendMore("NAKS", Encoding.Unicode);
                                // TODO: RLE!
                                naks.CopyTo(nakBuf, 0);
                                trace("SEND NAK");
                                ctl.Send(nakBuf);

                                return ControlREQState.RecvNAKS;
                            }),
                            new ZMQStateMasheen<ControlREQState>.State(ControlREQState.RecvNAKS, (sock, revents) =>
                            {
                                Queue<byte[]> packet = ctl.RecvAll();
                                // Don't care what the response is for now.

                                return ControlREQState.Nothing;
                            })
                        );

                        // Disk-write acknowledgement poll input handler:
                        pollItems[2].PollInHandler += new PollHandler((Socket sock, IOMultiPlex revents) =>
                        {
                            byte[] idxPkt = sock.RecvAll().Dequeue();

                            int chunkIdx = BitConverter.ToInt32(idxPkt, 0);
                            naks[chunkIdx] = false;

                            // Count the number of messages written to disk:
                            ++msgsWritten;
                            ++ackCount;

                            // Profiled - terrible performance. Not a big surprise here.
                            //ackCount = naks.Cast<bool>().Take(numChunks).Count(b => !b);

                            // If we ran up the timer, send more ACKs:
                            if (DateTimeOffset.UtcNow.Subtract(lastSentNAKs).TotalMilliseconds >= 250d)
                            {
                                lastSentNAKs = DateTimeOffset.UtcNow;
                                controlStateQueue.Enqueue(new QueuedControlMessage(ControlREQState.SendNAKS, null));
                            }
                        });

                        pollItems[0].PollInHandler += new PollHandler(dataFSM.StateMasheen);
                        pollItems[1].PollInHandler += new PollHandler(controlFSM.StateMasheen);

                        DateTimeOffset lastRecv = DateTimeOffset.UtcNow;

                        // Start the control state machine with a JOIN:
                        controlStateQueue.Enqueue(new QueuedControlMessage(ControlREQState.SendJOIN, null));

                        // Create a socket poller for the data socket:
                        while (true)
                        {
                            // If we disposed of the previous control socket, create a new one:
                            if (ctl == null)
                            {
                                trace("Creating new CONTROL socket");
                                // Set up new socket:
                                ctl = ctx.Socket(SocketType.REQ);
                                // Connect to the control request socket:
                                ctl.Connect("tcp://" + deviceCtl + ":" + portCtl.ToString());
                                ctl.Identity = new byte[1] { (byte)'@' }.Concat(myIdentity.ToByteArray()).ToArray();
                            }

                            if (!shuttingDown && IsSendState(controlFSM.CurrentState))
                            {
                                // If we ran up the timer, send more NAKs:
                                if (DateTimeOffset.UtcNow.Subtract(lastSentNAKs).TotalMilliseconds >= 200d)
                                {
                                    lastSentNAKs = DateTimeOffset.UtcNow;
                                    controlStateQueue.Enqueue(new QueuedControlMessage(ControlREQState.SendNAKS, null));
                                }

                                if (controlStateQueue.Count > 0)
                                {
                                    var msg = controlStateQueue.Dequeue();

                                    // Roll up similar state requests:
                                    int dupes = 0;
                                    while (controlStateQueue.Count > 0)
                                    {
                                        if (controlStateQueue.Peek().NewState != msg.NewState) break;

                                        msg = controlStateQueue.Dequeue();
                                        ++dupes;
                                    }

                                    if (dupes > 0)
                                    {
                                        trace("Removed {0} redundant queued states", dupes);
                                    }

                                    tmpControlState = msg.Object;

                                    // Run the control state machine to SEND:
                                    controlFSM.CurrentState = msg.NewState;
                                    controlFSM.StateMasheen(ctl, IOMultiPlex.POLLOUT);
                                }
                            }

                            // Measure our message send rate per minute:
                            long elapsed = recvTimer.ElapsedMilliseconds - lastElapsedMilliseconds;
                            if (elapsed >= 100L)
                            {
                                NetworkRecvChunksPerMinute = (int)((msgsRecv * 60000L) / elapsed);
                                DiskWriteChunksPerMinute = (int)((msgsWritten * 60000L) / elapsed);
                                lastElapsedMilliseconds = recvTimer.ElapsedMilliseconds;
                                msgsRecv = 0;
                                msgsWritten = 0;
                            }

                            if (!recvTimer.IsRunning)
                            {
                                recvTimer.Reset();
                                recvTimer.Start();
                                lastElapsedMilliseconds = 0L;
                            }

                            if (ackCount >= numChunks)
                            {
                                disk.Send(cmdExit);
                                break;
                            }

                            if (ctx.Poll(pollItems, 100L) == 0)
                                Thread.Sleep(1);
                        }

                        // Wait for the disk writer to finish up:
                        shuttingDown = true;
                        diskWriterThread.Join();

                        // If we were last to await some response, receive it:
                        if (controlFSM.CurrentState == ControlREQState.RecvALIVE || controlFSM.CurrentState == ControlREQState.RecvJOIN || controlFSM.CurrentState == ControlREQState.RecvNAKS)
                        {
                            PollItem[] recvPoll = new PollItem[1];
                            recvPoll[0] = ctl.CreatePollItem(IOMultiPlex.POLLIN);
                            recvPoll[0].PollInHandler += new PollHandler(controlFSM.StateMasheen);

                            // Wait a bit to receive the response:
                            for (int i = 0; (i < 20) && ((ctx.Poll(recvPoll, 100) == 1) && (ctl != null)); ++i)
                            {
                            }
                        }

                        // Send the LEAVE message:
                        ctl.SendMore(ctl.Identity);
                        ctl.Send("LEAVE", Encoding.Unicode);
                        Completed = true;
                    }
                    finally
                    {
                        if (tarball != null) tarball.Dispose();
                    }
                }
                finally
                {
                    if (data != null) data.Dispose();
                    if (ctl != null) ctl.Dispose();
                }
            }
            catch (System.Exception ex)
            {
                Console.Error.WriteLine(ex.ToString());
            }
        }
 private static void Send(Socket socket, IEnumerable<string> lines)
 {
     foreach (var line in lines.Take(lines.Count()-1)) socket.SendMore(line, Encoding.UTF8);
     socket.Send(lines.Last(), Encoding.UTF8);
 }