I want to be able to play multiple sounds at once. I tried this using multi threading but found they would still play one after the other. Is there a way to get them to play at the same time?
static void Main(string[] args)
{
Console.WriteLine("Hello World");
Thread th = new Thread(playSound);
Thread th1 = new Thread(playSound1);
th.Start();
th1.Start();
}
public static void playSound()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(#"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
s1.PlaySync();
}
public static void playSound1()
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(#"c:\Users\Ben\Documents\c#\note_e.wav");
s1.Load();
s1.PlaySync();
}
}
How about if we schedule a parallel execution.
Like that:
var files = new List<string>() {"note_1.wav", "note_2.wav"};
Parallel.ForEach(files, (currentFile) =>
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(currentFile);
s1.Load();
s1.PlaySync();
});
Try using
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
Usage:
mciSendString(#"open path\to\your\file.wav type waveaudio alias anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(#"play anAliasForYourSound", null, 0, IntPtr.Zero);
mciSendString(#"open path\to\your\anotherFile.wav type waveaudio alias anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
mciSendString(#"play anAliasForYourAnotherSound", null, 0, IntPtr.Zero);
You can't start threads at exactly same time but in this case you can improve your code by loading the sounds before starting the threads:
static void Main(string[] args)
{
System.Media.SoundPlayer s1 = new System.Media.SoundPlayer(
#"c:\Users\Ben\Documents\c#\note_c.wav");
s1.Load();
System.Media.SoundPlayer s2 = new System.Media.SoundPlayer(
#"c:\Users\Ben\Documents\c#\note_e.wav");
s2.Load();
Console.WriteLine("Hello World");
Thread th = new Thread(() => playSound(s1));
Thread th1 = new Thread(() => playSound(s2));
th.Start();
th1.Start();
}
public static void playSound(System.Media.SoundPlayer s)
{
s.PlaySync();
}
Otherwise you'll need to implement a synchronization system.
The best way is to use DirectX xAudio2 via SharpDX , you can find it here:
https://github.com/sharpdx/SharpDX-Samples/tree/master/Desktop/XAudio2/PlaySound
It works perfectly , but it would be a bit tricky to make the code better, by utilizing Events & tasks instead of the while loop and sync execution, and to be able to load the xAudio2 graph dynamically to change the sound without reconstructing the whole graph.
Add these Nuget packages SharpDx
SharpDX.MediaFoundation
SharpDx.Xaudio2
and use code below, it uses DirectX xAudio2 and constructs a graph with a mixer, it can play WAV 8 and 16 as well
public class SoundServices : IDisposable
{
/// <summary>
/// Gets current sound file
/// </summary>
string SoundFile { get; }
/// <summary>
/// Gets current sound stream
/// </summary>
public Stream SoundStream { get; private set; }
/// <summary>
/// Gets/Sets looping option, sound will loop if set to true.
/// </summary>
public bool IsLooping { get; private set; }
/// <summary>
/// Holds the message of last error if any, check it if some feature fails
/// </summary>
public string LastErrorMsg { get; private set; }
//Play control flags
bool IsPlaying = false;
bool IsUserStop = false;
AutoResetEvent Playing;
float CurrentVolume = 1.0f;
bool IsInitialized = false;
SoundStream stream;
WaveFormat waveFormat;
AudioBuffer buffer;
SourceVoice sourceVoice;
XAudio2 xaudio2;
MasteringVoice masteringVoice;
/// <summary>
/// Initializes an instance by creating xAudio2 graph and preparing audio Buffers
/// </summary>
/// <param name="soundStream">WAV format sound stream</param>
/// <param name="loop">Bool indicating to loop sound playing or not</param>
public SoundServices(Stream soundStream, bool loop)
{
try
{
if (soundStream == null)
{
throw new ArgumentNullException("soundStream", "Null is not allowed, please specify a valid stream");
}
SoundStream = soundStream;
SoundFile = null;
IsLooping = loop;
//Playing = new ManualResetEvent(false);
Playing = new AutoResetEvent(false);
BuildxAudio2Graph();
}
catch (Exception e)
{
throw e;
}
}
#region Sound Play API
/// <summary>
/// Plays the sound stream loaded during initialization
/// </summary>
/// <returns>Task of sound playing</returns>
public Task PlaySound()
{
return Task.Factory.StartNew(() =>
{
PlayRepeatAsync();
});
}
/// <summary>
/// Task for starting play of sound buffers using xAudio2 graph
/// </summary>
void PlayRepeatAsync()
{
try
{
IsPlaying = true;
if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
sourceVoice.Start();
Playing.WaitOne();
sourceVoice.Stop();
IsPlaying = false;
sourceVoice.FlushSourceBuffers();
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
}
catch (Exception e)
{
LastErrorMsg = "PlayRepeatAsync(): "+ e.Message;
IsPlaying = false;
}
}
/// <summary>
/// Initializes xAudio2 graph to be used for sound playing
/// </summary>
void BuildxAudio2Graph()
{
try
{
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//xAudio2 graph creation step (1) Create XAudio2 device
xaudio2 = new XAudio2();
//xAudio2 graph creation step (2) Create MasteringVoice and connect it to device
//*Note: You must use aMasteringVoice to connect to xAudioDevice
masteringVoice = new MasteringVoice(xaudio2);
//SetVolume(CurrentVolume);
//xAudio2 graph creation step (3) Prepare sourceVoice
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
// Adds a callback check buffer end and Looping option
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
//xAudio2 graph creation step (5) send the AudioBuffer to sourceVoice
IsInitialized = true;
}
catch (Exception e)
{
LastErrorMsg = "BuildxAudio2Graph(): " + e.Message;
IsInitialized = false;
}
}
/// <summary>
/// Recreates source buffer allowing sound change or loop change
/// </summary>
void RecreateBuffer()
{
try
{
SoundStream.Seek(0, SeekOrigin.Begin);
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
//if (buffer == null)
{
//stream = new SoundStream(SoundStream);
//waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = SharpDX.XAudio2.BufferFlags.EndOfStream
};
if (IsLooping)
buffer.LoopCount = AudioBuffer.LoopInfinite;
sourceVoice.FlushSourceBuffers();
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
//Close the stream as it is now loaded in buffer already
//stream.Close();
}
}
catch (Exception e)
{
LastErrorMsg = "RecreateBuffer(): " + e.Message;
}
}
/// <summary>
/// Changes current audio to a new one
/// </summary>
/// <param name="soundStream">a stream from wav file</param>
public void ChangeSoundTo(Stream soundStream, bool Loop)
{
try
{
IsLooping = Loop;
SoundStream = soundStream;
stream = new SoundStream(SoundStream);
waveFormat = stream.Format;
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
sourceVoice.BufferEnd += SourceVoice_BufferEnd;
RecreateBuffer();
}
catch (Exception e)
{
IsInitialized = false;
LastErrorMsg = "ChangeSoundTo(): " + e.Message;
}
}
/// <summary>
/// Set loop ot no loop
/// </summary>
/// <param name="loop">True = Loop forever, false = play till end</param>
public void SetLooping(bool loop)
{
if (IsPlaying)
Stop();
IsLooping = loop;
RecreateBuffer();
}
#endregion
/// <summary>
/// Immediately Stops currently playing sound
/// </summary>
public void Stop()
{
try
{
if (IsPlaying)
{
IsUserStop = true;
Playing.Set();
}
}
catch (Exception e)
{
LastErrorMsg = "Stop(): " + e.Message;
}
}
/// <summary>
/// Gets Current Volume
/// </summary>
/// <returns>Current volume</returns>
public float GetVolume()
{
float current = 0.0f;
try
{
if (sourceVoice == null || sourceVoice.IsDisposed) return CurrentVolume;
sourceVoice.GetVolume(out current);
}
catch (Exception e)
{
LastErrorMsg = "GetVolume(): " + e.Message;
}
return current;
}
/// <summary>
/// Sets the current volume
/// </summary>
/// <param name="newVolume">returns back the current setting for confirmation</param>
/// <returns>The current set volume</returns>
public float SetVolume(float newVolume)
{
try
{
if (newVolume > 1 || newVolume < 0) return GetVolume();
if (sourceVoice == null || sourceVoice.IsDisposed)
{
CurrentVolume = newVolume;
return newVolume;
}
sourceVoice.SetVolume(newVolume, 0);
return GetVolume();
}
catch (Exception e)
{
LastErrorMsg = "SetVolume(): " + e.Message;
return 0.0f;
}
}
/// <summary>
/// End of buffer event handler
/// </summary>
/// <param name="obj"></param>
private void SourceVoice_BufferEnd(IntPtr obj)
{
//Debug.WriteLine($"buffer end reached with looping {IsLooping}");
if (!IsLooping)
{
if (IsPlaying && !IsUserStop)
Playing.Set();
else if(IsUserStop)
{
IsUserStop = false;
}
}
}
public void Dispose()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
~SoundServices()
{
if (sourceVoice != null && !sourceVoice.IsDisposed)
{
sourceVoice.DestroyVoice();
sourceVoice.Dispose();
}
if (buffer != null && buffer.Stream != null)
buffer.Stream.Dispose();
if (masteringVoice != null && !masteringVoice.IsDisposed)
masteringVoice.Dispose();
if (xaudio2 != null && !xaudio2.IsDisposed)
xaudio2.Dispose();
}
}
And you can use multiple SoundServices instances as you need:
var sp = new SoundServices(new MemoryStream(File.ReadAllBytes(WavFileName)), true);
if (sp != null)
sp.PlaySound();
Related
so I have my client-server program which was working fine, but now I ran into an issue and I have not a single clue on how to solve it. So my client connects to the server, using async sockets. But when it tries to get the async result, it contains nothing. It disposes the socket and starts all over. This is my code(client):
public class Client
{
/// <summary>
/// Occurs as a result of an unrecoverable issue with the client.
/// </summary>
public event ClientFailEventHandler ClientFail;
/// <summary>
/// Represents a method that will handle failure of the client.
/// </summary>
/// <param name="s">The client that has failed.</param>
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
public delegate void ClientFailEventHandler(Client s, Exception ex);
/// <summary>
/// Fires an event that informs subscribers that the client has failed.
/// </summary>
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
private void OnClientFail(Exception ex)
{
var handler = ClientFail;
if (handler != null)
{
handler(this, ex);
}
}
/// <summary>
/// Occurs when the state of the client has changed.
/// </summary>
public event ClientStateEventHandler ClientState;
/// <summary>
/// Represents the method that will handle a change in the client's state
/// </summary>
/// <param name="s">The client which changed its state.</param>
/// <param name="connected">The new connection state of the client.</param>
public delegate void ClientStateEventHandler(Client s, bool connected);
/// <summary>
/// Fires an event that informs subscribers that the state of the client has changed.
/// </summary>
/// <param name="connected">The new connection state of the client.</param>
private void OnClientState(bool connected)
{
if (Connected == connected) return;
Connected = connected;
var handler = ClientState;
if (handler != null)
{
handler(this, connected);
}
}
/// <summary>
/// Occurs when a packet is received from the server.
/// </summary>
public event ClientReadEventHandler ClientRead;
/// <summary>
/// Represents a method that will handle a packet from the server.
/// </summary>
/// <param name="s">The client that has received the packet.</param>
/// <param name="packet">The packet that has been received by the server.</param>
public delegate void ClientReadEventHandler(Client s, IPacket packet);
/// <summary>
/// Fires an event that informs subscribers that a packet has been received by the server.
/// </summary>
/// <param name="packet">The packet that has been received by the server.</param>
private void OnClientRead(IPacket packet)
{
var handler = ClientRead;
if (handler != null)
{
handler(this, packet);
}
}
/// <summary>
/// Occurs when a packet is sent by the client.
/// </summary>
public event ClientWriteEventHandler ClientWrite;
/// <summary>
/// Represents the method that will handle the sent packet.
/// </summary>
/// <param name="s">The client that has sent the packet.</param>
/// <param name="packet">The packet that has been sent by the client.</param>
/// <param name="length">The length of the packet.</param>
/// <param name="rawData">The packet in raw bytes.</param>
public delegate void ClientWriteEventHandler(Client s, IPacket packet, long length, byte[] rawData);
/// <summary>
/// Fires an event that informs subscribers that the client has sent a packet.
/// </summary>
/// <param name="packet">The packet that has been sent by the client.</param>
/// <param name="length">The length of the packet.</param>
/// <param name="rawData">The packet in raw bytes.</param>
private void OnClientWrite(IPacket packet, long length, byte[] rawData)
{
var handler = ClientWrite;
if (handler != null)
{
handler(this, packet, length, rawData);
}
}
/// <summary>
/// The type of the packet received.
/// </summary>
public enum ReceiveType
{
Header,
Payload
}
/// <summary>
/// The buffer size for receiving data in bytes.
/// </summary>
public int BUFFER_SIZE { get { return 1024 * 16; } } // 16KB
/// <summary>
/// The keep-alive time in ms.
/// </summary>
public uint KEEP_ALIVE_TIME { get { return 25000; } } // 25s
/// <summary>
/// The keep-alive interval in ms.
/// </summary>
public uint KEEP_ALIVE_INTERVAL { get { return 25000; } } // 25s
/// <summary>
/// The header size in bytes.
/// </summary>
public int HEADER_SIZE { get { return 4; } } // 4B
/// <summary>
/// The maximum size of a packet in bytes.
/// </summary>
public int MAX_PACKET_SIZE { get { return (1024 * 1024) * 5; } } // 5MB
/// <summary>
/// Returns an array containing all of the proxy clients of this client.
/// </summary>
public ReverseProxyClient[] ProxyClients
{
get
{
lock (_proxyClientsLock)
{
return _proxyClients.ToArray();
}
}
}
/// <summary>
/// Handle of the Client Socket.
/// </summary>
private Socket _handle;
/// <summary>
/// A list of all the connected proxy clients that this client holds.
/// </summary>
private List<ReverseProxyClient> _proxyClients;
/// <summary>
/// Lock object for the list of proxy clients.
/// </summary>
private readonly object _proxyClientsLock = new object();
/// <summary>
/// The buffer for incoming packets.
/// </summary>
private byte[] _readBuffer;
/// <summary>
/// The buffer for the client's incoming payload.
/// </summary>
private byte[] _payloadBuffer;
/// <summary>
/// The Queue which holds buffers to send.
/// </summary>
private readonly Queue<byte[]> _sendBuffers = new Queue<byte[]>();
/// <summary>
/// Determines if the client is currently sending packets.
/// </summary>
private bool _sendingPackets;
/// <summary>
/// Lock object for the sending packets boolean.
/// </summary>
private readonly object _sendingPacketsLock = new object();
/// <summary>
/// The Queue which holds buffers to read.
/// </summary>
private readonly Queue<byte[]> _readBuffers = new Queue<byte[]>();
/// <summary>
/// Determines if the client is currently reading packets.
/// </summary>
private bool _readingPackets;
/// <summary>
/// Lock object for the reading packets boolean.
/// </summary>
private readonly object _readingPacketsLock = new object();
/// <summary>
/// The temporary header to store parts of the header.
/// </summary>
/// <remarks>
/// This temporary header is used when we have i.e.
/// only 2 bytes left to read from the buffer but need more
/// which can only be read in the next Receive callback
/// </remarks>
private byte[] _tempHeader;
/// <summary>
/// Decides if we need to append bytes to the header.
/// </summary>
private bool _appendHeader;
// Receive info
private int _readOffset;
private int _writeOffset;
private int _tempHeaderOffset;
private int _readableDataLen;
private int _payloadLen;
private ReceiveType _receiveState = ReceiveType.Header;
/// <summary>
/// Gets if the client is currently connected to a server.
/// </summary>
public bool Connected { get; private set; }
/// <summary>
/// The packet serializer.
/// </summary>
protected Serializer Serializer { get; set; }
private const bool encryptionEnabled = true;
private const bool compressionEnabled = true;
protected Client()
{
_proxyClients = new List<ReverseProxyClient>();
_readBuffer = new byte[BUFFER_SIZE];
_tempHeader = new byte[HEADER_SIZE];
}
/// <summary>
/// Attempts to connect to the specified ip address on the specified port.
/// </summary>
/// <param name="ip">The ip address to connect to.</param>
/// <param name="port">The port of the host.</param>
protected void Connect(IPAddress ip, ushort port)
{
try
{
Disconnect();
_handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
_handle.Connect(ip, port);
if (_handle.Connected)
{
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
OnClientState(true);
}
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
private void AsyncReceive(IAsyncResult result)
{
int bytesTransferred; // error occurs here
try
{
bytesTransferred = _handle.EndReceive(result);
if (bytesTransferred <= 0)
throw new Exception("no bytes transferred");
}
catch (NullReferenceException)
{
return;
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception)
{
Disconnect();
return;
}
byte[] received = new byte[bytesTransferred];
try
{
Array.Copy(_readBuffer, received, received.Length);
}
catch (Exception ex)
{
OnClientFail(ex);
return;
}
lock (_readBuffers)
{
_readBuffers.Enqueue(received);
}
lock (_readingPacketsLock)
{
if (!_readingPackets)
{
_readingPackets = true;
ThreadPool.QueueUserWorkItem(AsyncReceive);
}
}
try
{
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
private void AsyncReceive(object state)
{
while (true)
{
byte[] readBuffer;
lock (_readBuffers)
{
if (_readBuffers.Count == 0)
{
lock (_readingPacketsLock)
{
_readingPackets = false;
}
return;
}
readBuffer = _readBuffers.Dequeue();
}
_readableDataLen += readBuffer.Length;
bool process = true;
while (process)
{
switch (_receiveState)
{
case ReceiveType.Header:
{
if (_readableDataLen + _tempHeaderOffset >= HEADER_SIZE)
{ // we can read the header
int headerLength = (_appendHeader)
? HEADER_SIZE - _tempHeaderOffset
: HEADER_SIZE;
try
{
if (_appendHeader)
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset,
headerLength);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_payloadLen = BitConverter.ToInt32(_tempHeader, 0);
_tempHeaderOffset = 0;
_appendHeader = false;
}
else
{
_payloadLen = BitConverter.ToInt32(readBuffer, _readOffset);
}
if (_payloadLen <= 0 || _payloadLen > MAX_PACKET_SIZE)
throw new Exception("invalid header");
}
catch (Exception)
{
process = false;
Disconnect();
break;
}
_readableDataLen -= headerLength;
_readOffset += headerLength;
_receiveState = ReceiveType.Payload;
}
else // _readableDataLen < HEADER_SIZE
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_tempHeaderOffset += _readableDataLen;
_appendHeader = true;
process = false;
}
break;
}
case ReceiveType.Payload:
{
if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen)
_payloadBuffer = new byte[_payloadLen];
int length = (_writeOffset + _readableDataLen >= _payloadLen)
? _payloadLen - _writeOffset
: _readableDataLen;
try
{
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_writeOffset += length;
_readOffset += length;
_readableDataLen -= length;
if (_writeOffset == _payloadLen)
{
bool isError = _payloadBuffer.Length == 0;
if (!isError)
{
if (encryptionEnabled)
_payloadBuffer = AES.Decrypt(_payloadBuffer);
isError = _payloadBuffer.Length == 0; // check if payload decryption failed
}
if (!isError)
{
if (compressionEnabled)
{
try
{
_payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer);
}
catch (Exception)
{
process = false;
Disconnect();
break;
}
}
isError = _payloadBuffer.Length == 0; // check if payload decompression failed
}
if (isError)
{
process = false;
Disconnect();
break;
}
using (MemoryStream deserialized = new MemoryStream(_payloadBuffer))
{
try
{
IPacket packet = (IPacket)Serializer.Deserialize(deserialized);
OnClientRead(packet);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
}
_receiveState = ReceiveType.Header;
_payloadBuffer = null;
_payloadLen = 0;
_writeOffset = 0;
}
if (_readableDataLen == 0)
process = false;
break;
}
}
}
if (_receiveState == ReceiveType.Header)
{
_writeOffset = 0; // prepare for next packet
}
_readOffset = 0;
_readableDataLen = 0;
}
}
/// <summary>
/// Sends a packet to the connected server.
/// </summary>
/// <typeparam name="T">The type of the packet.</typeparam>
/// <param name="packet">The packet to be send.</param>
public void Send<T>(T packet) where T : IPacket
{
if (!Connected || packet == null) return;
lock (_sendBuffers)
{
using (MemoryStream ms = new MemoryStream())
{
try
{
Serializer.Serialize(ms, packet);
}
catch (Exception ex)
{
OnClientFail(ex);
return;
}
byte[] payload = ms.ToArray();
_sendBuffers.Enqueue(payload);
OnClientWrite(packet, payload.LongLength, payload);
lock (_sendingPacketsLock)
{
if (_sendingPackets) return;
_sendingPackets = true;
}
ThreadPool.QueueUserWorkItem(Send);
}
}
}
/// <summary>
/// Sends a packet to the connected server.
/// Blocks the thread until all packets have been sent.
/// </summary>
/// <typeparam name="T">The type of the packet.</typeparam>
/// <param name="packet">The packet to be send.</param>
public void SendBlocking<T>(T packet) where T : IPacket
{
Send(packet);
while (_sendingPackets)
{
Thread.Sleep(10);
}
}
private void Send(object state)
{
while (true)
{
if (!Connected)
{
SendCleanup(true);
return;
}
byte[] payload;
lock (_sendBuffers)
{
if (_sendBuffers.Count == 0)
{
SendCleanup();
return;
}
payload = _sendBuffers.Dequeue();
}
try
{
_handle.Send(BuildPacket(payload));
}
catch (Exception ex)
{
OnClientFail(ex);
SendCleanup(true);
return;
}
}
}
private byte[] BuildPacket(byte[] payload)
{
if (compressionEnabled)
payload = SafeQuickLZ.Compress(payload);
if (encryptionEnabled)
payload = AES.Encrypt(payload);
byte[] packet = new byte[payload.Length + HEADER_SIZE];
Array.Copy(BitConverter.GetBytes(payload.Length), packet, HEADER_SIZE);
Array.Copy(payload, 0, packet, HEADER_SIZE, payload.Length);
return packet;
}
private void SendCleanup(bool clear = false)
{
lock (_sendingPacketsLock)
{
_sendingPackets = false;
}
if (!clear) return;
lock (_sendBuffers)
{
_sendBuffers.Clear();
}
}
/// <summary>
/// Disconnect the client from the server, disconnect all proxies that
/// are held by this client, and dispose of other resources associated
/// with this client.
/// </summary>
public void Disconnect()
{
if (_handle != null)
{
_handle.Close();
_handle = null;
_readOffset = 0;
_writeOffset = 0;
_tempHeaderOffset = 0;
_readableDataLen = 0;
_payloadLen = 0;
_payloadBuffer = null;
_receiveState = ReceiveType.Header;
if (_proxyClients != null)
{
lock (_proxyClientsLock)
{
try
{
foreach (ReverseProxyClient proxy in _proxyClients)
proxy.Disconnect();
}
catch (Exception)
{
}
}
}
if (Commands.CommandHandler.StreamCodec != null)
{
Commands.CommandHandler.StreamCodec.Dispose();
Commands.CommandHandler.StreamCodec = null;
}
}
OnClientState(false);
}
public void ConnectReverseProxy(ReverseProxyConnect command)
{
lock (_proxyClientsLock)
{
_proxyClients.Add(new ReverseProxyClient(command, this));
}
}
public ReverseProxyClient GetReverseProxyByConnectionId(int connectionId)
{
lock (_proxyClientsLock)
{
return _proxyClients.FirstOrDefault(t => t.ConnectionId == connectionId);
}
}
public void RemoveProxyClient(int connectionId)
{
try
{
lock (_proxyClientsLock)
{
for (int i = 0; i < _proxyClients.Count; i++)
{
if (_proxyClients[i].ConnectionId == connectionId)
{
_proxyClients.RemoveAt(i);
break;
}
}
}
}
catch { }
}
}
The output from visual studio:
Exception thrown: 'System.Net.Sockets.SocketException' in System.dll
Client Fail - Exception Message: No connection could be made because the target machine actively refused it 127.0.0.1:4782
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Client Fail - Exception Message: Object reference not set to an instance of an object.
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.NullReferenceException' in Client.exe
The refuse was because the server wasnt running, when I start the server and it connects the crsahing loop begins
Thanks in advance
Found the error, the connection thread on the client had an empty ipadress string in the code and had the hostname it used didnt exist, I should've looked better at all code. Thanks
I have a simple requirement of moving files from source folder to destination folder. The files are about 5mb in size and arrive three at a time (every 5 seconds).
The current mechanism I have in place seems to move files however if the destination folder is not accessible for a few seconds, the files to process from the source directory does not queue up but gets missed.
My question is how do I create a queue with all the files which are created at source directory and move the files to destination? Do i need to use the background thread?
My watcher code looks like this.
public sealed class Watcher
{
int eventCount = 0;
#region Private Members
/// <summary>
/// File system watcher variable.
/// </summary>
private FileSystemWatcher fsw = null;
/// <summary>
/// Destination path to use.
/// </summary>
private string destination = #"c:\temp\doc2\";
/// <summary>
/// Source path to monitor.
/// </summary>
private string source = #"c:\temp\doc\";
/// <summary>
/// Default filter type is all files.
/// </summary>
private string filter = "*.bmp";
/// <summary>
/// Monitor all sub directories in the source folder.
/// </summary>
private bool includeSubdirectories = true;
/// <summary>
/// Background worker which will Move files.
/// </summary>
private BackgroundWorker bgWorker = null;
/// <summary>
/// Toggle flag to enable copying files and vice versa.
/// </summary>
private bool enableCopyingFiles = false;
/// <summary>
/// File System watcher lock.
/// </summary>
private object fswLock = new object();
private static Watcher watcherInstance;
#endregion
#region Public Properties
public static Watcher WatcherInstance
{
get
{
if (watcherInstance == null)
{
watcherInstance = new Watcher();
}
return watcherInstance;
}
}
public string Source
{
get
{
return source;
}
set
{
source = value;
}
}
public string Destination
{
get
{
return destination;
}
set
{
destination = value;
}
}
public string Filter
{
get
{
return filter;
}
set
{
filter = value;
}
}
public bool MonitorSubDirectories
{
get
{
return includeSubdirectories;
}
set
{
includeSubdirectories = value;
}
}
public bool EnableCopyingFiles
{
get
{
return enableCopyingFiles;
}
set
{
enableCopyingFiles = value;
}
}
public FileSystemWatcher FSW
{
get
{
return fsw;
}
set
{
fsw = value;
}
}
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
public Watcher()
{
// Intentionally left blank.
}
#endregion
#region Public Methods
/// <summary>
/// Method which will initialise the required
/// file system watcher objects to starting watching.
/// </summary>
public void InitialiseFSW()
{
fsw = new FileSystemWatcher();
bgWorker = new BackgroundWorker();
}
/// <summary>
/// Method which will start watching.
/// </summary>
public void StartWatch()
{
if (fsw != null)
{
fsw.Path = source;
fsw.Filter = filter;
fsw.IncludeSubdirectories = includeSubdirectories;
fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess;
// Setup events.
fsw.Created += fsw_Created;
// Important to set the below otherwise no events will be raised.
fsw.EnableRaisingEvents = enableCopyingFiles;
bgWorker.DoWork += bgWorker_DoWork;
}
else
{
Trace.WriteLine("File System Watcher is not initialised. Setting ISS Fault Alarm Bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
}
}
/// <summary>
/// Method to stop watch.
/// </summary>
public void StopWatch()
{
// Stop Watcher.
if (bgWorker != null)
{
bgWorker.DoWork -= bgWorker_DoWork;
}
if (fsw != null)
{
fsw.Created -= fsw_Created;
}
}
#endregion
#region Private Methods
/// <summary>
/// Method which will do the work on the background thread.
/// Currently Move files from source to destination and
/// monitor disk capacity.
/// </summary>
/// <param name="sender">Object Sender</param>
/// <param name="e">Event Arguments</param>
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Trace.WriteLine("ZZZZZ..Event Count:" + eventCount.ToString());
// Extract the file names form the arguments.
FileSystemEventArgs fsea = (FileSystemEventArgs)e.Argument;
// Form the correct filename.
// An assumption has been made that there will always be an '&' symbol in the filename.
// An assumption has been made that the batch code will be added before the first '&' symbol.
string newFileName = string.Empty;
//// First character we are looking for has been found.
//// Sanity checks.
//if (CommonActions.ExtDictionary != null)
//{
// // Add the batch Code.
// // newFileName = fnParts[i] + "_" + CommonActions.ExtDictionary["BatchID"] + "_" + "&";
// // Add the batch code before the filename for easy sorting in windows explorer.
// newFileName = CommonActions.ExtDictionary["BatchID"] + "_" + fsea.Name;
//}
//else
//{
// Batch Code not found. So prefix with hardcoded text.
newFileName = "BatchCode" + "_" + fsea.Name;
//newFileName = fsea.Name;
//}
// We should now have the fully formed filename now.
// Move the file to the new location
string destPath = destination + #"\" + newFileName;
var fi = new FileInfo(fsea.FullPath);
// TODO Check if the file exist.
if (File.Exists(Path.Combine(Source, fsea.Name)))
{
// Check if the file is accessiable.
if (IsAccessible(fi, FileMode.Open, FileAccess.Read))
{
if (!File.Exists(destPath))
{
try
{
// Copy the file.
//File.Copy(fsea.FullPath, destPath);
// Move the file.
//File.Move(fsea.FullPath, destPath);
File.Copy(fsea.FullPath, destPath);
File.SetAttributes(destPath, FileAttributes.ReadOnly);
//Stopwatch sWatch = Stopwatch.StartNew();
//TimeSpan fileDropTimeout = new TimeSpan(0, 0, 10);
//bool fileActionSuccess = false;
//do
//{
// // Copy the file.
// //File.Copy(fsea.FullPath, destPath);
// // Move the file.
// //File.Move(fsea.FullPath, destPath);
// File.Copy(fsea.FullPath, destPath);
// File.SetAttributes(destPath, FileAttributes.ReadOnly);
// fileActionSuccess = true;
//} while (sWatch.Elapsed < fileDropTimeout);
//if(!fileActionSuccess)
//{
// Trace.WriteLine("File Move or File Attribute settings failed.");
// throw new Exception();
//}
// Wait before checking for the file exists at destination.
Thread.Sleep(Convert.ToInt32(ConfigurationManager.AppSettings["ExistsAtDestination"]));
// Check if the file has actually been moved to dest.
if (!File.Exists(destPath))
{
// TODO Raise alarms here.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS File Move Fault Alarm bit");
}
}
catch (Exception ex)
{
// TODO log the exception and Raise alarm?
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS File Move Fault Alarm bit");
}
}
else
{
Trace.WriteLine("File Move failed as the file: " + newFileName + " already exists in the destination folder");
}
}
else
{
Trace.WriteLine("File Move failed. File is not accessible");
}
}
}
/// <summary>
/// Event which is raised when a file is created in the folder which is being watched.
/// </summary>
/// <param name="sender">Object sender</param>
/// <param name="e">Event arguments</param>
private void fsw_Created(object sender, FileSystemEventArgs e)
{
lock (fswLock)
{
eventCount++;
// Start the background worker.
// Check whether if the background worker is busy if not continue.
if (!bgWorker.IsBusy)
{
bgWorker.RunWorkerAsync(e);
}
else
{
Trace.WriteLine("An attempt to use background worker for concurrent tasks has been encountered ");
// Worker thread is busy.
int busyCount = 0;
while (busyCount < 4)
{
// Wait for 500ms and try again.
Thread.Sleep(500);
if (!bgWorker.IsBusy)
{
bgWorker.RunWorkerAsync(e);
break;
}
else
{
Trace.WriteLine("An attempt to use background worker for concurrent tasks has been encountered, attempt " + busyCount);
busyCount++;
}
}
}
}
}
/// <summary>
/// Extension method to check if a file is accessible.
/// </summary>
/// <param name="fi">File Info</param>
/// <param name="mode">File Mode</param>
/// <param name="access">File Access</param>
/// <returns>Attempts three times. True if the file is accessible</returns>
private bool IsAccessible(FileInfo fi, FileMode mode, FileAccess access)
{
bool hasAccess = false;
int i = 0;
while (!hasAccess)
{
try
{
using (var fileStream = File.Open(fi.FullName, mode, access))
{
}
hasAccess = true;
i = 1;
break;
}
catch (Exception ex)
{
if (i < 4)
{
// We will swallow the exception, wait and try again.
Thread.Sleep(500);
// Explicitly set hasaccess flag.
hasAccess = false;
i++;
}
else
{
i = 0;
Trace.WriteLine("Failed to Move. File is not accessable. " + ex.ToString());
// Notify HMI
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS File Move Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFileCopyFault);
// Explicitly set hasaccess flag.
hasAccess = false;
break;
}
}
}
// return hasAccess;
return true;
}
#endregion
}
I will start watching for file as below
private void CopyFiles(bool enableCopy)
{
if (enableCopy)
{
// Initialise watcher.
Watcher.WatcherInstance.InitialiseFSW();
// Set Source.
Watcher.WatcherInstance.Source = ConfigurationManager.AppSettings["Source"];
// Set Destination.
Watcher.WatcherInstance.Destination = ConfigurationManager.AppSettings["Destination"];
//Trace.WriteLine("FTP Destination is set to:" + Watcher.WatcherInstance.Destination);
// Set Filter
Watcher.WatcherInstance.Filter = "*.bmp";
// Watch subdirectories?
Watcher.WatcherInstance.MonitorSubDirectories = true;
// Enable events.
Watcher.WatcherInstance.EnableCopyingFiles = enableCopy;
// Start Watch.
Watcher.WatcherInstance.StartWatch();
}
else
{
//if (wchr != null && wchr.FSW != null)
//{
// Stop Watcher.
Watcher.WatcherInstance.StopWatch();
// Stop copying files. As the batch is stopped.
Watcher.WatcherInstance.FSW.EnableRaisingEvents = enableCopy;
//}
}
}
These are my findings and finally seem to move files without missing any files.
Background worker is not the correct thing to use
Create a task for each file and let it do its thing.
The updated watcher class looks like this.
public sealed class Watcher
{
int eventCount = 0;
#region Private Members
/// <summary>
/// File system watcher variable.
/// </summary>
private FileSystemWatcher fsw = null;
/// <summary>
/// Destination path to use.
/// </summary>
private string destination = #"c:\temp\doc2\";
/// <summary>
/// Source path to monitor.
/// </summary>
private string source = #"c:\temp\doc\";
/// <summary>
/// Default filter type is all files.
/// </summary>
private string filter = "*.bmp";
/// <summary>
/// Monitor all sub directories in the source folder.
/// </summary>
private bool includeSubdirectories = true;
/// <summary>
/// Background worker which will Move files.
/// </summary>
private BackgroundWorker bgWorker = null;
/// <summary>
/// Toggle flag to enable copying files and vice versa.
/// </summary>
private bool enableCopyingFiles = false;
/// <summary>
/// File System watcher lock.
/// </summary>
private object fswLock = new object();
private static Watcher watcherInstance;
#endregion
#region Public Properties
public static Watcher WatcherInstance
{
get
{
if (watcherInstance == null)
{
watcherInstance = new Watcher();
}
return watcherInstance;
}
}
public string Source
{
get
{
return source;
}
set
{
source = value;
}
}
public string Destination
{
get
{
return destination;
}
set
{
destination = value;
}
}
public string Filter
{
get
{
return filter;
}
set
{
filter = value;
}
}
public bool MonitorSubDirectories
{
get
{
return includeSubdirectories;
}
set
{
includeSubdirectories = value;
}
}
public bool EnableCopyingFiles
{
get
{
return enableCopyingFiles;
}
set
{
enableCopyingFiles = value;
}
}
public FileSystemWatcher FSW
{
get
{
return fsw;
}
set
{
fsw = value;
}
}
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
public Watcher()
{
// Intentionally left blank.
}
#endregion
#region Public Methods
/// <summary>
/// Method which will initialise the required
/// file system watcher objects to starting watching.
/// </summary>
public void InitialiseFSW()
{
fsw = new FileSystemWatcher();
bgWorker = new BackgroundWorker();
}
/// <summary>
/// Method which will start watching.
/// </summary>
public void StartWatch()
{
if (fsw != null)
{
fsw.Path = source;
fsw.Filter = filter;
fsw.IncludeSubdirectories = includeSubdirectories;
fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess;
// Setup events.
fsw.Created += fsw_Created;
// Important to set the below otherwise no events will be raised.
fsw.EnableRaisingEvents = enableCopyingFiles;
bgWorker.DoWork += bgWorker_DoWork;
}
else
{
Trace.WriteLine("File System Watcher is not initialised. Setting ISS Fault Alarm Bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
}
}
/// <summary>
/// Method to stop watch.
/// </summary>
public void StopWatch()
{
// Stop Watcher.
if (bgWorker != null)
{
bgWorker.DoWork -= bgWorker_DoWork;
}
if (fsw != null)
{
fsw.Created -= fsw_Created;
}
}
#endregion
#region Private Methods
/// <summary>
/// Method which will do the work on the background thread.
/// Currently Move files from source to destination and
/// monitor disk capacity.
/// </summary>
/// <param name="sender">Object Sender</param>
/// <param name="e">Event Arguments</param>
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
}
private void PerformFileActions(string sourcePath)
{
string extractedFileName = Path.GetFileName(sourcePath);
string newFileName = string.Empty;
newFileName = "BatchCode" + "_" + extractedFileName;
// We should now have the fully formed filename now.
// Move the file to the new location
string destPath = destination + #"\" + newFileName;
var fi = new FileInfo(sourcePath);
// TODO Check if the file exist.
if (File.Exists(Path.Combine(Source, extractedFileName)))
{
// Check if the file is accessiable.
if (IsAccessible(fi, FileMode.Open, FileAccess.Read))
{
if (!File.Exists(destPath))
{
try
{
File.Copy(sourcePath, destPath);
File.SetAttributes(destPath, FileAttributes.ReadOnly);
// Wait before checking for the file exists at destination.
Thread.Sleep(Convert.ToInt32(ConfigurationManager.AppSettings["ExistsAtDestination"]));
// Check if the file has actually been moved to dest.
if (!File.Exists(destPath))
{
// TODO Raise alarms here.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS File Move Fault Alarm bit");
}
}
catch (Exception ex)
{
// TODO log the exception and Raise alarm?
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS File Move Fault Alarm bit");
}
}
else
{
Trace.WriteLine("File Move failed as the file: " + newFileName + " already exists in the destination folder");
}
}
else
{
Trace.WriteLine("File Move failed. File is not accessible");
}
}
}
/// <summary>
/// Event which is raised when a file is created in the folder which is being watched.
/// </summary>
/// <param name="sender">Object sender</param>
/// <param name="e">Event arguments</param>
private void fsw_Created(object sender, FileSystemEventArgs e)
{
lock (fswLock)
{
DateTime lastRead = DateTime.MinValue;
DateTime lastWriteTime = File.GetCreationTime(e.FullPath);
if (lastWriteTime != lastRead)
{
eventCount++;
// Start a new task and forget.
Task.Factory.StartNew(() => {
PerformFileActions(e.FullPath);
});
lastRead = lastWriteTime;
}
}
}
/// <summary>
/// Extension method to check if a file is accessible.
/// </summary>
/// <param name="fi">File Info</param>
/// <param name="mode">File Mode</param>
/// <param name="access">File Access</param>
/// <returns>Attempts three times. True if the file is accessible</returns>
private bool IsAccessible(FileInfo fi, FileMode mode, FileAccess access)
{
bool hasAccess = false;
int i = 0;
while (!hasAccess)
{
try
{
using (var fileStream = File.Open(fi.FullName, mode, access))
{
}
hasAccess = true;
i = 1;
break;
}
catch (Exception ex)
{
if (i < 4)
{
// We will swallow the exception, wait and try again.
Thread.Sleep(500);
// Explicitly set hasaccess flag.
hasAccess = false;
i++;
}
else
{
i = 0;
Trace.WriteLine("Failed to Move. File is not accessable. " + ex.ToString());
// Notify HMI
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS File Move Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFileCopyFault);
// Explicitly set hasaccess flag.
hasAccess = false;
break;
}
}
}
// return hasAccess;
return true;
}
#endregion
}
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 8 years ago.
I have a weird problem that i can't solve, i have a form that i open within another form, as soon as i open that form and since there is no event to fire after page finish loading, at the form load event i set a Timer that will start after 5sec. The timer will trigger a Task that will download files, downloading files is updated in a progressbar. The problem is that anything i try to change while Tasks are running doesn't update the GUI and will only change GUI after all Tasks finishes, note that the progressbar gets updated fine. Here is my code:
private void frm_HosterDownloader_Load(object sender, EventArgs e)
{
StartDownloadTimer = new Timer();
StartDownloadTimer.Tick += StartDownloadTimer_Tick;
StartDownloadTimer.Interval = 5000;
StartDownloadTimer.Start();
}
void StartDownloadTimer_Tick(object sender, EventArgs e)
{
StartDownload();
StartDownloadTimer.Stop();
}
private void StartDownload()
{
int counter = 0;
Dictionary<string, string> hosters = Hosters.GetAllHostersUrls();
progressBar_Download.Maximum = hosters.Count * 100;
progressBar_Download.Minimum = 0;
progressBar_Download.Value = 0;
foreach (KeyValuePair<string, string> host in hosters)
{
//Updating these tow lables never works, only when everything finishes
lbl_FileName.Text = host.Key;
lbl_NumberOfDownloads.Text = (++counter).ToString() + "/" + hosters.Count().ToString();
Task downloadTask = new Task(() =>
{
Downloader downloader = new Downloader(host.Value, string.Format(HostersImagesFolder + #"\{0}.png", IllegalChars(host.Key)));
downloader.HosterName = host.Key;
downloader.DownloadFinished += downloader_DownloadFinished;
downloader.Execute();
});
downloadTask.Start();
downloadTask.Wait();
}
}
void downloader_DownloadFinished(object sender, ProgressEventArgs e)
{
progressBar_Download.Value = progressBar_Download.Value + (int)e.ProgressPercentage;
}
I tired putting the tow label statments within the Task and even tried to pass them as an argument to be updated in the DownloadFinish event but no luck.
Edit:
Here is the Downloader Class:
public class Downloader : DownloaderBase
{
public string HosterName { set; get; }
/// <summary>
/// Initializes a new instance of the <see cref="Downloader"/> class.
/// </summary>
/// <param name="hoster">The hoster to download.</param>
/// <param name="savePath">The path to save the video.</param>
/// <param name="bytesToDownload">An optional value to limit the number of bytes to download.</param>
/// <exception cref="ArgumentNullException"><paramref name="video"/> or <paramref name="savePath"/> is <c>null</c>.</exception>
public Downloader(string hosterUrl, string savePath, int? bytesToDownload = null)
: base(hosterUrl, savePath, bytesToDownload)
{ }
/// <summary>
/// Occurs when the downlaod progress of the file file has changed.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadProgressChanged;
/// <summary>
/// Starts download.
/// </summary>
/// <exception cref="IOException">The video file could not be saved.</exception>
/// <exception cref="WebException">An error occured while downloading the video.</exception>
public override void Execute()
{
this.OnDownloadStarted(new ProgressEventArgs(0, HosterName));
var request = (HttpWebRequest)WebRequest.Create(this.HosterUrl);
if (this.BytesToDownload.HasValue)
{
request.AddRange(0, this.BytesToDownload.Value - 1);
}
try
{
// the following code is alternative, you may implement the function after your needs
request.Timeout = 100000;
request.ReadWriteTimeout = 100000;
request.ContinueTimeout = 100000;
using (WebResponse response = request.GetResponse())
{
using (Stream source = response.GetResponseStream())
{
using (FileStream target = File.Open(this.SavePath, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = source.Read(buffer, 0, buffer.Length)) > 0)
{
target.Write(buffer, 0, bytes);
copiedBytes += bytes;
var eventArgs = new ProgressEventArgs((copiedBytes * 1.0 / response.ContentLength) * 100, HosterName);
if (this.DownloadProgressChanged != null)
{
this.DownloadProgressChanged(this, eventArgs);
if (eventArgs.Cancel)
{
cancel = true;
}
}
}
}
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
Execute();
}
this.OnDownloadFinished(new ProgressEventArgs(100, HosterName));
}
}
public abstract class DownloaderBase
{
/// <summary>
/// Initializes a new instance of the <see cref="DownloaderBase"/> class.
/// </summary>
/// <param name="hosterUrl">The video to download/convert.</param>
/// <param name="savePath">The path to save the video/audio.</param>
/// /// <param name="bytesToDownload">An optional value to limit the number of bytes to download.</param>
/// <exception cref="ArgumentNullException"><paramref name="hosterUrl"/> or <paramref name="savePath"/> is <c>null</c>.</exception>
protected DownloaderBase(string hosterUrl, string savePath, int? bytesToDownload = null)
{
if (hosterUrl == null)
throw new ArgumentNullException("video");
if (savePath == null)
throw new ArgumentNullException("savePath");
this.HosterUrl = hosterUrl;
this.SavePath = savePath;
this.BytesToDownload = bytesToDownload;
}
/// <summary>
/// Occurs when the download finished.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadFinished;
/// <summary>
/// Occurs when the download is starts.
/// </summary>
public event EventHandler<ProgressEventArgs> DownloadStarted;
/// <summary>
/// Gets the number of bytes to download. <c>null</c>, if everything is downloaded.
/// </summary>
public string HosterUrl { get; set; }
/// <summary>
/// Gets the number of bytes to download. <c>null</c>, if everything is downloaded.
/// </summary>
public int? BytesToDownload { get; private set; }
/// <summary>
/// Gets the path to save the video/audio.
/// </summary>
public string SavePath { get; private set; }
/// <summary>
/// Starts the work of the <see cref="DownloaderBase"/>.
/// </summary>
public abstract void Execute();
protected void OnDownloadFinished(ProgressEventArgs e)
{
if (this.DownloadFinished != null)
{
this.DownloadFinished(this, e);
}
}
protected void OnDownloadStarted(ProgressEventArgs e)
{
if (this.DownloadStarted != null)
{
this.DownloadStarted(this, e);
}
}
}
It is not useful to use a Task this way:
downloadTask.Start();
downloadTask.Wait();
The Wait() will block the calling code and that is handling an event. Your downloads are effectively executing on the main GUI thread, blocking it.
The solution is
//downloadTask.Wait();
You don't seem to need it.
There is rarely a good reason to use threads (either new ones you create or threadpool) to do IO bound work. Here is an async alternative to your synchronous Execute method:
public async Task ExecuteAsync()
{
this.OnDownloadStarted(new ProgressEventArgs(0, HosterName));
var httpClient = new HttpClient();
var request = (HttpWebRequest)WebRequest.Create(this.HosterUrl);
if (this.BytesToDownload.HasValue)
{
request.AddRange(0, this.BytesToDownload.Value - 1);
}
try
{
request.Timeout = 100000;
request.ReadWriteTimeout = 100000;
request.ContinueTimeout = 100000;
var response = await httpClient.SendAsync(request);
var responseStream = await response.Content.ReadAsStreamAsync();
using (FileStream target = File.Open(this.SavePath, FileMode.Create, FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await target.WriteAsync(buffer, 0, bytes);
copiedBytes += bytes;
var eventArgs = new ProgressEventArgs((copiedBytes * 1.0 / response.ContentLength) * 100, HosterName);
if (this.DownloadProgressChanged != null)
{
this.DownloadProgressChanged(this, eventArgs);
if (eventArgs.Cancel)
{
cancel = true;
}
}
}
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.Timeout)
}
this.OnDownloadFinished(new ProgressEventArgs(100, HosterName));
}
Now, there is no need to use Task.Wait or create a new Task. IO bound work is asynchronous by nature. In a combination with the new async-await keywords in C# 5, you can keep your UI responsive through the whole time, as each await yields control back to the calling method, and frees your winforms message pump to process more messasges in the meanwhile.
private async void frm_HosterDownloader_Load(object sender, EventArgs e)
{
await Task.Delay(5000);
await StartDownloadAsync();
}
private async Task StartDownloadAsync()
{
int counter = 0;
Dictionary<string, string> hosters = Hosters.GetAllHostersUrls();
progressBar_Download.Maximum = hosters.Count * 100;
progressBar_Download.Minimum = 0;
progressBar_Download.Value = 0;
var downloadTasks = hosters.Select(hoster =>
{
lbl_FileName.Text = hoster.Key;
lbl_NumberOfDownloads.Text = (++counter).ToString() + "/" + hosters.Count().ToString();
Downloader downloader = new Downloader(host.Value, string.Format(HostersImagesFolder + #"\{0}.png", IllegalChars(host.Key)));
downloader.HosterName = host.Key;
downloader.DownloadFinished += downloader_DownloadFinished;
return downloader.ExecuteAsync();
});
return Task.WhenAll(downloadTasks);
}
Note i changed your timer to a Task.Delay, since it internally uses a timer and you only need it to execute once.
If you want more on the use of async-await, you can start here.
My application has to writing received UDP data to disk continuously, and the speed is about 40M/s - 50M/s. My current logic is that all data is cached in a queue and Create a BinaryWriter to write queue data to local disk. It works well most time, but sometimes the writing speed will be very slow and causes cached data too large, then my application crushes.
Is there any method to improve writing file speed and keep a stable writing speed? Or i have to adjust my hardware?
Add more info:
The hareware could sustain my application write, I ran application for 1 hours many times, application only crushes occasionally.
This is my class to write file:
class BufferedFileStorageManager : IFileStorageManager
{
private BinaryWriter writer;
private ConcurrentQueue<byte[]> buffer;
private bool isWriting;
private bool isClosed;
public void Open(string filePath)
{
buffer = new ConcurrentQueue<byte[]>();
writer = new BinaryWriter(new FileStream(filePath, FileMode.Create));
isWriting = false;
isClosed = false;
}
/// <summary>
/// Writes the specified data syncronizly. Method will return after writing finished.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte[] data)
{
writer.Write(data);
}
/// <summary>
/// Write the data asyncronizly.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public async Task WriteAsync(byte[] data)
{
if (isClosed)
{
var closedTask = Task.Factory.StartNew(() => LogManager.Warn("File is closed, skip writing data."));
await closedTask;
}
else
{
buffer.Enqueue(data);
var writeTask = Task.Factory.StartNew(WriteInQueue);
await writeTask;
}
}
private void WriteInQueue()
{
if (isWriting)
return;
isWriting = true;
byte[] data;
while (buffer.TryDequeue(out data))
{
writer.Write(data);
}
isWriting = false;
}
/// <summary>
/// Close file. Method will return until all pending data is written to file
/// </summary>
public void Close()
{
WriteInQueue();
while (isWriting)
{
Thread.Sleep(1);
}
writer.Close();
}
public async Task CloseAsync()
{
isClosed = true;
var closeTask = new Task(Close);
closeTask.Start();
await closeTask;
writer.Dispose();
writer = null;
}
}
I have a small method I use to disable my socket that listens for incoming connections.
/// <summary>
/// Stops and disables the service
/// </summary>
public void Disable() {
if (Running) {
try {
thread.Abort();
}
catch (System.Threading.ThreadAbortException) {
// This catch is not raised.
// We can simply process our closing of the socket and nullify the thread
}
finally {
socket.Close();
socket = null;
thread = null;
if (socket == null && thread == null) {
m_Running = false;
OnDisabled(this, new EventArgs());
}
}
}
}
My problem is that even after I call Close() and nullify the socket, the clients still remain connected. I ran a check using netstat -a, and it shows the clients are still connected.
TCP 127.0.0.1:2161 activate:7777 ESTABLISHED
TCP 127.0.0.1:7777 activate:2161 ESTABLISHED
7777 is the port my host socket listens on. So my question is, after closing the host socket, why do the client sockets not disconnect. How do they remain connected to a socket that is null, and is no longer listenning?
Some additional info
/// <summary>
/// Enables and runs the service
/// </summary>
public void Enable() {
if (!Running) {
ThreadStart start = new ThreadStart(RunServiceAsync);
thread = new Thread(start);
thread.IsBackground = true;
thread.Start();
m_Running = true;
OnEnabled(this, new EventArgs());
}
}
The above method is how the thread is created. Everything works fine, the threading, connections; the only issue is when I close the socket (host), and nullify it the clients are still connected to it.
The question is, once the host socket is closed and set to null, what are the clients connected to? Shouldn't they disconnect and lose connection to the host, because the host socket is closed?
Here is the full code to help
// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// ***************************************
namespace CipherBox.Drivers {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.ComponentModel;
using CipherBox.Objects;
/// <summary>
/// Driver that manages the network connection between the master program and clients, also provides informational events
/// </summary>
public class NetworkDriver : IDriver {
#region Fields
private Socket socket;
private Thread thread;
#endregion
#region Properties
private int m_Port = 7777;
/// <summary>
/// Gets the port that the network runs on. The default port is 7777.
/// </summary>
public int Port {
get {
return m_Port;
}
}
#endregion
#region Events
/// <summary>
/// Delegate for when a node connects to the service
/// </summary>
public delegate void NodeConnectedEventHandler(object sender, NetworkNodeEventArgs e);
/// <summary>
/// Triggers when an node connects to the service
/// </summary>
public event NodeConnectedEventHandler NodeConnected;
/// <summary>
/// Event callback for NodeConnected
/// </summary>
private void OnNodeConnected(object sender, NetworkNodeEventArgs e) {
if (NodeConnected != null) {
foreach (NodeConnectedEventHandler handler in NodeConnected.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
NodeConnected(this, e);
}
}
}
}
/// <summary>
/// Delegate for when a node disconnects from the service
/// </summary>
public delegate void NodeDisconnectedEventHandler(object sender, NetworkNodeEventArgs e);
/// <summary>
/// Triggers when an node disconnects from the service
/// </summary>
public event NodeDisconnectedEventHandler NodeDisconnected;
/// <summary>
/// Event callback for NodeDisconnected
/// </summary>
private void OnNodeDisconnected(object sender, NetworkNodeEventArgs e) {
if (NodeDisconnected != null) {
foreach (NodeDisconnectedEventHandler handler in NodeDisconnected.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
NodeDisconnected(this, e);
}
}
}
}
#endregion
#region Methods
private NetworkNode FillNode(Socket socket) {
StringBuilder stream = new StringBuilder();
byte[] buffer = new byte[4096];
int bytesReceived = -1;
do {
try {
bytesReceived = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
}
catch (System.Net.Sockets.SocketException) {
return null;
}
finally {
stream.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
}
} while (!stream.ToString().EndsWith("\r\n\r\n"));
string[] packet = stream.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
if (packet.Length == 9) {
if (packet[0].ToLower() == "CipherBox".ToLower()) {
NetworkNode node = new NetworkNode();
node.Domain = packet[1];
node.LocalIP = IPAddress.Parse(packet[2]);
node.MachineName = packet[3];
node.Platform = packet[4];
node.RemoteIP = IPAddress.Parse(packet[5]);
node.Workgroup = packet[6];
node.Socket = socket;
return node;
}
else {
return null;
}
}
else {
return null;
}
}
private bool IsDisconnected(Socket socket) {
bool connected = false;
try {
connected = !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (System.Net.Sockets.SocketException) {
connected = false;
}
return !connected;
}
private void MonitorNode(NetworkNode node) {
ParameterizedThreadStart start = new ParameterizedThreadStart(MonitorNodeAsync);
Thread thread = new Thread(start);
thread.IsBackground = true;
thread.Start(node);
}
private void MonitorNodeAsync(object obj) {
NetworkNode node = obj as NetworkNode;
while (Running || node != null) {
if (IsDisconnected(node.Socket)) {
node.Socket.Shutdown(SocketShutdown.Both);
node.Socket.Close();
node.Socket = null;
OnNodeDisconnected(null, new NetworkNodeEventArgs(node));
return;
}
else {
Thread.Sleep(1000);
}
}
}
private void RunServiceAsync() {
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, Port);
socket.Bind(localEP);
socket.Listen(1);
do {
Socket client;
try {
client = socket.Accept();
}
catch (System.Net.Sockets.SocketException) {
continue;
}
NetworkNode node = FillNode(client);
if (node != null) {
OnNodeConnected(null, new NetworkNodeEventArgs(node));
MonitorNode(node);
}
} while (Running);
}
/// <summary>
/// Sets the port that the network runs on
/// </summary>
/// <param name="port">The port to set</param>
public void SetPort(int port) {
m_Port = port;
}
#endregion
#region IDriver Members
/// <summary>
/// Triggered when the network driver is disabled
/// </summary>
public event EventHandler<EventArgs> Disabled;
/// <summary>
/// Event callback for Disabled
/// </summary>
private void OnDisabled(object sender, System.EventArgs e) {
if (Disabled != null) {
foreach (EventHandler<EventArgs> handler in Disabled.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
Disabled(this, e);
}
}
}
}
/// <summary>
/// Triggered when the network driver is enabled
/// </summary>
public event EventHandler<EventArgs> Enabled;
/// <summary>
/// Event callback for Enabled
/// </summary>
private void OnEnabled(object sender, System.EventArgs e) {
if (Enabled != null) {
foreach (EventHandler<EventArgs> handler in Enabled.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
Enabled(this, e);
}
}
}
}
/// <summary>
/// Stops and disables the service
/// </summary>
public void Disable() {
if (Running) {
try {
thread.Abort();
}
catch (System.Threading.ThreadAbortException) {
// This catch is not raised.
// We can simply process our closing of the socket and nullify the thread
}
finally {
socket.Close();
socket = null;
thread = null;
if (socket == null && thread == null) {
m_Running = false;
OnDisabled(this, new EventArgs());
}
}
}
}
/// <summary>
/// Enables and runs the service
/// </summary>
public void Enable() {
if (!Running) {
ThreadStart start = new ThreadStart(RunServiceAsync);
thread = new Thread(start);
thread.IsBackground = true;
thread.Start();
m_Running = true;
OnEnabled(this, new EventArgs());
}
}
private bool m_Running = false;
/// <summary>
/// Gets a System.Boolean value indicating whether the service is running or not
/// </summary>
public bool Running {
get {
return m_Running;
}
}
#endregion
}
}
Yo have to call socket.shutdown(Both) the parameter could be Send, Receive or Both Depending of how you like to end the connection. This function send the necessary TCP message to the client to close the connection.
I corrected the issue by storing references to each connection in a collection and closing all the connections using the client sockets.
private void DestructConnections() {
foreach (Socket connection in connections) {
connection.Shutdown(SocketShutdown.Both);
connection.Close();
}
connections.Clear();
}
It is recommended that when using a connection oriented socket you should call Shutdown before you Close the connection. Shutdown is for notifying communication end. Close is for releasing the managed / unmanaged resources
Resource:
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown.aspx
When you work with sockets in any language, you create server socket which only listen and accept connections and there're many p2p sockets to send and/or receive data.
So your answer is design of sockets subsystem in Microsoft Windows, BSD, Linux, etc.