Parse shapefile (shp file) without shx and dbf files - c#

I need to upload a shapefile and store all shapes in database. I spent two days to finding a good library but so far I do not find any complete solution.
I try to parse the file with 'Catfood.Shapefile' library, but it needs two other file beside of .shp file which I do not have.
I manage to implement a code in Java but now I need to re-implement it in c#.
please give me some advice!

Ok, Shp file is a well design file which you can see the description here. the shx and dbf only add some help to parse shp file. I add a class into Catfood.Shapefile which its name is ShpParser. The input of this class is only Shp file. it does not need to shx and dbf file to extract the shapes in the file.
if the meta data is null, two classes are throw exception, make sure you remove metadata exceptions from following method:
1- Shape constructor
2- ShapeFactory.ParseShape
and here the code of ShpParser
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Text;
namespace Catfood.Shapefile
{
/// <summary>
///
/// </summary>
public class ShpParser : IDisposable, IEnumerator<Shape>, IEnumerable<Shape>
{
private bool _disposed;
private bool _opened;
private int _currentIndex;
private int _count;
private RectangleD _boundingBox;
private ShapeType _type;
private int _currentPosition;
private FileStream _mainStream;
private Header _mainHeader;
/// <summary>
///
/// </summary>
public ShpParser()
{
_currentIndex = -1;
_currentPosition = 0;
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
public ShpParser(string path)
: this()
{
Open(path);
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
private void Open(string path)
{
if (_disposed)
{
throw new ObjectDisposedException("ShpParser");
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
if (!File.Exists(path))
{
throw new FileNotFoundException("shp file not found", path);
}
_mainStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
if (_mainStream.Length < Header.HeaderLength)
{
throw new InvalidOperationException("Shapefile main file does not contain a valid header");
}
// read in and parse the headers
var headerBytes = new byte[Header.HeaderLength];
_mainStream.Read(headerBytes, 0, Header.HeaderLength);
_mainHeader = new Header(headerBytes);
_type = _mainHeader.ShapeType;
_boundingBox = new RectangleD(_mainHeader.XMin, _mainHeader.YMin, _mainHeader.XMax, _mainHeader.YMax);
_currentPosition = Header.HeaderLength;
_count = _mainHeader.FileLength * 2; // FileLength return the size of file in words (16 bytes)
_opened = true;
}
#region IDisposable Members
/// <summary>
/// Close the Shapefile. Equivalent to calling Dispose().
/// </summary>
public void Close()
{
Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool canDisposeManagedResources)
{
if (!_disposed)
{
if (canDisposeManagedResources)
{
if (_mainStream != null)
{
_mainStream.Close();
_mainStream = null;
}
}
_disposed = true;
_opened = false;
}
}
#endregion
public bool MoveNext()
{
if (_disposed) throw new ObjectDisposedException("ShpParser");
if (!_opened) throw new InvalidOperationException("ShpParser not open.");
if (_currentPosition < (_count - 1))
{
// try to read the next database record
return true;
}
else
{
// reached the last shape
return false;
}
}
public void Reset()
{
if (_disposed) throw new ObjectDisposedException("ShpParser");
if (!_opened) throw new InvalidOperationException("ShpParser not open.");
_currentIndex = -1;
}
public Shape Current
{
get
{
if (_disposed) throw new ObjectDisposedException("ShpParser");
if (!_opened) throw new InvalidOperationException("ShpParser not open.");
//get recordheader;
var recordheader = new byte[8];
_mainStream.Read(recordheader, 0, recordheader.Length);
int contentOffsetInWords = EndianBitConverter.ToInt32(recordheader, 0, ProvidedOrder.Big);
int contentLengthInWords = EndianBitConverter.ToInt32(recordheader, 4, ProvidedOrder.Big);
_mainStream.Seek(_currentPosition , SeekOrigin.Begin); // back to header of record
int bytesToRead = (contentLengthInWords * 2) + 8;
byte[] shapeData = new byte[bytesToRead];
_currentPosition += bytesToRead;
_mainStream.Read(shapeData, 0, bytesToRead);
return ShapeFactory.ParseShape(shapeData , null);
// return null;
}
}
object IEnumerator.Current
{
get
{
if (_disposed) throw new ObjectDisposedException("ShpParser");
if (!_opened) throw new InvalidOperationException("ShpParser not open.");
return this.Current;
}
}
public IEnumerator<Shape> GetEnumerator()
{
return (IEnumerator<Shape>)this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return (System.Collections.IEnumerator)this;
}
}
}
how you can use the ShpParser
using (var shapefile = new ShpParser("my.shp")
{
foreach (Shape shape in shapefile)
{
Console.WriteLine("ShapeType: {0}", shape.Type);
}
}

Related

Movie Streamer in unity3d continous repeat the image on screen c#

I try to get this work correct but wen i start to stream it contious repeat the image video.
I need it stream only one video.
The problem is wen i start to stream the video image it continous repeat the video, how can i stop the player to not repeat the image and only Get one.
This is what i get wen i start and i want to avoid the video to continuous repeating the video
I have edit and my script and add the lines of code
But It Continous Repeat The Video With Out Stop
This is my script
using UnityEngine;
using System;
using MP;
using MP.Net;
/// <summary>
/// Movie streamer
/// </summary>
public class MovieStreamer : MoviePlayerBase
{
#region ----- public ------
/// <summary>
/// Movie source url
/// </summary>
public string sourceUrl;
/// <summary>
/// Movie load options. The Load() methods on this component will use
/// this unless you're provinding your own.
/// </summary>
public LoadOptions loadOptions = LoadOptions.Default;
/// <summary>
/// Background thread status
/// </summary>
public string status;
public long bytesReceived;
public bool IsConnected
{
get {
return movie==null || movie.demux==null ? false : ((Streamer)movie.demux).IsConnected;
}
}
/// <summary>
/// Connects to an URL for streaming.
///
/// In case it fails, exception text is logged and FALSE is returned
/// </summary>
public bool Load (string srcUrl)
{
return Load (srcUrl, null);
}
public bool Load (string srcUrl, LoadOptions loadOptions)
{
this.sourceUrl = srcUrl;
if(loadOptions == null) {
loadOptions = this.loadOptions;
} else {
this.loadOptions = loadOptions;
}
try {
base.Load(new MovieSource() { url = srcUrl }, loadOptions);
return true;
}
catch (Exception e) {
if(loadOptions.enableExceptionThrow) {
throw e;
} else {
Debug.LogError (e);
return false;
}
}
}
[ContextMenu("Reconnect")]
public bool ReConnect ()
{
bool success = true;
if (!string.IsNullOrEmpty(sourceUrl)) {
success = Load (sourceUrl, loadOptions);
}
return success;
}
#endregion ------ / public ------
#region ----- private -----
private int lastVideoFrame = -1;
void Start ()
{
ReConnect ();
}
void OnGUI ()
{
if (!IsConnected || !movie.demux.hasVideo)
return;
// if we're playing the movie directly to screen, but don't
// show it before we've received at least one frame
if (drawToScreen && framebuffer != null && ((Streamer)movie.demux).VideoPosition > 0) {
DrawFramebufferToScreen ();
}
}
void Update ()
{
// get the thread status and write it here
if(movie != null && movie.demux != null)
{
if(movie.demux is HttpMjpegStreamer) {
status = ((HttpMjpegStreamer)movie.demux).Status;
bytesReceived = ((HttpMjpegStreamer)movie.demux).BytesReceived;
}
}
// if this.play changed, Play or Stop the movie
HandlePlayStop ();
// decode a frame when necessary
if(play) {
HandleFrameDecode ();
DrawFramebufferToScreen();
HandlePlayStop();
}
}
protected void HandleFrameDecode ()
{
if (!IsConnected || !movie.demux.hasVideo || movie.videoDecoder == null)
return;
// decode a frame if there's a new one available
if (movie.videoDecoder.Position != lastVideoFrame)
{
if(movie.videoDecoder.Position >= 0)
{
movie.videoDecoder.DecodeNext ();
// update the aspect ration of the video
movie.demux.videoStreamInfo.width = framebuffer.width;
movie.demux.videoStreamInfo.height = framebuffer.height;
}
lastVideoFrame = movie.videoDecoder.Position;
}
}
#endregion
}

xamarin.ios the mp3 stops plaiying when the screen switches off

i'm developing an app, and i need to play music from url.
i'm using this code, and it works fine.
But, while a song is playing, and the screen switches off, then the music stops. I need that the music continues playing until its end, even if the screen switches off.
i suppose i have to change this file, but i don't know how:
using System;
using AudioToolbox;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace SecondoSenso
{
/// <summary>
/// A Class to hold the AudioBuffer with all setting together
/// </summary>
internal class AudioBuffer
{
public IntPtr Buffer { get; set; }
public List<AudioStreamPacketDescription> PacketDescriptions { get; set; }
public int CurrentOffset { get; set; }
public bool IsInUse { get; set; }
}
/// <summary>
/// Wrapper around OutputQueue and AudioFileStream to allow streaming of various filetypes
/// </summary>
public class StreamingPlayback : IDisposable
{
public bool boolDispose = false;
public event EventHandler Finished;
public event Action<OutputAudioQueue> OutputReady;
// the AudioToolbox decoder
AudioFileStream fileStream;
int bufferSize = 128 * 256;
List<AudioBuffer> outputBuffers;
AudioBuffer currentBuffer;
// Maximum buffers
int maxBufferCount = 4;
// Keep track of all queued up buffers, so that we know that the playback finished
int queuedBufferCount = 0;
// Current Filestream Position - if we don't keep track we don't know when to push the last uncompleted buffer
long currentByteCount = 0;
//Used to trigger a dump of the last buffer.
bool lastPacket;
public OutputAudioQueue OutputQueue;
public bool Started { get; private set; }
public float Volume {
get {
return OutputQueue.Volume;
}
set {
OutputQueue.Volume = value;
}
}
/// <summary>
/// Defines the size forearch buffer, when using a slow source use more buffers with lower buffersizes
/// </summary>
public int BufferSize {
get {
return bufferSize;
}
set {
bufferSize = value;
}
}
/// <summary>
/// Defines the maximum Number of Buffers to use, the count can only change after Reset is called or the
/// StreamingPlayback is freshly instantiated
/// </summary>
public int MaxBufferCount {
get {
return maxBufferCount;
}
set {
maxBufferCount = value;
}
}
public StreamingPlayback () : this (AudioFileType.MP3)
{
}
public StreamingPlayback (AudioFileType type)
{
fileStream = new AudioFileStream (type);
fileStream.PacketDecoded += AudioPacketDecoded;
fileStream.PropertyFound += AudioPropertyFound;
}
public void Reset ()
{
if (fileStream != null) {
fileStream.Close ();
fileStream = new AudioFileStream (AudioFileType.MP3);
currentByteCount = 0;
fileStream.PacketDecoded += AudioPacketDecoded;
fileStream.PropertyFound += AudioPropertyFound;
}
}
public void ResetOutputQueue ()
{
if (OutputQueue != null) {
OutputQueue.Stop (true);
OutputQueue.Reset ();
foreach (AudioBuffer buf in outputBuffers) {
buf.PacketDescriptions.Clear ();
OutputQueue.FreeBuffer (buf.Buffer);
}
outputBuffers = null;
OutputQueue.Dispose ();
}
}
/// <summary>
/// Stops the OutputQueue
/// </summary>
public void Pause ()
{
OutputQueue.Pause ();
Started = false;
}
/// <summary>
/// Starts the OutputQueue
/// </summary>
public void Play ()
{
OutputQueue.Start ();
Started = true;
}
/// <summary>
/// Main methode to kick off the streaming, just send the bytes to this method
/// </summary>
public void ParseBytes (byte[] buffer, int count, bool discontinuity, bool lastPacket)
{
this.lastPacket = lastPacket;
fileStream.ParseBytes (buffer, 0, count, discontinuity);
}
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
public void Dispose(int tt) {
}
/// <summary>
/// Cleaning up all the native Resource
/// </summary>
protected virtual void Dispose (bool disposing)
{
if (disposing) {
if (OutputQueue != null)
OutputQueue.Stop(true);
if (outputBuffers != null) {
foreach (var b in outputBuffers)
OutputQueue.FreeBuffer (b.Buffer);
outputBuffers.Clear ();
outputBuffers = null;
}
if (fileStream != null) {
fileStream.Close ();
fileStream = null;
}
if (OutputQueue != null) {
OutputQueue.Dispose ();
OutputQueue = null;
}
}
}
/// <summary>
/// Saving the decoded Packets to our active Buffer, if the Buffer is full queue it into the OutputQueue
/// and wait until another buffer gets freed up
/// </summary>
void AudioPacketDecoded (object sender, PacketReceivedEventArgs args)
{
foreach (var p in args.PacketDescriptions) {
currentByteCount += p.DataByteSize;
AudioStreamPacketDescription pd = p;
int left = bufferSize - currentBuffer.CurrentOffset;
if (left < pd.DataByteSize) {
EnqueueBuffer ();
WaitForBuffer ();
}
AudioQueue.FillAudioData (currentBuffer.Buffer, currentBuffer.CurrentOffset, args.InputData, (int)pd.StartOffset, pd.DataByteSize);
// Set new offset for this packet
pd.StartOffset = currentBuffer.CurrentOffset;
// Add the packet to our Buffer
currentBuffer.PacketDescriptions.Add (pd);
// Add the Size so that we know how much is in the buffer
currentBuffer.CurrentOffset += pd.DataByteSize;
}
if ((fileStream != null && currentByteCount == fileStream.DataByteCount) || lastPacket)
EnqueueBuffer ();
}
/// <summary>
/// Flush the current buffer and close the whole thing up
/// </summary>
public void FlushAndClose ()
{
if (OutputQueue != null) {
EnqueueBuffer ();
OutputQueue.Flush ();
}
Dispose ();
}
/// <summary>
/// Enqueue the active buffer to the OutputQueue
/// </summary>
void EnqueueBuffer ()
{
currentBuffer.IsInUse = true;
OutputQueue.EnqueueBuffer (currentBuffer.Buffer, currentBuffer.CurrentOffset, currentBuffer.PacketDescriptions.ToArray ());
queuedBufferCount++;
StartQueueIfNeeded ();
}
/// <summary>
/// Wait until a buffer is freed up
/// </summary>
void WaitForBuffer ()
{
int curIndex = outputBuffers.IndexOf (currentBuffer);
currentBuffer = outputBuffers [curIndex < outputBuffers.Count - 1 ? curIndex + 1 : 0];
lock (currentBuffer) {
while (currentBuffer.IsInUse)
Monitor.Wait (currentBuffer);
}
}
void StartQueueIfNeeded ()
{
if (Started)
return;
Play ();
}
/// <summary>
/// When a AudioProperty in the fed packets is found this callback is called
/// </summary>
void AudioPropertyFound (object sender, PropertyFoundEventArgs args)
{
if (args.Property == AudioFileStreamProperty.ReadyToProducePackets) {
Started = false;
if (OutputQueue != null)
OutputQueue.Dispose ();
OutputQueue = new OutputAudioQueue (fileStream.StreamBasicDescription);
if (OutputReady != null)
OutputReady (OutputQueue);
currentByteCount = 0;
OutputQueue.BufferCompleted += HandleBufferCompleted;
outputBuffers = new List<AudioBuffer> ();
for (int i = 0; i < MaxBufferCount; i++) {
IntPtr outBuffer;
OutputQueue.AllocateBuffer (BufferSize, out outBuffer);
outputBuffers.Add (new AudioBuffer () {
Buffer = outBuffer,
PacketDescriptions = new List<AudioStreamPacketDescription> ()
});
}
currentBuffer = outputBuffers.First ();
OutputQueue.MagicCookie = fileStream.MagicCookie;
}
}
/// <summary>
/// Is called when a buffer is completly read and can be freed up
/// </summary>
void HandleBufferCompleted (object sender, BufferCompletedEventArgs e)
{
queuedBufferCount--;
IntPtr buf = e.IntPtrBuffer;
foreach (var buffer in outputBuffers) {
if (buffer.Buffer != buf)
continue;
// free Buffer
buffer.PacketDescriptions.Clear ();
buffer.CurrentOffset = 0;
lock (buffer) {
buffer.IsInUse = false;
Monitor.Pulse (buffer);
}
}
if (queuedBufferCount == 0 && Finished != null)
Finished (this, new EventArgs ());
}
}
}
how can i change the code to allow that?
thanks in advance for helps.
Put this following code in your AppDelegate.csin the FinishedLaunchingmethod.
NSError sessionError = null;
AVAudioSession.SharedInstance().SetCategory(AVAudioSession.CategoryAmbient, out sessionError);
AVAudioSession.SharedInstance().SetActive(true, out sessionError);
Apps get suspended when the phone is put to sleep. If you wan't your app to continue executing, you have to register for background execution.
Here is a nice guide Jonathan Sagorin (if you don't mind putting it over from Swift/obj-c).
Basically the class you are looking for is AVAudioSession and it's SetActive Method to activate a background audio session.

Client's Socket.send aren't working well when another client joins

I'm writing a chat program using sockets in C# Winform.
It works well with single client, but when I turn on another client, previous client's (worked well) Send function never works, but it keeps receiving data.
Here's my whole source code (because idk what's wrong with it, and I got some advices that I gave them poor details)
Server:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Player__Server_
{
public partial class Form1 : Form
{
Socket Serv;
List<Socket> ClnSocket = new List<Socket>();
List<string> MusList = new List<string>();
List<string> Nickname = new List<string>();
int ClnCounter = 0;
int MusCounter = 0;
int BufferingCount = 0;
Socket socket;
Thread run;
private delegate void sDelegate(string sData, int socketIndex);
public Form1()
{
InitializeComponent();
}
new public string Left(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(0, TextLength);
}
new public string Right(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(Text.Length - TextLength, TextLength);
}
new public string Mid(string sString, int nStart, int nLength)
{
string sReturn;
--nStart;
if (nStart <= sString.Length)
{
if ((nStart + nLength) <= sString.Length)
{
sReturn = sString.Substring(nStart, nLength);
}
else
{
sReturn = sString.Substring(nStart);
}
}
else
{
sReturn = string.Empty;
}
return sReturn;
}
private void Form1_Load(object sender, EventArgs e)
{
// prevent exception
Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.OpenOrCreate);
fe.Close();
// Read "Music.server" file and add to MusList (List)
string line;
StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.server");
while ((line = fr.ReadLine()) != null)
{
MusList.Add(line);
MusCounter++;
}
fr.Close();
// prevent exception
Stream fa = new FileStream(Environment.CurrentDirectory + "/Account.server", FileMode.OpenOrCreate);
fa.Close();
Serv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Serv.Bind(new IPEndPoint(IPAddress.Any, 9180));
Serv.Listen(100);
Thread accept = new Thread(this.Accept);
accept.Start();
}
private void runChatting(object s)
{
byte[] str = new byte[2048];
socket = s as Socket;
while (true)
{
try
{
str = new byte[2048];
socket.Receive(str);
sDelegate sdelegate = new sDelegate(this.Receive);
this.Invoke(sdelegate, Encoding.Default.GetString(str), ClnSocket.IndexOf(socket));
}
catch
{
try
{
ClnSocket.Remove(socket);
ClnCounter--;
}
catch { }
return;
}
}
}
private string GetMusic(string MusName)
{
// Function :: return original information of music- search by name
int i;
for (i = 0; i < MusCounter; i++)
{
try
{
if (MusList[i].IndexOf(MusName) > 0)
{
return MusList[i];
}
}
catch { }
}
return null;
}
private void Receive(string sData, int socketIndex)
{
TextBox.AppendText("GET : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
sData = sData.Replace("\0", "");
if (Left(sData,10) == "#musicadd#")
{
string TempData = Mid(sData, 11, sData.Length);
string[] SpliteData = TempData.Split('#');
if (GetMusic(SpliteData[1]) == null)
{
Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.Append);
StreamWriter ws = new StreamWriter(fs);
ws.WriteLine(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
ws.Close();
fs.Close();
MusList.Add(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
MusCounter++;
}
SendTo("#musicadd#" + SpliteData[1], socketIndex);
}
else if (Left(sData, 7) == "#login#")
{
SendAll(Mid(sData, 8, sData.Length) + " Connected." + Environment.NewLine);
}
else if (Left(sData, 14) == "#requestmusic#")
{
string requestValue = GetMusic(Mid(sData, 15, sData.Length));
SendAll("#buffermusic#" + requestValue);
BufferingCount = 0;
}
else if (Left(sData, 12) == "#bufferdone#")
{
BufferingCount++;
if (BufferingCount == ClnCounter)
{
SendAll("#musicplay#");
}
}
else
{
SendAll(sData);
}
}
private void SendAll(string sData)
{
int i;
for (i = 0; i < ClnSocket.Count; i++)
{
try
{
ClnSocket[i].Send(Encoding.Default.GetBytes(sData));
}
catch { }
}
TextBox.AppendText("POST : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
}
private void SendTo(string sData, int socketIndex)
{
try
{
ClnSocket[socketIndex].Send(Encoding.Default.GetBytes(sData));
TextBox.AppendText("POST TO (" + socketIndex.ToString() + ") : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
}
catch { }
}
private void Accept()
{
while (true)
{
ClnSocket.Add(Serv.Accept());
ClnCounter++;
run = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(this.runChatting));
run.Start(ClnSocket[ClnCounter - 1]);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.Serv.Close();
this.Serv = null;
for (int i = 0; i < this.ClnSocket.Count; i++)
{
this.ClnSocket[i].Close();
}
this.ClnSocket.Clear();
System.Environment.Exit(0);
}
}
}
Client:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.IO;
namespace MineSky_Player
{
public partial class Form1 : Form
{
WMPLib.WindowsMediaPlayer Player = new WMPLib.WindowsMediaPlayer();
public Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Thread run;
string BufferingInformation;
int current_min = 0, current_sec = 0;
int duration_min = 0, duration_sec = 0;
int MusCounter = 0;
public string mynick { get; set; }
string MyNick;
List<string> MusList = new List<string>();
public delegate void sDelegate(string sData);
public Form1()
{
try
{
InitializeComponent();
socket.Connect("localhost", 9180);
run = new Thread(new ParameterizedThreadStart(Run));
run.Start();
}
catch (Exception ex){
MessageBox.Show(ex.ToString());
System.Environment.Exit(0);
}
}
private void Form1_Load(object sender, EventArgs e)
{
// prevent exception
Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.OpenOrCreate);
fe.Close();
// Read "Music.client" file and add to MusList (List)
string line;
StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.client");
while ((line = fr.ReadLine()) != null)
{
MusList.Add(line);
MusCounter++;
MusicList.Items.Add(line);
}
fr.Close();
MyNick = mynick;
}
new public string Left(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(0, TextLength);
}
new public string Right(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(Text.Length - TextLength, TextLength);
}
new public string Mid(string sString, int nStart, int nLength)
{
string sReturn;
--nStart;
if (nStart <= sString.Length)
{
if ((nStart + nLength) <= sString.Length)
{
sReturn = sString.Substring(nStart, nLength);
}
else
{
sReturn = sString.Substring(nStart);
}
}
else
{
sReturn = string.Empty;
}
return sReturn;
}
private void BufferTick_Tick(object sender, EventArgs e)
{
if (Player.playState.ToString() == "wmppsPlaying")
{
Player.controls.stop();
ToSocket("#bufferdone#");
BufferTick.Enabled = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Player.controls.play();
}
private void Run(object s)
{
byte[] str = new byte[2048];
try
{
while (true)
{
str = new byte[2048];
socket.Receive(str);
sDelegate sdelegate = new sDelegate(this.Receive);
IntPtr x;
if (!this.IsHandleCreated) x = this.Handle;
this.Invoke(sdelegate, Encoding.Default.GetString(str));
}
}
catch (Exception e)
{
MessageBox.Show("Connection Lost." + Environment.NewLine + e.ToString());
Application.Exit();
}
}
public void Receive(string sData)
{
//MessageBox.Show("GET : " + sData);
sData = sData.Replace("\0", "");
if (Left(sData, 10) == "#musicadd#")
{
if (MusicList.Items.Contains(Mid(sData, 11, sData.Length)))
{
MessageBox.Show("Already in the list!", "MSPlayer", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.Append);
StreamWriter ws = new StreamWriter(fs);
ws.WriteLine(Mid(sData, 11, sData.Length));
ws.Close();
fs.Close();
MusList.Add(Mid(sData, 11, sData.Length));
MusicList.Items.Add(Mid(sData, 11, sData.Length));
MusCounter++;
}
}
else if (Left(sData, 13) == "#buffermusic#")
{
PlayProgressBar.Value = 0;
current_min = 0;
current_sec = 0;
Player.URL = "";
BufferingInformation = Mid(sData, 14, sData.Length);
playingLabel.Text = BufferingInformation.Split('#')[1];
playLabel.Text = "Buffering...";
Player.URL = "https://player.soundcloud.com/player.swf?url=https%3A//api.soundcloud.com/tracks/" + BufferingInformation.Split('#')[0] + ";color=ff5500&show_comments=false&auto_play=true& color=a2eeff";
BufferTick.Enabled = true;
}
else if (Left(sData, 11) == "#musicplay#")
{
duration_min = Int32.Parse(BufferingInformation.Split('#')[2]) / 60;
duration_sec = Int32.Parse(BufferingInformation.Split('#')[2]) % 60;
playLabel.Text = "0:00 / " + duration_min.ToString() + ":" + duration_sec.ToString();
PlayProgressBar.Maximum = Int32.Parse(BufferingInformation.Split('#')[2]);
Player.controls.play();
PlayTick.Enabled = true;
}
else
{
Text_Board.AppendText(sData.Replace("\#\", "#"));
}
}
public void Send(string sData)
{
sData = sData.Replace("#", "\#\");
Send_Supplied(sData);
}
public void Send_Supplied(string sData)
{
if (Left(sData, 2) == "//")
{
sData = sData.ToLower();
if (Left(sData, 6) == "//exit") Application.Exit();
}
else
{
ToSocket(sData);
}
}
private void ToSocket(string sData)
{
socket.Send(Encoding.Default.GetBytes(sData));
}
private void Text_Chat_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
string str = this.Text_Chat.Text.Replace(Environment.NewLine, "");
this.Text_Chat.Text = "";
Send(MyNick + " : " + str + Environment.NewLine);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
socket.Close();
System.Environment.Exit(0);
}
private void Button_Exit_Click(object sender, EventArgs e)
{
socket.Close();
System.Environment.Exit(0);
}
private void MusicList_DoubleClick(object sender, EventArgs e)
{
ToSocket("#requestmusic#" + MusicList.GetItemText(MusicList.SelectedItem));
}
private void Button_MusicAdd_Click(object sender, EventArgs e)
{
AddForm addform = new AddForm();
addform.socket = socket;
addform.ShowDialog();
}
private void MusicList_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void PlayTick_Tick(object sender, EventArgs e)
{
if (Player.playState.ToString() == "wmppsPlaying")
{
current_sec++;
PlayProgressBar.Value++;
if (current_sec == 60)
{
current_min++;
current_sec = 0;
}
playLabel.Text = current_min.ToString() + ":" + current_sec.ToString() + " / " + duration_min.ToString() + ":" + duration_sec.ToString();
if (PlayProgressBar.Value == PlayProgressBar.Maximum)
{
Player.controls.stop();
playingLabel.Text = "Not Playing";
playLabel.Text = "0:00 / 0:00";
PlayProgressBar.Value = 0;
PlayTick.Enabled = false;
}
}
}
}
}
Addform and Enterform are not the problem, I think. I've tested.
I wasn't able to get your code to compile when I copied it in to a new project, so I'm not sure. One thing is for sure though, you have to many while loops taking place here. I would recommend replacing those with the async pattern that the Socket class has available. It would remove all of the while loops. A little bit of extra abstraction would help as well.
I have broken the server up in to its own class, then provided each Socket connection a wrapper class that the server will hold a reference to.
Note that I am using Visual Studio 2015, so my string formatting is using the new C# 6.0 style. If you can not use C# 6, it is easy enough to convert back to string.Format("{0}", foo);
Server Status
We need to have a way of checking what the current server status is. This can be as simple as a small enum.
/// <summary>
/// Provides status values for the server to use, indicating its current state.
/// </summary>
public enum ServerStatus
{
/// <summary>
/// The server has stopped.
/// </summary>
Stopped,
/// <summary>
/// Server is in the process of starting.
/// </summary>
Starting,
/// <summary>
/// Server is up and running.
/// </summary>
Running
}
ConnectedArgs
Next, the Server class needs to raise some events so that the Form code-behind can be told when a client connects, and when the client has sent the server a message. We will provide a class called ConnectedArgs as our event handler argument. This class will hold a reference to our actual client, using the wrapper class we will create next.
public class ConnectedArgs : EventArgs
{
/// <summary>
/// Instances a new ConnectedArgs class.
/// </summary>
/// <param name="state">The state of a client connection.</param>
public ConnectedArgs(ConnectionState state)
{
this.ConnectedClient = state;
}
/// <summary>
/// Gets the client currently connected.
/// </summary>
public ConnectionState ConnectedClient { get; private set; }
}
ConnectionState
This class is responsible for holding the Socket associated with the connected client, and handle receiving the clients message data asynchronously. This uses the BeginInvoke and EndInvoke async pattern included with the Socket.
This class will have an event that will be used to notify the Form that a new message was received. Note that this was pulled from one of my existing projects, so the data parsing basically checks the buffer and if the buffer does not include a \r\n, it is considered incomplete. It caches it and waits for the next chunk of data from the client to process and try and complete. You will want to replace the ProcessReceivedData method with your custom method that handles processing the received data. When you are done, just push the results in to the OnDataReceived method so your Form can be given it.
public sealed class ConnectionState
{
/// <summary>
/// The size of the buffer that will hold data sent from the client
/// </summary>
private readonly int bufferSize;
/// <summary>
/// A temporary collection of incomplete messages sent from the client. These must be put together and processed.
/// </summary>
private readonly List<string> currentData = new List<string>();
/// <summary>
/// What the last chunk of data sent from the client contained.
/// </summary>
private string lastChunk = string.Empty;
/// <summary>
/// Instances a new PlayerConnectionState.
/// </summary>
/// <param name="player">An instance of a Player type that will be performing network communication</param>
/// <param name="currentSocket">The Socket used to communicate with the client.</param>
/// <param name="bufferSize">The storage size of the data buffer</param>
public ConnectionState(Socket currentSocket, int bufferSize)
{
this.CurrentSocket = currentSocket;
this.bufferSize = bufferSize;
this.Buffer = new byte[bufferSize];
}
/// <summary>
/// This event is raised when the server has received new, valid, data from the client.
/// </summary>
public event EventHandler<string> DataReceived;
/// <summary>
/// Gets the Socket for the player associated with this state.
/// </summary>
public Socket CurrentSocket { get; private set; }
/// <summary>
/// Gets the data currently in the network buffer
/// </summary>
public byte[] Buffer { get; private set; }
/// <summary>
/// Gets if the current network connection is in a valid state.
/// </summary>
public bool IsConnectionValid
{
get
{
return this.CurrentSocket != null && this.CurrentSocket.Connected;
}
}
/// <summary>
/// Starts listening for network communication sent from the client to the server
/// </summary>
public void StartListeningForData()
{
this.Buffer = new byte[bufferSize];
this.CurrentSocket.BeginReceive(this.Buffer, 0, bufferSize, 0, new AsyncCallback(this.ReceiveData), null);
}
/// <summary>
/// Receives the input data from the user.
/// </summary>
/// <param name="result">The result.</param>
private void ReceiveData(IAsyncResult result)
{
// If we are no longer in a valid state, dispose of the connection.
if (!this.IsConnectionValid)
{
this.CurrentSocket?.Dispose();
return;
}
int bytesRead = this.CurrentSocket.EndReceive(result);
if (bytesRead == 0 || !this.Buffer.Any())
{
this.StartListeningForData();
return;
}
ProcessReceivedData(bytesRead);
this.StartListeningForData();
}
/// <summary>
/// Process the data we received from the client.
/// </summary>
/// <param name="bytesRead"></param>
private void ProcessReceivedData(int bytesRead)
{
// Encode our input string sent from the client
this.lastChunk = Encoding.ASCII.GetString(this.Buffer, 0, bytesRead);
// If the previous chunk did not have a new line feed, then we add this message to the collection of currentData.
// This lets us build a full message before processing it.
if (!lastChunk.Contains("\r\n"))
{
// Add this to our incomplete data stash and read again.
this.currentData.Add(lastChunk);
return;
}
// This message contained at least 1 new line, so we split it and process per line.
List<string> messages = lastChunk.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
foreach (string line in this.PruneReceivedMessages(messages))
{
this.OnDataReceived(line);
}
}
/// <summary>
/// Runs through the messages collection and prepends data from a previous, incomplete, message
/// and updates the internal message tracking state.
/// </summary>
/// <param name="messages"></param>
private List<string> PruneReceivedMessages(List<string> messages)
{
// Append the first line to the incomplete line given to us during the last pass if one exists.
if (this.currentData.Any() && messages.Any())
{
messages[0] = string.Format("{0} {1}", string.Join(" ", this.currentData), messages[0]);
this.currentData.Clear();
}
// If we have more than 1 line and the last line in the collection does not end with a line feed
// then we add it to our current data so it may be completed during the next pass.
// We then remove it from the lines collection because it can be infered that the remainder will have
// a new line due to being split on \n.
if (messages.Count > 1 && !messages.Last().EndsWith("\r\n"))
{
this.currentData.Add(messages.Last());
messages.Remove(messages.Last());
}
return messages;
}
private void OnDataReceived(string data)
{
var handler = this.DataReceived;
if (handler == null)
{
return;
}
handler(this, data);
}
}
Server
Now that we have the client async receiving and processing of data, we need to write the server component to actually accept incoming Socket connections asynchronously.
This class will have two events that the Form will subscribe to. One for when a client connects, and another for when the client disconnects. Each client that is connected, is assigned a ConnectionState and cached in a collection of List<ConnectionState>. This removes the need for you to keep a fragile counter of the number of clients connected.
You could optionally wire up a timer that periodically prunes the List<ConnectionState> collection. You can check if each instance in the collection has its IsConnectionValid set to true. If it returns false, remove it from the collection.
/// <summary>
/// The Default Desktop game Server
/// </summary>
public sealed class Server
{
/// <summary>
/// The user connection buffer size
/// </summary>
private const int UserConnectionBufferSize = 1024;
/// <summary>
/// The server socket
/// </summary>
private Socket serverSocket;
/// <summary>
/// The player connections
/// </summary>
private List<ConnectionState> connectedClients;
/// <summary>
/// Used for making access to the connectedClients collection thread-safe
/// </summary>
private object lockObject = new object();
/// <summary>
/// Initializes a new instance of the <see cref="Server"/> class.
/// </summary>
public Server()
{
this.Status = ServerStatus.Stopped;
this.connectedClients = new List<ConnectionState>();
}
/// <summary>
/// Occurs when a client connects to the server.
/// </summary>
public event EventHandler<ConnectedArgs> ClientConnected;
/// <summary>
/// Occurs when a client is disconnected from the server.
/// </summary>
public event EventHandler<ConnectedArgs> ClientDisconnected;
/// <summary>
/// Gets or sets the port that the server is running on.
/// </summary>
public int Port { get; set; }
/// <summary>
/// Gets or sets the maximum queued connections.
/// </summary>
public int MaxQueuedConnections { get; set; }
/// <summary>
/// Gets the current server status.
/// </summary>
public ServerStatus Status { get; private set; }
public void Start()
{
if (this.Status != ServerStatus.Stopped)
{
throw new InvalidOperationException("The server is either starting or already running. You must stop the server before starting it again.");
}
else if (this.Port == 0)
{
throw new InvalidOperationException("You can not start the server on Port 0.");
}
this.Status = ServerStatus.Starting;
// Get our server address information
IPHostEntry serverHost = Dns.GetHostEntry(Dns.GetHostName());
var serverEndPoint = new IPEndPoint(IPAddress.Any, this.Port);
// Instance the server socket, bind it to a port.
this.serverSocket = new Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
this.serverSocket.Bind(serverEndPoint);
this.serverSocket.Listen(this.MaxQueuedConnections);
// Begin listening for connections.
this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);
this.Status = ServerStatus.Running;
}
/// <summary>
/// Stops the server.
/// </summary>
public void Stop()
{
this.DisconnectAll();
// We test to ensure the server socket is still connected and active.
this.serverSocket.Blocking = false;
try
{
this.serverSocket.Send(new byte[1], 0, 0);
// Message was received meaning it's still receiving, so we can safely shut it down.
this.serverSocket.Shutdown(SocketShutdown.Both);
}
catch (SocketException e)
{
// Error code 10035 indicates it works, but will block the socket.
// This means it is still receiving and we can safely shut it down.
// Otherwise, it's not receiving anything and we don't need to shut down.
if (e.NativeErrorCode.Equals(10035))
{
this.serverSocket.Shutdown(SocketShutdown.Both);
}
}
finally
{
this.Status = ServerStatus.Stopped;
}
}
/// <summary>
/// Disconnects the specified IServerPlayer object.
/// </summary>
/// <param name="connection">The client to disconnect.</param>
public void Disconnect(ConnectionState connection)
{
if (connection != null && connection.IsConnectionValid)
{
connection.CurrentSocket.Shutdown(SocketShutdown.Both);
this.connectedClients.Remove(connection);
this.OnClientDisconnected(connection);
}
}
/// <summary>
/// Disconnects everyone from the server.
/// </summary>
public void DisconnectAll()
{
// Loop through each connection and disconnect them.
foreach (ConnectionState state in this.connectedClients)
{
Socket connection = state.CurrentSocket;
if (connection != null && connection.Connected)
{
connection.Shutdown(SocketShutdown.Both);
this.OnClientDisconnected(state);
}
}
this.connectedClients.Clear();
}
/// <summary>
/// Called when a client connects.
/// </summary>
private void OnClientConnected(ConnectionState connection)
{
EventHandler<ConnectedArgs> handler = this.ClientConnected;
if (handler == null)
{
return;
}
handler(this, new ConnectedArgs(connection));
}
/// <summary>
/// Called when a client disconnects.
/// </summary>
private void OnClientDisconnected(ConnectionState connection)
{
EventHandler<ConnectedArgs> handler = this.ClientDisconnected;
if (handler == null)
{
return;
}
handler(this, new ConnectedArgs(connection));
}
/// <summary>
/// Connects the client to the server and then passes the connection responsibilities to the client object.
/// </summary>
/// <param name="result">The async result.</param>
private void ConnectClient(IAsyncResult result)
{
// Connect and register for network related events.
Socket connection = this.serverSocket.EndAccept(result);
// Send our greeting
byte[] buffer = Encoding.ASCII.GetBytes("Welcome to the Music App Server!");
connection.BeginSend(buffer, 0, buffer.Length, 0, new AsyncCallback(asyncResult => connection.EndReceive(asyncResult)), null);
// Fetch the next incoming connection.
this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);
this.CompleteClientSetup(new ConnectionState(connection, UserConnectionBufferSize));
}
/// <summary>
/// Caches the ConnectionState and has the state begin listening to client data.
/// </summary>
/// <param name="connectionState"></param>
private void CompleteClientSetup(ConnectionState connectionState)
{
lock (this.lockObject)
{
this.connectedClients.Add(connectionState);
}
// Start receiving data from the client.
connectionState.StartListeningForData();
this.OnClientConnected(connectionState);
}
}
Form
Now that we have our server code put together, the Form can be constructed. I just did a simple form, with a single button for connecting and a single multi-line textbox for viewing the data.
I added an event handler for the Form's Loading event and for the Button's Clicked event.
public partial class Form1 : Form
{
private Server server;
public Form1()
{
InitializeComponent();
server = new Server { Port = 9180, MaxQueuedConnections = 100 };
}
private void Form1_Load(object sender, EventArgs e)
{
server.ClientConnected += OnClientConnected;
server.ClientDisconnected += OnClientDisconnected;
}
private void OnClientDisconnected(object sender, ConnectedArgs e)
{
this.Invoke(new Action(() => this.textBox1.AppendText("A Client disconnected.\n")));
}
private void OnClientConnected(object sender, ConnectedArgs e)
{
this.Invoke(new Action(() =>
{
this.textBox1.AppendText("New Client Connected.\n");
e.ConnectedClient.DataReceived += OnClientSentDataToServer;
}));
}
private void OnClientSentDataToServer(object sender, string e)
{
this.Invoke(new Action(() => this.textBox1.AppendText($"{e}\n")));
}
private void button1_Click(object sender, EventArgs e)
{
this.textBox1.AppendText("Server starting.\n");
server.Start();
this.textBox1.AppendText("Server running.\n");
}
}
Due to the server running as a background process, you have to marshall the event handlers back to the UI thread when accessing the TextBox. You could abstract that piece out a bit as well, so that the Server and ConnectionState objects always push to the dispatcher you provide them. This reduces the number of Invoke(Action) invocations you have to do.
This works well with multiple connections. I started the server up, and then spun up two different connections across a couple different computers without any issues. You will need to customize the data processing and pushing messages to the client. This should at least solve your multiple connection issue, and be less stress on the CPU (no more while loops!).
Hope this helps.

How to dispose System.Threading.EventWaitHandle in Mono?

The System.Threading.EventWaitHandle type does not contain a definition for 'dispose'.
How am I supposed to dispose of the EventWaitHandle when I want to kill the object in which it is contained? I've been having a serious memory leak problem and I've tried setting the EventWaitHandle to null and the memory leak persists.
Here's my code (I'm using Bob Craven's library, I added only the dispose() method):
using System;
using System.Threading;
namespace Rlc.Cron
{
public class CronObject
{
public delegate void CronEvent(CronObject cronObject);
public event CronEvent OnCronTrigger;
public event CronEvent OnStarted;
public event CronEvent OnStopped;
public event CronEvent OnThreadAbort;
private CronObjectDataContext _cronObjectDataContext;
private Guid _id = Guid.NewGuid();
private object _startStopLock = new object();
private EventWaitHandle _wh = new AutoResetEvent(false);
private Thread _thread;
public bool _isStarted;
private bool _isStopRequested;
private DateTime _nextCronTrigger;
public Guid Id { get { return _id; } }
public object Object { get { return _cronObjectDataContext.Object; } }
public DateTime LastTigger { get { return _cronObjectDataContext.LastTrigger; } }
/// <summary>
/// Initializes a new instance of the <see cref="CronObject"/> class.
/// </summary>
/// <param name="cronObjectDataContext">The cron object data context.</param>
public CronObject(CronObjectDataContext cronObjectDataContext)
{
if (cronObjectDataContext == null)
{
throw new ArgumentNullException("cronObjectDataContext");
}
if (cronObjectDataContext.Object == null)
{
throw new ArgumentException("cronObjectDataContext.Object");
}
if (cronObjectDataContext.CronSchedules == null || cronObjectDataContext.CronSchedules.Count == 0)
{
throw new ArgumentException("cronObjectDataContext.CronSchedules");
}
_cronObjectDataContext = cronObjectDataContext;
}
/// <summary>
/// Starts this instance.
/// </summary>
/// <returns></returns>
public bool Start()
{
lock (_startStopLock)
{
// Can't start if already started.
//
if (_isStarted)
{
return false;
}
_isStarted = true;
_isStopRequested = false;
// This is a long running process. Need to run on a thread
// outside the thread pool.
//
_thread = new Thread(ThreadRoutine);
_thread.Start();
}
// Raise the started event.
//
if(OnStarted != null)
{
OnStarted(this);
}
return true;
}
/// <summary>
/// Stops this instance.
/// </summary>
/// <returns></returns>
public bool Stop()
{
lock (_startStopLock)
{
// Can't stop if not started.
//
if (!_isStarted)
{
return false;
}
_isStarted = false;
_isStopRequested = true;
// Signal the thread to wake up early
//
_wh.Set();
// Wait for the thread to join.
//
if(!_thread.Join(5000))
{
_thread.Abort();
// Raise the thread abort event.
//
if(OnThreadAbort != null)
{
OnThreadAbort(this);
}
}
}
// Raise the stopped event.
//
if(OnStopped != null)
{
OnStopped(this);
}
return true;
}
public void dispose(){
this.Stop ();
this.OnCronTrigger = null;
this.OnStarted=null;
this.OnStopped=null;
this.OnThreadAbort=null;
this._cronObjectDataContext=null;
this._startStopLock = null;
this._wh = null;
this._thread=null;
}
/// <summary>
/// Cron object thread routine.
/// </summary>
private void ThreadRoutine()
{
// Continue until stop is requested.
//
while(!_isStopRequested)
{
// Determine the next cron trigger
//
DetermineNextCronTrigger(out _nextCronTrigger);
TimeSpan sleepSpan = _nextCronTrigger - DateTime.Now;
if(sleepSpan.TotalMilliseconds < 0)
{
// Next trigger is in the past. Trigger the right away.
//
sleepSpan = new TimeSpan(0, 0, 0, 0, 50);
}
// Wait here for the timespan or until I am triggered
// to wake up.
//
if(!_wh.WaitOne(sleepSpan))
{
// Timespan is up...raise the trigger event
//
if(OnCronTrigger != null)
{
OnCronTrigger(this);
}
// Update the last trigger time.
//
_cronObjectDataContext.LastTrigger = DateTime.Now;
}
}
}
/// <summary>
/// Determines the next cron trigger.
/// </summary>
/// <param name="nextTrigger">The next trigger.</param>
private void DetermineNextCronTrigger(out DateTime nextTrigger)
{
nextTrigger = DateTime.MaxValue;
foreach (CronSchedule cronSchedule in _cronObjectDataContext.CronSchedules)
{
DateTime thisTrigger;
if(cronSchedule.GetNext(LastTigger, out thisTrigger))
{
if (thisTrigger < nextTrigger)
{
nextTrigger = thisTrigger;
}
}
}
}
~CronObject(){
Console.WriteLine ("===================CRONOBJECT DESTROYED!!===============");
}
}
}

ArgumentException in producer-consumer queue in C#

I have a producer/consumer queue as following but I am getting ArgumentWException.
Following is the code:
public class ProducerConsumer<T> where T : class
{
#region Private Variables
private Thread _workerThread;
private readonly Queue<T> _workQueue;
private object _enqueueItemLocker = new object();
private object _processRecordLocker = new object();
private readonly Action<T> _workCallbackAction;
private AutoResetEvent _workerWaitSignal;
#endregion
#region Constructor
public ProducerConsumer(Action<T> action)
{
_workQueue = new Queue<T>();
_workCallbackAction = action;
}
#endregion
#region Private Methods
private void ProcessRecord()
{
while (true)
{
T workItemToBeProcessed = default(T);
bool hasSomeWorkItem = false;
lock (_processRecordLocker)
{
hasSomeWorkItem = _workQueue.Count > 0;
if (hasSomeWorkItem)
{
workItemToBeProcessed = _workQueue.Dequeue();
if (workItemToBeProcessed == null)
{
return;
}
}
}
if (hasSomeWorkItem)
{
if (_workCallbackAction != null)
{
_workCallbackAction(workItemToBeProcessed);
}
}
else
{
_workerWaitSignal.WaitOne();
}
}
}
#endregion
#region Public Methods
/// <summary>
/// Enqueues work item in the queue.
/// </summary>
/// <param name="workItem">The work item.</param>
public void EnQueueWorkItem(T workItem)
{
lock (_enqueueItemLocker)
{
_workQueue.Enqueue(workItem);
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerWaitSignal.Set();
}
}
/// <summary>
/// Stops the processer, releases wait handles.
/// </summary>
/// <param name="stopSignal">The stop signal.</param>
public void StopProcesser(AutoResetEvent stopSignal)
{
EnQueueWorkItem(null);
_workerThread.Join();
_workerWaitSignal.Close();
_workerWaitSignal = null;
if (stopSignal != null)
{
stopSignal.Set();
}
}
/// <summary>
/// Starts the processer, starts a new worker thread.
/// </summary>
public void StartProcesser()
{
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerThread = new Thread(ProcessRecord) { IsBackground = true };
_workerThread.Start();
}
#endregion
}
Another class is:
public class Tester
{
private readonly ProducerConsumer<byte[]> _proConsumer;
public Tester()
{
_proConsumer = new ProducerConsumer<byte[]>(Display);
}
public void AddData(byte[] data)
{
try
{
_proConsumer.EnQueueWorkItem(recordData);
}
catch (NullReferenceException nre)
{
}
}
public void Start()
{
_proConsumer.StartProcesser();
}
private static object _recordLocker = new object();
private void Display(byte[] recordByteStream)
{
try
{
lock (_recordLocker)
{
Console.WriteLine("Done with data:" + BitConverter.ToInt32(recordByteStream, 0));
}
}
catch (Exception ex)
{
}
}
}
And my main function:
class Program
{
private static Tester _recorder;
static void Main(string[] args)
{
_recorder = new Tester();
_recorder.StartRecording();
for (int i = 0; i < 100000; i++)
{
_recorder.AddRecordData(BitConverter.GetBytes(i));
}
Console.Read();
}
}
Any idea why do I get the exception and what should I do to avoid that ?
Your class, in its current implementation, is not thread-safe. You're using two different objects for your Enqueue (lock (_enqueueItemLocker)) and Dequeue (lock (_processRecordLocker)) calls, which creates a race condition in your Queue<T>.
You need to lock the same object instance on both calls in order to safely use the queue.
If you're using .NET 4, I'd recommend either using ConcurrentQueue<T> or BlockingCollection<T> instead, as these would eliminate the need for the locks in your code, since they're thread-safe.

Categories

Resources