I have Pinger Application that gets all ping informations of servers written in the Servers.txt file
Half of them working servers, half of them not. I want to make my application write working servers to the another .txt file called WorkingServers.txt located in C:\Program Files\Servers folder
Here is my code:
using System;
using System.IO;
using System.Threading;
namespace AnchovyMultiPing
{
class Program
{
static void Main(string[] args)
{
var servers = File.ReadAllLines(#"C:\Program Files\Servers\Servers.txt");
foreach (var line in servers)
{
string currentServer = line;
if (args == null) throw new ArgumentNullException("args");
var waiter = new ManualResetEventSlim(false);
var pingData = new MultiPing(new[] { line }, waiter, 300);
waiter.Wait();
Console.WriteLine(pingData.GetPingInformation());
}
Console.WriteLine();
Console.WriteLine("- * - * - The Job is Done - * - * -");
Console.ReadLine();
}
}
}
and MultiPing.cs:
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
namespace AnchovyMultiPing
{
internal sealed class MultiPing : AbstractMultiPing
{
private int Timeout { get; set; }
private string[] Hosts { get; set; }
private int _count;
private ManualResetEventSlim Waiter { get; set; }
private readonly byte[] _buffer = Encoding.ASCII.GetBytes("aaa");
private Dictionary<string, long> _table = new Dictionary<string, long>();
private class Parameters
{
public String Host { get; set; }
public ManualResetEventSlim Event { get; set; }
}
public MultiPing(string[] hosts, ManualResetEventSlim waiter, int timeout = 12000)
{
Hosts = hosts;
Waiter = waiter;
Timeout = timeout;
RequestTime();
}
public override IMultiPings RequestTime()
{
try
{
_count = 0;
_table = new Dictionary<string, long>();
foreach (string host in Hosts)
{
using (var pingSender = new Ping())
{
pingSender.PingCompleted += PingCompletedCallback;
var options = new PingOptions(64, true);
try
{
pingSender.SendAsync(host, Timeout, _buffer, options,
new Parameters {Host = host, Event = Waiter});
}
catch
{
_count += 1;
if (_count == Hosts.Length)
Waiter.Set();
}
}
}
}
catch (MultiPingException ex)
{
Console.Write("RequestTime Exception");
Console.ReadKey();
}
return this;
}
//[MethodImpl(MethodImplOptions.Synchronized)] leaving this in favour of the operating system scheduler for better performance.
private void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
try
{
_count += 1;
PingReply reply = e.Reply;
if (reply != null && reply.Address != null && reply.Address.ToString() != "0.0.0.0")
{
if (_count > 0)
{
try
{
if (!_table.ContainsKey(reply.Address.ToString()))
_table.Add(((Parameters) e.UserState).Host, reply.RoundtripTime);
}
catch (NullReferenceException ex)
{
// catch null exception
throw new MultiPingException("Ping round trip time is null");
}
}
}
if (_count == Hosts.Length)
{
((Parameters) e.UserState).Event.Set();
}
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
Console.WriteLine(e.Error.ToString());
((ManualResetEventSlim) e.UserState).Set();
}
}
catch (MultiPingException ex)
{
Console.Write("Exception");
Console.ReadKey();
}
}
public override String GetPingInformation()
{
var build = new StringBuilder();
foreach (string host in _table.Keys)
{
build.AppendLine(string.Format("{0} : {1}", host, _table[host]));
}
return build.ToString();
}
public override String GetIp()
{
string ip = "";
long time = -1L;
foreach (string host in _table.Keys)
{
long roundTime = _table[host];
if ((time == -1L) || (roundTime >= 0 && roundTime < time))
{
time = roundTime;
ip = host;
}
}
return ip;
}
}
}
Declare new string at the beginning var log = string.Empty; At the end of the iteration add the server name to it, if the ping is successful. I don't know how the MultiPing class looks like, so I added artificial IsSuccessful, but what I mean is something like this:
waiter.Wait();
if(pingData.IsSuccessful)
{
log += line;
}
Console.WriteLine(pingData.GetPingInformation());
Then as the last line of your program save everything to the other file.
File.AppendAllText("C:\Program Files\Servers\WorkingServers.txt", log);
Related
I developed an MPI test program where the master node distributes work to the worker nodes.
The worker node uses comm.Send() to request work and the master node checks with comm.ImmediateProbe if any of the worker nodes wants to request some work. If a request is available it is read with comm.Receive and the work is sent to the worker for processing.
When I run my test program with mpiexec.exe on a single host, either localhost or also a remote host everything works as expected, but when I run it on two hosts at the same time
the Send on the remote host blocks and the master nodes ImmediateProbe never receives the message sent from the worker on the remote host.
I run the program with mpiexec.exe -wdir \\DESKTOP-58QONBS\MPITest -hosts 2 DESKTOP-58QONBS 2 LAPTOP-L8F7AN5R 1 MPITest.exe
I'm new to MPI, so maybe I am doing something wrong I just could not figure out yet why the behaviour is like this when using two hosts at the same time.
The full code is below:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
namespace MPITest
{
public abstract class MPIMasterWorkerBase<TWork, TResult>
where TWork : class
where TResult : class
{
protected abstract void Initialize(bool isMaster);
protected abstract void Main();
protected abstract void ProcessResult(TResult result);
protected abstract TResult ProcessWork(TWork work);
protected abstract TWork GetWork();
private volatile bool terminate = false;
private Thread thread;
private MPI.Intracommunicator comm;
public void Run(string[] args)
{
MPI.Environment.Run(ref args, comm =>
{
this.comm = comm;
if (comm.Size < 2)
{
Console.WriteLine("At least 2 processes are required.");
return;
}
if (comm.Rank == 0)
{
Initialize(isMaster: true);
thread = new Thread(MasterThread);
thread.Start();
Main();
terminate = true;
thread.Join();
}
else
{
Initialize(isMaster: false);
thread = new Thread(WorkerThread);
thread.Start();
thread.Join();
}
});
}
private void MasterThread()
{
Console.WriteLine($"MasterStart {MPI.Environment.ProcessorName}");
var done = new bool[comm.Size];
done[0] = true;
while (!done.All(x => x == true))
{
for (int i = 1; i < comm.Size; i++)
{
if (comm.ImmediateProbe(i, 0) != null)
{
Console.WriteLine($"Receive: {i}");
comm.Receive<int>(i, 0);
var work = GetWork();
if (work != null)
{
comm.Send(1, i, 0);
comm.Send(work, i, 0);
}
else
{
if (terminate)
{
comm.Send(-1, i, 0);
done[i] = true;
}
else
{
comm.Send(0, i, 0);
}
}
}
if (comm.ImmediateProbe(i, 1) != null)
{
var result = comm.Receive<TResult>(i, 1);
ProcessResult(result);
}
}
Thread.Sleep(1000);
}
Console.WriteLine("MasterStop");
}
private void WorkerThread()
{
Console.WriteLine($"WorkerStart: {comm.Rank} {MPI.Environment.ProcessorName}");
while (!terminate)
{
Thread.Sleep(1000);
Console.WriteLine($"Send: {comm.Rank}");
comm.Send(0, 0, 0);
var flag = comm.Receive<int>(0, 0);
if (flag == -1)
break;
else if (flag == 0)
continue;
var work = comm.Receive<TWork>(0, 0);
var result = ProcessWork(work);
comm.Send(result, 0, 1);
}
Console.WriteLine($"WorkerStop: {comm.Rank}");
}
}
[Serializable]
public class WorkItem
{
public int Id { get; set; }
}
public class MPITest : MPIMasterWorkerBase<WorkItem, WorkItem>
{
private ConcurrentQueue<WorkItem> queue = new();
private int id;
protected override void Initialize(bool isMaster)
{
}
protected override void Main()
{
var startTime = DateTime.UtcNow;
while ((DateTime.UtcNow - startTime).TotalSeconds < 10)
{
for (int i = 0; i < 2; i++)
queue.Enqueue(new WorkItem { Id = id++ });
Thread.Sleep(1000);
}
}
protected override WorkItem GetWork()
{
if (queue.TryDequeue(out var result))
return result;
return null;
}
protected override WorkItem ProcessWork(WorkItem work)
{
Console.WriteLine($"Processing Work {work.Id}");
return work;
}
protected override void ProcessResult(WorkItem result)
{
Console.WriteLine($"Process Result {result.Id}");
}
}
class Program
{
static void Main(string[] args)
{
new MPITest().Run(args);
}
}
}
The comm.Send was blocking, but after some minutes of waiting the program started to work.
The issues were caused by the VirtualBox Host-Only Network Adapter that was also installed on the system. Disabling this adapter in the network settings resolved all the issues.
I am using RabbitMQ.Client(C#) to work with RabbitMQ. I am having trouble retrieving the messages from the queue once I remove and re-add the message received event handler.
consumer.Received -= OnMessageRecieved;
I have a complex system, where a windows service subscribes to the RabbitMQ queues and process the messages. There are multiple threads running for various things - timer to call PUSH api, another timer to do api authentication etc. If api authentication fails, we don't want to process the messages from the queue. We want to keep the messages in ready state. Only, when the api authentication is success, we want to process the messages. So, on failure event, we remove the event handler and on success we add it back. When we do that, the event handler is added successfully, but now the messages are not retrieved from the queue.
To simulate this, I have created a console app. I have written this in less than an hour, I know this code is very raw and dirty - please excuse me for that.
sub.StopReceiveMessages(); has the code that removes handler consumer.Received -= OnMessageRecieved. And, sub.StartReceiveMessages(); has the code that removes handler consumer.Received += OnMessageRecieved. When you add it back I thought it would work as normal. But, it doesn't hit the MessageReceived() anymore. Is it that we have to call BasicConsume again although I am using the same consumer? Any help would be appreciate.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Client = RabbitMQ.Client;
namespace RabbitMQTest
{
class Program
{
static void Main(string[] args)
{
MessageBusSubscription sub = new MessageBusSubscription();
sub.Subscription("EmployeeDataChanged", "HR", "CompanyA", 5, 5000);
sub.MessagesReceived += MessageReceived;
Console.WriteLine("Press ESC to exit");
while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
// Simulating an event where we have to stop pulling the messages from the queue
sub.StopReceiveMessages();
Thread.Sleep(2000);
// After some time, the issue is resolved and now we resume reading the messages from the queue
sub.StartReceiveMessages();
}
sub.Dispose();
Environment.Exit(0);
}
private static bool MessageReceived(string topic, string subscription, List<MessageContainer> messages)
{
List<MessageContainer> data = null;
data = messages as List<MessageContainer>;
foreach (var messageContainer in data)
{
// Do something with the message
// Ack or Reject based on some logic
}
return true;
}
}
public class MessageBusSubscription : IDisposable
{
#region variables
Client.Events.EventingBasicConsumer consumer;
Client.ConnectionFactory factory;
private System.Timers.Timer _timer = null;
private Client.IModel _channel = null;
private string _topic = string.Empty;
private string _subscription = string.Empty;
int batchCounter = 0;
int batchSize = 0;
ManualResetEvent _waitHandle = new ManualResetEvent(false);
bool _disposing = false;
bool _isSubscribed = false;
List<MessageContainer> messages = new List<MessageContainer>();
private object _processMessageLocker = new object();
public event Func<string, string, List<MessageContainer>, bool> MessagesReceived;
#endregion
public MessageBusSubscription()
{
Client.IConnection conn = GetConnection();
_channel = conn.CreateModel();
}
public void Subscription(string exchangeName, string queueName, string routingKey, int batchSize, double batchInterval)
{
_topic = exchangeName;
_subscription = queueName;
DeclareExchangeAndQueue(exchangeName, queueName, routingKey);
if (batchInterval > 0 && batchSize > 1)
{
_timer = new System.Timers.Timer(batchInterval);
_timer.Elapsed += (o, e) => {
ProcessMessagesReceived(exchangeName, queueName, true);
};
}
Subscribe(routingKey, exchangeName, queueName, batchSize, batchInterval);
}
public Task Subscribe(string routingKey, string topic, string subscription, int _batchSize, double batchInterval)
{
try
{
consumer = new Client.Events.EventingBasicConsumer(_channel);
batchCounter = 0;
batchSize = _batchSize;
consumer.Received += OnMessageRecieved;
_isSubscribed = true;
//RabbitMQ PUSH implementation using RabbitMQ.Client library
var t = Task.Factory.StartNew(() =>
{
try
{
if (_timer != null)
{
_timer.Start();
}
var queueName = string.Join(".", routingKey, topic, subscription);
if (!_disposing)
{
_channel.BasicConsume(queueName, false, consumer);
_waitHandle.WaitOne();
}
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
}
if (_channel != null)
{
if (_channel.IsOpen)
_channel.Close();
_channel.Dispose();
}
}
catch (Exception ex)
{
}
});
return t;
}
catch (Exception ex)
{
var exTask = new Task(() => { throw new AggregateException(ex); });
exTask.RunSynchronously();
return exTask;
}
}
public void OnMessageRecieved(Client.IBasicConsumer sender, Client.Events.BasicDeliverEventArgs e)
{
try
{
string sourceExchange = string.Empty;
string sourceQueue = string.Empty;
string body = Encoding.ASCII.GetString(e.Body);
string routingKey = e.RoutingKey;
ulong deliveryTag = e.DeliveryTag;
sourceExchange = "";
sourceQueue = "";
MessageContainer msgContainer = new MessageContainer();
msgContainer.Message = body;
batchCounter++;
msgContainer.DeliveryTag = deliveryTag;
lock (_processMessageLocker)
{
messages.Add(msgContainer);
ProcessMessagesReceived(_topic, _subscription, false);
}
}
catch (Exception ex)
{
}
}
public void ProcessMessagesReceived(string topic, string subscription, bool hasTimerElapsed)
{
try
{
// if it's the last message in the batch, or the interval has elapsed
if ((batchCounter % batchSize == 0 && messages.Count > 0) || (hasTimerElapsed && messages.Count > 0))
{
if (_timer != null)
{
_timer.Stop();
}
lock (_processMessageLocker)
{
// process the message
if (!MessagesReceived(topic, subscription, messages))
{
throw new Exception("Message processing exception - look in the default error queue (broker)");
}
messages.Clear();
}
batchCounter = 0;
if (_timer != null)
{
_timer.Start();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public Client.IConnection GetConnection()
{
factory = new Client.ConnectionFactory();
factory.UserName = "guest";
factory.Password = "guest";
factory.VirtualHost = "/";
factory.HostName = "localhost";
factory.Protocol = Client.Protocols.AMQP_0_9_1;
factory.Port = 5673;
return factory.CreateConnection();
}
public void DeclareExchangeAndQueue(string exchangeName, string queueName, string routingKey)
{
using (var exchangeConn = factory.CreateConnection())
using (Client.IModel channel = exchangeConn.CreateModel())
{
channel.ExchangeDeclare(exchangeName, Client.ExchangeType.Direct);
var queue = String.Join(".", routingKey, exchangeName, queueName);
channel.QueueDeclare(queue, false, false, false, null);
channel.QueueBind(queue, exchangeName, routingKey, null);
}
}
public void StartReceiveMessages()
{
if (_timer != null && !_isSubscribed)
{
_timer.Start();
}
if (consumer != null && !_isSubscribed)
{
consumer.Received += OnMessageRecieved;
_isSubscribed = true;
}
}
public void StopReceiveMessages()
{
if (_timer != null)
{
_timer.Stop();
}
if (consumer != null)
{
consumer.Received -= OnMessageRecieved;
_isSubscribed = false;
}
}
public void Dispose()
{
_disposing = true;
_waitHandle.Set();
_waitHandle?.Dispose();
_waitHandle = null;
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
}
}
}
public class MessageContainer
{
public ulong DeliveryTag { get; set; }
public string Message { get; set; }
}
}
Don't unsubscribe from the Received event, instead use the BasicCancel method to stop consuming messages, then use BasicConsume again to start consuming.
A lot of the thread synchronization code as well as running the consumer in a Task isn't really the best practice. If you'd like further assistance with this code, save it in a git repository or gist somewhere and follow-up on the official mailing list.
NOTE: the RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.
I have been given the task to create a interface where I receive data through socket from the sender, for this purpose I am using NetMQ PushSocket for the sender side and then I receive the data at client side sung PullSocket and I have to update the UI (WPF app) when data is received so I receive data using poller in ReceiveReady event of the PullSocket when I do this in a seperate service class and call that class in UI ViewModel the UI thread hangs, so I use Poller.Run in a task, now the problem is that when I stop the poller and then restart it again it doesn't call the ReceiveReady event
Here is the ReceiverService for receiving the data.
public class ReceiverService
{
string msg;
string _address;
int _port;
PullSocket receiver;
NetMQPoller poller;
private MapViewModel ViewModel { get; set; }
public ReceiverService(MapViewModel mapViewModel, int port = 5555)
{
_address = GetComputerLanIP();
_port = port;
receiver = new PullSocket($"tcp://{_address}:{_port}");
receiver.Options.Linger = TimeSpan.Zero;
this.ViewModel = mapViewModel;
poller = new NetMQPoller { receiver };
receiver.ReceiveReady += receiver_ReceiveReady;
}
public void Start()
{
receiver.Connect($"tcp://{_address}:{_port}");
poller.Run();
}
public void Stop()
{
receiver.Disconnect($"tcp://{_address}:{_port}");
poller.Stop();
}
private void receiver_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
// receive won't block as a message is ready
msg = e.Socket.ReceiveFrameString();
// send a response
if (!string.IsNullOrEmpty(msg))
{
try
{
//Updaing the ViewModel here
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
}
private string GetComputerLanIP()
{
string strHostName = Dns.GetHostName();
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
foreach (var ipAddress in ipEntry.AddressList)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
{
return ipAddress.ToString();
}
}
return "";
}
private string GetValueFromMessage(string identifier)
{
msg.Replace("{", "");
msg.Replace("}", "");
identifier = /*" " + */identifier + " = ";
try
{
int index = msg.IndexOf(identifier) + identifier.Length;
if (index != -1)
{
int index2 = msg.IndexOf(";", index);
if (index2 == -1)
{
index2 = msg.Length;
}
return msg.Substring(index, index2 - index);
}
}
catch (IndexOutOfRangeException ex)
{
return null;
}
return null;
}
}
and in my ViewModel I have set commands for these
private void StartReceiver()
{
Task.Run(() => ReceiverService.Start());
}
private void StopReceiver()
{
Task.Run(() => ReceiverService.Stop());
}
What am I doing wrong? I am new to NetMQ and WPF. TIA
at first it would be good to make a task inside ReceiverService, kind of an ActorModel, because in the end if You would like to reuse it anywhere You need to remember that You should creat a Task first.
always it would be good to have socket in using statement, because You should always close socket if You are not using it
public async Task StartAsync() {
await Task.Run(() => ThreadBody())
}
public void Stop()
{
_poller.Stop();
}
private void ThreadBody()
{
using (PullSocket receiverSocket = new PullSocket())
using (_poller = new NetMQPoller())
{
receiverSocket.Connect($"tcp://{_address}:{_port}");
receiverSocket.ReceiveReady += receiver_ReceiveReady;
_poller.Add(receiverSocket);
_poller.Run();
}
}
I wrote a client-server app, this is a console chat application for many clients. When only one client is connected, the application works well, but when two or more clients are connected I have a bug, after sending one message to the second client, he lost connection to the server and only first client can send a message to the server...
I used Task for asynchronous operations like listening port and sending messages. When one client sends a message to the server, it adds it to the list messages and resends to all clients to refresh all windows.
Server application:
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Server
{
class Program
{
private static List<Client> clients = new List<Client>();
private static TcpListener listener = null;
private static StreamReader reader = null;
private static StreamWriter writer = null;
private static List<Task> clientTasks = new List<Task>();
private static List<string> messages = new List<string>();
public static void Main()
{
Console.Title = "Server";
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
listener.Start();
Console.WriteLine("Server started...");
var connectTask = Task.Run(() => ConnectClients());
//var listenTask = Task.Run(() => ListenClients());
Task.WaitAll(connectTask);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
if (listener != null)
{
listener.Stop();
}
}
}
private static void ConnectClients()
{
Console.WriteLine("Waiting for incoming client connections...");
while (true)
{
if (listener.Pending()) //if someone want to connect
{
clients.Add(new Client(listener.AcceptTcpClient(), "Client: " + (clients.Count + 1)));
Console.WriteLine(clients[clients.Count - 1].clientName + " connected to server.");
var newClientTask = Task.Run(() => HandleClient(clients[clients.Count - 1]));
clientTasks.Add(newClientTask); //start new task for new client
}
}
}
private static void HandleClient(Client TCPClient)
{
Console.WriteLine("Starting handle client");
string s = string.Empty;
writer = new StreamWriter(TCPClient.client.GetStream());
reader = new StreamReader(TCPClient.client.GetStream());
try
{
while (!(s = reader.ReadLine()).Equals("Exit") || (s == null))
{
if(!TCPClient.client.Connected)
{
Console.WriteLine("Client disconnected.");
clients.Remove(TCPClient);
}
Console.WriteLine("From client: " + TCPClient.clientName + " -> " + s);
messages.Add(TCPClient.clientName + ": " + s); //save new message
//Console.WriteLine(s);
foreach (Client c in clients) //refresh all connected clients
{
c.writer.WriteLine("%C"); //clear client
foreach (string msg in messages)
{
c.writer.WriteLine(msg);
c.writer.Flush();
}
}
}
//CloseServer();
}
catch (Exception e) { Console.WriteLine(e); }
Console.WriteLine("ending handle client");
}
private static void CloseServer()
{
reader.Close();
writer.Close();
clients.ForEach(tcpClient => tcpClient.client.Close());
}
}
}
Client information class:
using System.Net.Sockets;
using System.IO;
namespace Server
{
class Client
{
public TcpClient client;
public StreamWriter writer;
public string clientName;
public Client(TcpClient client, string clientName)
{
this.client = client;
writer = new StreamWriter(client.GetStream());
this.clientName = clientName;
}
}
}
Client application:
using System.Net.Sockets;
using System.Net;
using System.IO;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Client
{
class Program
{
private static TcpClient client = new TcpClient();
private static StreamReader reader;
private static StreamWriter writer;
private static bool refresh;
private static List<string> messages = new List<string>();
public static void Main()
{
Console.Title = "Client";
ConnectLoop();
//Task.WaitAll(sendTask, recieveTask); //wait for end of all tasks
}
private static void ConnectLoop()
{
bool refreshTask = false;
Task sendTask = null, recieveTask = null, updateConvTask = null;
while (true)
{
if(!client.Connected) //try to connect
{
refreshTask = true;
if(sendTask != null || recieveTask != null || updateConvTask != null)
{
sendTask.Dispose();
recieveTask.Dispose();
updateConvTask.Dispose();
sendTask = recieveTask = updateConvTask = null;
}
Console.WriteLine("Connecting to server...");
try
{
client.Connect(IPAddress.Parse("127.0.0.1"), 8080);
}
catch (SocketException) { }
Thread.Sleep(10);
}
else if(refreshTask) // \/ CONNECTED \/
{
Console.WriteLine("Connected.");
reader = new StreamReader(client.GetStream());
writer = new StreamWriter(client.GetStream());
sendTask = Task.Run(() => SendMessage()); //task for sending messages
recieveTask = Task.Run(() => RecieveMessage()); //task for recieving messages
updateConvTask = Task.Run(() => UpdateConversation()); //task for update console window
refreshTask = false;
}
}
}
private static void SendMessage()
{
string msgToSend = string.Empty;
do
{
Console.WriteLine("Enter a message to send to the server");
msgToSend = Console.ReadLine();
writer.WriteLine(msgToSend);
writer.Flush();
} while (!msgToSend.Equals("Exit"));
EndConnection();
}
private static void RecieveMessage()
{
try
{
while (client.Connected)
{
//Console.Clear();
string msg = reader.ReadLine();
if(msg != string.Empty)
{
if (msg == "%C") //special message from server, clear messages if recieve it
{
messages.Clear();
}
else
{
messages.Add(msg);
refresh = true; //refresh console window
}
}
//Console.Clear();
//Console.WriteLine(msgFromServer);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static void UpdateConversation()
{
//string conversationTmp = string.Empty;
try
{
while (true)
{
if (refresh) //only if refresh
{
refresh = false;
Console.Clear();
messages.ForEach(msg => Console.WriteLine(msg)); //write all messages
Console.WriteLine();
}
}
}
catch (Exception) { }
}
private static void EndConnection()
{
reader.Close();
writer.Close();
client.Close();
}
}
}
I know that my bug will be something stupid. I'm new to TCP/IP applications, could you give me links to some tutorials that use it and Tasks?
Thanks for any help.
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.