NetMQ Unity Pub/Sub - c#

I'm running into some issues trying to get NetMQ to work. My code compiles fine but I'm not achieving my desired outcome. Testing both Pub and Sub side from one PC.
I have a UI Handler that is linked to four buttons, start/stop pub/sub.
Would love if someone could shed some light into the issue.
Desired outcome: To be able to transfer test.wav from "Sending Folder" to "Receiving Folder"
Publisher Logic
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using System.IO;
public class Publisher
{
private readonly Thread _publisherThread;
private readonly Thread _subscriberThread;
public Publisher()
{
_publisherThread = new Thread(PublisherWork);
_publisherThread.Start();
_subscriberThread = new Thread(SubscriberWork);
_subscriberThread.Start();
}
public void Start()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557");
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav";
byte[] fileBytes = File.ReadAllBytes(filePath);
pubSocket.SendMoreFrame("File").SendFrame(fileBytes);
}
}
private void PublisherWork()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557"); // [C] Port
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav"; // [C] Filepath
byte[] fileBytes = File.ReadAllBytes(filePath);
while (true)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes); // [C] Topic
Thread.Sleep(1000);
}
}
}
private void SubscriberWork()
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557"); // [C] Port
subSocket.Subscribe("File"); // [C] Topic
while (true)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
}
private void SaveWavFile(byte[] fileBytes)
{
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav";
File.WriteAllBytes(filePath, fileBytes);
}
}
Subscriber Logic
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class Subscriber : MonoBehaviour
{
public Button startButton;
public Button stopButton;
private Thread _publisherThread;
private Thread _subscriberThread;
private bool _isRunning;
private void Start()
{
startButton.onClick.AddListener(StartThreads);
stopButton.onClick.AddListener(StopThreads);
}
private void StartThreads()
{
_isRunning = true;
_publisherThread = new Thread(PublisherWork);
_publisherThread.Start();
_subscriberThread = new Thread(SubscriberWork);
_subscriberThread.Start();
}
private void StopThreads()
{
_isRunning = false;
_publisherThread.Join();
_subscriberThread.Join();
}
private void PublisherWork()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557"); // [C] Port
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav"; // [C] Filepath
byte[] fileBytes = File.ReadAllBytes(filePath);
while (_isRunning)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes); // [C] Topic
Thread.Sleep(1000);
}
}
NetMQConfig.Cleanup();
}
private void SubscriberWork()
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557"); // [C] Port
subSocket.Subscribe("File"); // [C] Topic
while (_isRunning)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
NetMQConfig.Cleanup();
}
private void SaveWavFile(byte[] fileBytes)
{
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav"; // [C]
File.WriteAllBytes(filePath, fileBytes);
}
}
UI Handler
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class UIHandler : MonoBehaviour
{
public Button startSubscriberButton;
public Button stopSubscriberButton;
public Button startPublisherButton;
public Button stopPublisherButton;
public Text pubStartStatus;
public Text pubStopStatus;
public Text subStartStatus;
public Text subStopStatus;
private Publisher _publisher;
private readonly Thread _publisherThread;
private readonly Thread _subscriberThread;
private bool _publisherRunning = false;
private bool _subscriberRunning = false;
public UIHandler()
{
// Initializing the threads
_publisherThread = new Thread(PublisherWork);
_subscriberThread = new Thread(SubscriberWork);
}
private void Start()
{
_publisher = new Publisher();
startPublisherButton.onClick.AddListener(StartPublisher);
stopPublisherButton.onClick.AddListener(StopPublisher);
startSubscriberButton.onClick.AddListener(StartSubscriber);
stopSubscriberButton.onClick.AddListener(StopSubscriber);
}
public void StartPublisher()
{
// Starting the publisher thread
_publisherThread.Start();
_publisherRunning = true;
pubStartStatus.text = "pubStartStatus: Started";
}
public void StopPublisher()
{
// Stopping the publisher thread
_publisherRunning = false;
_publisherThread.Join();
pubStopStatus.text = "pubStartStatus: Stopped";
}
public void StartSubscriber()
{
// Starting the subscriber thread
_subscriberThread.Start();
_subscriberRunning = true;
subStartStatus.text = "subStartStatus: Stopped";
}
public void StopSubscriber()
{
// Stopping the subscriber thread
_subscriberRunning = false;
_subscriberThread.Join();
subStopStatus.text = "subStartStatus: Stopped";
}
private void PublisherWork()
{
// Creating a publisher socket and binding it to the specified address
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557");
// Reading the file bytes from the specified location
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav";
byte[] fileBytes = File.ReadAllBytes(filePath);
// Publishing the file bytes continuously
while (_publisherRunning)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes);
Thread.Sleep(1000);
}
}
NetMQConfig.Cleanup();
}
private void SubscriberWork()
{
// Creating a subscriber socket and connecting it to the specified address
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557");
subSocket.Subscribe("File");
// Receiving and saving the file bytes continuously
while (_subscriberRunning)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
NetMQConfig.Cleanup();
}
private void SaveWavFile(byte[] fileBytes)
{
// Saving the received file bytes to the specified location
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav";
File.WriteAllBytes(filePath, fileBytes);
}
}

Related

How to send a picture to the mail using mime and ssl

I'm trying to send a letter with a picture to the mail, my code works with html files, pictures are sent, but when opened, it gives an error. I'm doing my lab work and on assignment I can't use MailMessage for example. I set the sending address from my windows forms.
using System;
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
namespace NetworksNeLaba2
{
public class Program
{
private const string AddressFrom = "MYGM";
private const string PasswordFrom = "MYPS";
private static readonly Emailbox _emailbox = new Emailbox(AddressFrom, PasswordFrom);
private const string HostName = "smtp.gmail.com";
private const int Port = 465;
private static readonly ConnectionInstaller _connectionInstaller = new ConnectionInstaller(HostName, Port);
public static void Main(string[] args)
{
}
public void InitializeComponents(string address, string subject)
{
var direction = new MessageDirection(_emailbox.Address, address);
var commands = new []
{
$"EHLO {HostName}",
"AUTH LOGIN",
_emailbox.Address,
_emailbox.Password,
$"MAIL FROM:<{direction.From}>",
$"RCPT TO:<{direction.To}>",
"DATA"
};
var fileName = "cat.jpeg";
var ct = new ContentType();
ct.MediaType = MediaTypeNames.Image.Jpeg;
var data = new Attachment(fileName, ct.MediaType);
var s = data.ContentStream;
var reader = new StreamReader(s);
var letterContent = new []
{
$"Subject:{subject}\r\n",
$"From:Roma {AddressFrom}\r\n",
$"To:{address}\r\n",
$"Content-Type:{data.ContentType}\r\n",
$"Content-Disposition:{data.ContentDisposition}\r\n",
$"Content:{reader.ReadToEnd()}\r\n",
//$"Date:{DateTime.Now}\r\n",
".\r\n"
};
var letter = String.Concat(letterContent);
_connectionInstaller.SendMessage(commands, letter);
}
}
}
The first part of the code is what commands and the content of the letter I set, below I attached the code that processes the commands.
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
namespace NetworksNeLaba2
{
public class ConnectionInstaller
{
private readonly TcpClient _tcpClient = new TcpClient();
private readonly SslStream _sslStream;
private readonly byte[] _buffer = new byte[512];
public ConnectionInstaller(string hostName, int port)
{
_tcpClient.Connect(hostName, port);
_sslStream = new SslStream(_tcpClient.GetStream());
_sslStream.AuthenticateAsClient(hostName);
}
public void SendMessage(string[] commands,string letterContent)
{
foreach (var command in commands)
{
WriteBuffer(command + "\r\n");
}
WriteBuffer(letterContent);
}
private void WriteBuffer(string str)
{
_sslStream.Write(Encoding.ASCII.GetBytes(str));
_sslStream.Read(_buffer, 0, _buffer.Length);
Console.WriteLine(Encoding.ASCII.GetString(_buffer));
Array.Clear(_buffer, 0, _buffer.Length);
}
}
}

How to stream web cam per ASP.Net Core WebApi

I try to create an endpoint which delivers a live stream of my webcam. I´m using AForge to access the camera, but I can´t manage to create and return a stream that actually renders in a video tag in the html client.
Additionally: The MemoryStream I'm using growes every second. And that is definitly not what I want.
What I tried so far:
Cam.cs:
public class Cam
{
MemoryStream stream = new MemoryStream();
public Cam()
{
FilterInfoCollection videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
VideoCaptureDevice finalVideo = new VideoCaptureDevice(videoCaptureDevices[0].MonikerString);
finalVideo.NewFrame += this._streamNewFrame;
finalVideo.Start();
}
private void _streamNewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
System.Drawing.Image imgforms = (Bitmap)eventArgs.Frame.Clone();
imgforms.Save(this.stream, ImageFormat.Bmp);
this.stream.Seek(0, SeekOrigin.Begin);
}
public Stream GetStream()
{
MemoryStream stream = new MemoryStream();
this.stream.CopyTo(stream);
return stream;
}
}
CamController.cs:
[ApiController]
[Route("[controller]")]
public class CamController : ControllerBase
{
private readonly Cam cam;
public CamController(Cam cam)
{
this.cam = cam;
}
[HttpGet]
public IActionResult Get()
{
var contentType = "multipart/x-mixed-replace;boundary=myboundary";
Stream stream = this.cam.GetStream();
var result = new FileStreamResult(stream, contentType)
{
EnableRangeProcessing = true,
};
return result;
}
}
Update 1:
I made progress. I managed to create a valid MJPEP stream (it is pretty easy if know how it has to look like). Look at that:
CamController.cs:
using Microsoft.AspNetCore.Mvc;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace demo.Controllers
{
[ApiController]
[Route("[controller]")]
public class CamController : ControllerBase
{
private readonly Cam cam;
public CamController(Cam cam)
{
this.cam = cam;
}
[HttpGet]
[Route("cam/video")]
public async Task Get()
{
Response.ContentType = "video/webm";
// How to go on here?
}
[HttpGet]
[Route("cam/mjepg")]
public async Task Get2()
{
Response.StatusCode = 206;
Response.ContentType = "multipart/x-mixed-replace; boundary=frame";
Response.Headers.Add("Connection", "Keep-Alive");
StreamingSession session = this.cam.StreamOn(data =>
{
if (Request.HttpContext.RequestAborted.IsCancellationRequested)
{
throw new Exception();
}
Response.Body.Write(this.CreateHeader(data.Length));
Response.Body.Write(data);
Response.Body.Write(this.CreateFooter());
Response.Body.Flush();
});
await Response.StartAsync();
await session.WaitAsync();
}
/// <summary>
/// Create an appropriate header.
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
private byte[] CreateHeader(int length)
{
string header =
"--frame" + "\r\n" +
"Content-Type:image/jpeg\r\n" +
"Content-Length:" + length + "\r\n\r\n";
return Encoding.ASCII.GetBytes(header);
}
private byte[] CreateFooter()
{
return Encoding.ASCII.GetBytes("\r\n");
}
}
}
Cam.cs:
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace demo
{
public class Cam
{
object locker = new object();
bool signaledToStop = false;
List<StreamingSession> sessions = new List<StreamingSession>();
VideoCaptureDevice finalVideo;
public Cam()
{
FilterInfoCollection videoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
finalVideo = new VideoCaptureDevice(videoCaptureDevices[0].MonikerString);
finalVideo.VideoResolution = finalVideo.VideoCapabilities
.OrderByDescending(x => x.MaximumFrameRate)
.ThenByDescending(x=>x.FrameSize.Width)
.FirstOrDefault();
finalVideo.NewFrame += this._streamNewFrame;
}
private void _streamNewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
{
System.Drawing.Image imgforms = (Bitmap)eventArgs.Frame.Clone();
byte[] data = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
imgforms.Save(stream, ImageFormat.Jpeg);
data = stream.ToArray();
}
lock (this.locker)
{
foreach (var session in sessions.ToList())
{
session.ProvideData(data);
}
}
}
public StreamingSession StreamOn(Action<byte[]> callback)
{
StreamingSession session = new StreamingSession(callback);
lock (this.locker)
{
this.sessions.Add(session);
if (this.signaledToStop)
{
this.finalVideo.WaitForStop();
}
if (!this.finalVideo.IsRunning)
{
this.finalVideo.Start();
this.signaledToStop = false;
}
}
session.OnSessionEnded += Session_OnSessionEnded;
return session;
}
private void Session_OnSessionEnded(object sender, EventArgs e)
{
lock(this.locker)
{
this.sessions.Remove(sender as StreamingSession);
if (!this.sessions.Any())
{
this.finalVideo.SignalToStop();
this.signaledToStop = true;
}
}
}
}
public class StreamingSession
{
public StreamingSession(Action<byte[]> Callback)
{
this.Callback = Callback;
}
private Action<byte[]> Callback;
private TaskCompletionSource Completion = new TaskCompletionSource();
public event EventHandler OnSessionEnded;
public Task WaitAsync(int? timeout = null)
{
if (timeout.HasValue)
{
return Task.WhenAny(Task.Delay(timeout.Value), this.Completion.Task);
}
return this.Completion.Task;
}
public void ProvideData(byte[] data)
{
try
{
this.Callback(data);
}
catch(Exception)
{
this.EndSession();
}
}
public void EndSession()
{
this.Completion.SetResult();
if (this.OnSessionEnded != null)
{
this.OnSessionEnded(this, null);
}
}
}
}
But there is a problem left: The "video" (more like moving images) only renders in an img tag. But it shall render in a video tag. I read that video tag doesn´t support mjpeg - but how can I encode the stream in a format the video tag understands?
I uploaded the complete code here: https://github.com/ChristophWieske/asp-net-core-live-stream-source
You may want to consider WebRTC.
WebRTC is a relatively new browser based technology that allows a server to send streams to a JavaScript client.
See the WebRTC Samples for client side code: https://github.com/webrtc/samples
Server side code is a bit more complex. Here's an overly complicated example that uses "Mixed Reality." It's a bit overboard, but should point you in the right direction.
https://microsoft.github.io/MixedReality-WebRTC/manual/cs/helloworld-cs-connection-core3.html

How to edit my code to run in console rather than GUI

I was following a tutorial to connect my bluetooth module to pc and read data from it, but in the tutorial he designed the program to run as a GUI, now I'm trying to edit it to run in console, which I will merge the new code in unity engine to move my game by the input data.
The GUI
used and the code,
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using InTheHand;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System.IO;
namespace Bluetooth
{
public partial class Form1 : Form
{
List<string> items;
public Form1()
{
items = new List<string>();
InitializeComponent();
}
private void bGo_Click(object sender, EventArgs e)
{
if(serverStarted)
{
updateUI("Server already started!");
return;
}
if(rbClient.Checked)
{
connectAsClient();
}
else
{
connectAsServer();
}
}
private void connectAsServer()
{
Thread bluetoothServerThread = new Thread(new ThreadStart(serverConnectThread));
bluetoothServerThread.Start();
}
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
bool serverStarted = false;
public void serverConnectThread()
{
serverStarted = true;
updateUI("Server started, waiting for clients");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
BluetoothClient conn = blueListener.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mstream = conn.GetStream();
while (true)
{
try
{
//handle server connection
byte[] received = new byte[1024];
mstream.Read(received, 0, received.Length);
updateUI("Received: " + Encoding.ASCII.GetString(received));
byte[] sent = Encoding.ASCII.GetBytes("Hello world\n");
mstream.Write(sent, 0, sent.Length);
}
catch (IOException exception)
{
updateUI("Client has disconnected!!");
break;
}
}
}
private void connectAsClient()
{
startScan();
}
private void startScan()
{
listBox1.DataSource = null;
listBox1.Items.Clear();
items.Clear();
Thread bluetoothScanThread = new Thread(new ThreadStart(scan));
bluetoothScanThread.Start();
}
BluetoothDeviceInfo[] devices;
private void scan()
{
updateUI("Start scanning...");
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
updateUI(devices.Length.ToString() + " devices discovered");
updateUI("Scan complete");
foreach(BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
}
updateDeviceList();
}
private void updateUI(string message)
{
Func<int> del = delegate ()
{
tbOutput.AppendText(message + System.Environment.NewLine);
return 0;
};
Invoke(del);
}
private void updateDeviceList()
{
Func<int> del = delegate ()
{
listBox1.DataSource = items;
return 0;
};
Invoke(del);
}
BluetoothDeviceInfo DeviceInfo;
private void listBox1_DoubleClick(object sender, EventArgs e)
{
DeviceInfo = devices.ElementAt(listBox1.SelectedIndex);
updateUI(DeviceInfo.DeviceName + " was Selected, attempting connect");
if(PairDevice())
{
updateUI("Device paired");
updateUI("starting connect thread");
Thread bluetoothClientThread = new Thread(new ThreadStart(ClientConnectThread));
bluetoothClientThread.Start();
}
else
{
updateUI("pair failed");
}
}
private void ClientConnectThread()
{
BluetoothClient client = new BluetoothClient();
updateUI("Attempting connect");
client.BeginConnect(DeviceInfo.DeviceAddress, mUUID, this.BluetoothClientConnectCallback, client);
}
void BluetoothClientConnectCallback(IAsyncResult result)
{
BluetoothClient client = (BluetoothClient)result.AsyncState;
client.EndConnect(result);
Stream stream = client.GetStream();
stream.ReadTimeout = 10000;
while(true)
{
try
{
byte[] received = new byte[1024];
stream.Read(received, 0, received.Length);
updateUI(Encoding.ASCII.GetString(received));
}
catch (IOException exception)
{
updateUI("Client has disconnected!!");
break;
}
}
}
string mypin = "1234";
private bool PairDevice()
{
if(!DeviceInfo.Authenticated)
{
if (!BluetoothSecurity.PairRequest(DeviceInfo.DeviceAddress, mypin)) ;
{
return false;
}
}
return true;
}
bool ready = false;
byte[] message;
private void tbText_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == 13)
{
byte[] b = Encoding.ASCII.GetBytes(tbText.Text);
ready = true;
tbText.Clear();
}
}
}
}
I tried to edit my code but I don't know why scan method can't be called.
class Program
{
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
List<string> items = new List<string>();
BluetoothDeviceInfo[] devices;
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Thread bluetoothScanThread = new Thread(new ThreadStart(scan));
bluetoothScanThread.Start();
}
public void scan()
{
Console.WriteLine("Start scanning...");
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
Console.WriteLine(devices.Length.ToString() + " devices discovered");
Console.WriteLine("Scan complete");
foreach (BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
}
}
}

Handle all Events for an aggregate

Please see my first Persistent Subscription below:
namespace PersistentSubscription
{
internal class Program
{
private static void Main()
{
var subscription = new PersistentSubscriptionClient();
subscription.Start();
}
}
public class PersistentSubscriptionClient
{
private IEventStoreConnection _conn;
private const string STREAM = "$ce-customer";
private const string GROUP = "a_test_group";
private const int DEFAULTPORT = 1113;
private static readonly UserCredentials User = new UserCredentials("admin", "changeit");
private EventStorePersistentSubscriptionBase _subscription;
public void Start()
{
var settings = ConnectionSettings.Create();
using (_conn = EventStoreConnection.Create(settings, new IPEndPoint(IPAddress.Loopback, DEFAULTPORT)))
{
_conn.ConnectAsync().Wait();
CreateSubscription();
ConnectToSubscription();
Console.WriteLine("waiting for events. press enter to exit");
Console.ReadLine();
}
}
private void ConnectToSubscription()
{
var bufferSize = 10;
var autoAck = true;
Action<EventStorePersistentSubscriptionBase, ResolvedEvent> eventAppeared = EventAppeared;
_subscription = _conn.ConnectToPersistentSubscription(STREAM, GROUP, eventAppeared, SubscriptionDropped, User, bufferSize, autoAck);
}
private void SubscriptionDropped(EventStorePersistentSubscriptionBase eventStorePersistentSubscriptionBase,
SubscriptionDropReason subscriptionDropReason, Exception ex)
{
ConnectToSubscription();
}
private static void EventAppeared(EventStorePersistentSubscriptionBase eventStorePersistentSubscriptionBase,
ResolvedEvent resolvedEvent)
{
MemoryStream stream = new MemoryStream(resolvedEvent.Event.Data);
IFormatter formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
try
{
CustomerCreated customerCreated = (CustomerCreated)formatter.Deserialize(stream);
Console.WriteLine(customerCreated);
}
catch (Exception e)
{
var test = "test";
}
}
private void CreateSubscription()
{
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.DoNotResolveLinkTos()
.StartFromCurrent();
try
{
_conn.CreatePersistentSubscriptionAsync(STREAM, GROUP, settings, User).Wait();
}
catch (AggregateException ex)
{
if (ex.InnerException.GetType() != typeof(InvalidOperationException)
&& ex.InnerException?.Message != $"Subscription group {GROUP} on stream {STREAM} already exists")
{
throw;
}
}
}
}
}
and my first client below:
using System;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using EventStore.ClientAPI;
namespace WritingEvents
{
class Program
{
static void Main(string[] args)
{
const int DEFAULTPORT = 1113;
var settings = ConnectionSettings.Create();
using (var conn = EventStoreConnection.Create(settings, new IPEndPoint(IPAddress.Loopback, DEFAULTPORT)))
{
conn.ConnectAsync().Wait();
CustomerCreated c1 = new CustomerCreated { Id = Guid.NewGuid(), Name = "Maria" };
EventData customerCreated1 = GetEventDataFor(c1);
conn.AppendToStreamAsync("customer-100", ExpectedVersion.Any, customerCreated1).Wait();
}
}
private static EventData GetEventDataFor(CustomerCreated customerCreated)
{
IFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, customerCreated);
byte[] customerCreatedEventByteArray = stream.ToArray();
return new EventData(
Guid.NewGuid(),
"eventType",
true,
customerCreatedEventByteArray,
null
);
}
}
[Serializable]
public class CustomerCreated
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
I run the server and then then the client. I see an error when deserializing the CustomerCreated event on the server side. The error is: "End of stream was encountered before parsing was completed".
If I change this line:
private const string STREAM = "$ce-customer";
to this:
private const string STREAM = "customer-100";
Then deserialization works correctly on the server side.
How do I handle all customer events - not just customer 100?
I have --run-projections=all when starting Event Store. I have also enabled all projections:
This question helped me: Using the Event Store Client API (.NET), how to I write to a stream and link one event to another?
I simply had to change this:
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.DoNotResolveLinkTos() //Specifically this line
.StartFromCurrent();
to this:
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.ResolveLinkTos() //Specifically this line
.StartFromCurrent();
DoNotResolveLinkTos gets a link to the original event, whereas ResolveLinkTos gets the actual event itself. Therefore I was trying to deserialize the link object, which was causing the exception.

Design solution for working with multiple instantiations at the same time

I don't know if my title is correct. But here's what I want to know.
I have a Download class that returns certain events and has a couple of methods. Each instance of Download class can download a single file. And all those events and methods are related to the file being downloaded.
As it's a multi file downloader, multiple instantiations are required when more than a single file needs to be downloaded.
Each download has a download id, but that is not supplied to the Download class, to keep it independent from the other classes.
Now getting all the info from each instance of the file download and being able to control a single download, is the problem. How do I know which download is which?
Any solutions? Or design patterns you could recommend? I've hit a roadblock.
Download class:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
namespace Mackerel_Download_Manager
{
public class Download
{
public event EventHandler<DownloadStatusChangedEventArgs> ResumablityChanged;
public event EventHandler<DownloadProgressChangedEventArgs> ProgressChanged;
public event EventHandler Completed;
public bool stop = true; // by default stop is true
public bool paused = false;
SemaphoreSlim pauseLock = new SemaphoreSlim(1);
string filename;
public void DownloadFile(Uri DownloadLink, string Path)
{
filename = System.IO.Path.GetFileName(Path);
stop = false; // always set this bool to false, everytime this method is called
var fileInfo = new FileInfo(Path);
long existingLength = 0;
if (fileInfo.Exists)
existingLength = fileInfo.Length;
var request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink);
request.Proxy = null;
request.AddRange(existingLength);
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
long fileSize = existingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded
bool downloadResumable; // need it for not sending any progress
if ((int)response.StatusCode == 206) //same as: response.StatusCode == HttpStatusCode.PartialContent
{
//Console.WriteLine("Resumable");
downloadResumable = true;
}
else // sometimes a server that supports partial content will lose its ability to send partial content(weird behavior) and thus the download will lose its resumability
{
if (existingLength > 0)
{
if (ResumeUnsupportedWarning() == false) // warn and ask for confirmation to continue if the half downloaded file is unresumable
{
return;
}
}
existingLength = 0;
downloadResumable = false;
}
OnResumabilityChanged(new DownloadStatusChangedEventArgs(downloadResumable));
using (var saveFileStream = fileInfo.Open(downloadResumable ? FileMode.Append : FileMode.Create, FileAccess.Write))
using (var stream = response.GetResponseStream())
{
byte[] downBuffer = new byte[4096];
int byteSize = 0;
long totalReceived = byteSize + existingLength;
var sw = Stopwatch.StartNew();
while (!stop && (byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
saveFileStream.Write(downBuffer, 0, byteSize);
totalReceived += byteSize;
float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds;
OnProgressChanged(new DownloadProgressChangedEventArgs(totalReceived, fileSize, (long)currentSpeed));
pauseLock.Wait();
pauseLock.Release();
}
sw.Stop();
}
}
if (!stop) OnCompleted(EventArgs.Empty);
}
catch (WebException e)
{
System.Windows.MessageBox.Show(e.Message, filename);
}
}
public void pause()
{
if (!paused)
{
paused = true;
// Note this cannot block for more than a moment
// since the download thread doesn't keep the lock held
pauseLock.Wait();
}
}
public void unpause()
{
if (paused)
{
paused = false;
pauseLock.Release();
}
}
public void StopDownload()
{
stop = true;
this.unpause(); // stop waiting on lock if needed
}
public bool ResumeUnsupportedWarning()
{
var messageBoxResult = System.Windows.MessageBox.Show("When trying to resume the download , Mackerel got a response from the server that it doesn't support resuming the download. It's possible that it's a temporary error of the server, and you will be able to resume the file at a later time, but at this time Mackerel can download this file from the beginning.\n\nDo you want to download this file from the beginning?", filename, System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == System.Windows.MessageBoxResult.Yes)
{
return true;
}
else
{
return false;
}
}
protected virtual void OnResumabilityChanged(DownloadStatusChangedEventArgs e)
{
var handler = ResumablityChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnProgressChanged(DownloadProgressChangedEventArgs e)
{
var handler = ProgressChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnCompleted(EventArgs e)
{
var handler = Completed;
if (handler != null)
{
handler(this, e);
}
}
}
public class DownloadStatusChangedEventArgs : EventArgs
{
public DownloadStatusChangedEventArgs(bool canResume)
{
ResumeSupported = canResume;
}
public bool ResumeSupported { get; private set; }
}
public class DownloadProgressChangedEventArgs : EventArgs
{
public DownloadProgressChangedEventArgs(long totalReceived, long fileSize, long currentSpeed)
{
BytesReceived = totalReceived;
TotalBytesToReceive = fileSize;
CurrentSpeed = currentSpeed;
}
public long BytesReceived { get; private set; }
public long TotalBytesToReceive { get; private set; }
public float ProgressPercentage
{
get
{
return ((float)BytesReceived / (float)TotalBytesToReceive) * 100;
}
}
public float CurrentSpeed { get; private set; } // in bytes
public TimeSpan TimeLeft
{
get
{
var bytesRemainingtoBeReceived = TotalBytesToReceive - BytesReceived;
return TimeSpan.FromSeconds(bytesRemainingtoBeReceived / CurrentSpeed);
}
}
}
}
Download class is instantiated inside a Mackerel class, that starts the download for the given downloads.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
namespace Mackerel_Download_Manager
{
public static class Mackerel
{
//Main Menu functions
public static void ResumeDownload(string[] DownloadIDs)
{
foreach (var DownloadID in DownloadIDs)
{
var itemToResume = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
if (itemToResume.Running == false)
{
itemToResume.Running = true;
var download = new Download();
download.DownloadFile(itemToResume.DownloadLink, itemToResume.SaveTo);
var window = new Dialogs.DownloadProgress(itemToResume);
window.Show();
double progress = 0;
itemToResume.Status = string.Format("{0:0.00}%", progress);
Downloads.DownloadEntries.CollectionChanged += delegate
{
if (!itemToResume.Running) window.Close();
};
}
}
}
public static void StopDownload(string[] DownloadIDs)
{
foreach (var DownloadID in DownloadIDs)
{
var itemToStop = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
if (itemToStop.Running == true)
itemToStop.Running = false;
}
}
public static void StopAllDownloads()
{
foreach (var itemToStop in Downloads.DownloadEntries.Where(download => download.Running == true))
itemToStop.Running = false;
}
public static void RemoveDownload(string[] DownloadIDs) // this method is able to delete multiple downloads
{
foreach (var DownloadID in DownloadIDs)
{
// delete from the download list
var selectedDownload = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
var selectedDownloadIndex = Downloads.DownloadEntries.IndexOf(selectedDownload);
Downloads.DownloadEntries.RemoveAt(selectedDownloadIndex);
//delete from the harddrive
if (File.Exists(selectedDownload.SaveTo))
File.Delete(selectedDownload.SaveTo);
}
Downloads.Serialize(); // save current state of object
}
public static void RemoveCompletedDownloads() // this method just removes all completed downloads from Mackerel's download list (it doesn't delete them from the hard drive)
{
foreach (var itemToRemove in Downloads.DownloadEntries.Where(download => download.Status == "Complete").ToList())
{
Downloads.DownloadEntries.Remove(itemToRemove);
}
}
// Context Menu
public static void OpenDownloadProperties(string DownloadID) // Open "Download Properties" for the given download ID
{
var DownloadProperties = new Dialogs.Context_Menu.DownloadProperties(DownloadID);
DownloadProperties.Owner = Application.Current.MainWindow; // so that this dialog centers to its parent window, as its window is set to WindowStartupLocation="CenterOwner"
DownloadProperties.ShowDialog();
}
}
}
Full source code is here: https://github.com/Expenzor/mackerel-download-manager
Sounds like your making the download itself an object but are using "downloader" as the name instead. I would maybe suggest an array of downloaded objects or similar. The downloader class can have a method which creates a new object - perhaps a download object. Using an array or linked list etc will give you the opportunity to address the object and call them independently - know which is which.
Posting your code would help as well.

Categories

Resources