I need a simple JSON-RPC 1.0 client in C#, preferably using .NET 2.0 or later.
I checked out JRock 0.9
They have several samples including Yahoo reader, but the samples demo JSON, not JSON-RPC.
I understand I could implement RPC part using any of the available JSON parsers, like JRock or two from Microsoft. I would prefer a ready sample.
2 Samples here
There are two different implementations. Read the whole thread + check the attachments
The samples above work with HTTP requests. Here's a variant that works with TCP (the Nil class is just an empty class used for requests with no return value):
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Newtonsoft.Json.Linq;
using AustinHarris.JsonRpc;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reactive.Concurrency;
using System.Net.Sockets;
using System.Text;
namespace JsonRpc
{
public class JsonRpcClient
{
private static object idLock = new object();
private static int id = 0;
public Encoding encoding { get; set; }
public JsonRpcClient(IPEndPoint serviceEndpoint, Encoding encoding)
{
this.serviceEndPoint = serviceEndpoint;
this.encoding = encoding;
}
private static Stream CopyAndClose(Stream inputStream)
{
const int readSize = 256;
byte[] buffer = new byte[readSize];
MemoryStream ms = new MemoryStream();
int count = inputStream.Read(buffer, 0, readSize);
while (count > 0)
{
ms.Write(buffer, 0, count);
count = inputStream.Read(buffer, 0, readSize);
}
ms.Position = 0;
inputStream.Close();
return ms;
}
public IObservable<JsonResponse<T>> InvokeWithScheduler<T>(string method, object arg, IScheduler scheduler)
{
var req = new AustinHarris.JsonRpc.JsonRequest()
{
Method = method,
Params = new object[] { arg }
};
return InvokeRequestWithScheduler<T>(req, scheduler);
}
public IObservable<JsonResponse<T>> InvokeSingleArgument<T>(string method, object arg)
{
var req = new AustinHarris.JsonRpc.JsonRequest()
{
Method = method,
Params = new object[] { arg }
};
return InvokeRequest<T>(req);
}
public IObservable<JsonResponse<T>> InvokeWithScheduler<T>(string method, object[] args, IScheduler scheduler)
{
var req = new AustinHarris.JsonRpc.JsonRequest()
{
Method = method,
Params = args
};
return InvokeRequestWithScheduler<T>(req, scheduler);
}
public IObservable<JsonResponse<T>> Invoke<T>(string method, object[] args)
{
var req = new AustinHarris.JsonRpc.JsonRequest()
{
Method = method,
Params = args
};
return InvokeRequest<T>(req);
}
public IObservable<JsonResponse<T>> InvokeRequestWithScheduler<T>(JsonRequest jsonRpc, IScheduler scheduler)
{
var res = Observable.Create<JsonResponse<T>>((obs) =>
scheduler.Schedule(()=>{
makeRequest<T>(jsonRpc, obs);
}));
return res;
}
public IObservable<JsonResponse<T>> InvokeRequest<T>(JsonRequest jsonRpc)
{
return InvokeRequestWithScheduler<T>(jsonRpc, ImmediateScheduler.Instance);
}
private string sendAndReceive(string messageToSend) {
string res = null;
// Data buffer for incoming data.
byte[] bytes = new byte[1024];
// Connect to a remote device.
try {
// Create a TCP/IP socket.
Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
socket.Connect(this.serviceEndPoint);
Console.Write("Socket connected to "+socket.RemoteEndPoint.ToString());
// Encode the data string into a byte array.
byte[] msg = encoding.GetBytes(messageToSend);
// Send the data through the socket.
int bytesSent = socket.Send(msg);
// Receive the response from the remote device.
int bytesRec = socket.Receive(bytes);
res = encoding.GetString(bytes,0,bytesRec);
Console.Write("Server response = "+res);
// Release the socket.
socket.Shutdown(SocketShutdown.Both);
socket.Close();
} catch (ArgumentNullException ane) {
Console.Write("ArgumentNullException : "+ane.ToString());
} catch (SocketException se) {
Console.Write("SocketException : " + se.ToString());
} catch (Exception e) {
Console.Write("Unexpected exception : " + e.ToString());
}
} catch (Exception e) {
Console.Write(e.ToString());
}
return res;
}
private void makeRequest<T>(JsonRequest jsonRpc, IObserver<JsonResponse<T>> obs)
{
JsonResponse<T> rjson = null;
string sstream = "";
try
{
int myId;
lock (idLock)
{
myId = ++id;
}
jsonRpc.Id = myId.ToString();
}
catch (Exception ex)
{
obs.OnError(ex);
}
try
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(jsonRpc)+"\r\n";
if (typeof(T).Equals(typeof(Nil)))
{
sendAndReceive(json);
rjson = new JsonResponse<T>();
}
else
{
sstream = sendAndReceive(json);
rjson = Newtonsoft.Json.JsonConvert.DeserializeObject<JsonResponse<T>>(sstream);
}
}
catch (Exception ex)
{
obs.OnError(ex);
}
if (rjson == null)
{
string exceptionMessage = "";
try
{
JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(sstream) as JObject;
exceptionMessage = jo["Error"].ToString();
}
catch(Exception ex){
exceptionMessage = sstream+"\r\n"+ex.Message;
}
obs.OnError(new Exception(exceptionMessage));
}
else
{
obs.OnNext(rjson);
}
obs.OnCompleted();
}
public IPEndPoint serviceEndPoint { get; set; }
}
}
Here is an example of a .net4 client exposed through Observables (Rx).
http://jsonrpc2.codeplex.com/SourceControl/changeset/view/13061#63133
Here is an almost identical wp7 client that is also exposed through Rx.
http://jsonrpc2.codeplex.com/SourceControl/changeset/view/13061#282775
Both of those examples do their work asynchronously so they may be more complicated then you are looking for, unless of course you wanted examples that were asynchronous. :)
Good Luck!
Related
I'm currently working on a project where I use a serial port with a UWP app.
Sometimes I have the problem that I expect a certain amount of data but when I don't get that amount of data my serial port keeps reading until I cancel it manually.
When I initialize the serialDevice I set the serialDevice.ReadTimeout parameter but that doesn't do anything.
I also tried to bind a cancellationToken to the ReadTimeout parameter but that didn't work either. If I remember correctly it cancelled my method before it even finished...
Is there some way to implement a working timeout?
I use this as my base:
public class ReadWriteAdapter
{
public SemaphoreSlim Semaphore { get; }
private static readonly object LockObject = new object();
private static ReadWriteAdapter _instance;
public static ReadWriteAdapter Current
{
get
{
if (_instance == null)
{
lock (LockObject)
{
if (_instance == null)
{
_instance = new ReadWriteAdapter();
}
}
}
return _instance;
}
}
private ReadWriteAdapter()
{
Semaphore = new SemaphoreSlim(1, 1);
}
private SerialDevice _serialPort;
public CancellationTokenSource ReadCancellationTokenSource;
public CancellationTokenSource WriteCancellationTokenSource;
private object _readCancelLock = new object();
private object _writeCancelLock = new object();
private DataWriter _dataWriter;
private DataReader _dataReader;
public bool IsDeviceInitialized()
{
return _serialPort != null;
}
public async Task<string> Init()
{
try
{
if (_serialPort != null) return "port already configured!";
var aqs = SerialDevice.GetDeviceSelector("COM3");
var devices = await DeviceInformation.FindAllAsync(aqs, null);
if (!devices.Any()) return "no devices found!";
await OpenPort(devices[0].Id);
WriteCancellationTokenSource = new CancellationTokenSource();
ReadCancellationTokenSource = new CancellationTokenSource();
return "found port!";
}
catch (Exception ex)
{
return ex.Message;
}
}
private async Task OpenPort(string deviceId)
{
try
{
_serialPort = await SerialDevice.FromIdAsync(deviceId);
if (_serialPort != null)
{
_serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
_serialPort.BaudRate = 19200;
_serialPort.Parity = SerialParity.None;
_serialPort.StopBits = SerialStopBitCount.One;
_serialPort.DataBits = 8;
_serialPort.Handshake = SerialHandshake.None;
_dataWriter = new DataWriter(_serialPort.OutputStream);
_dataReader = new DataReader(_serialPort.InputStream);
}
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint readBufferLength)
{
Task<uint> loadAsyncTask;
var returnArray = new byte[readBufferLength];
lock (_readCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = _dataReader.LoadAsync(readBufferLength).AsTask(cancellationToken);
}
var bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
_dataReader.ReadBytes(returnArray);
}
return returnArray;
}
public async Task<uint> WriteCommand(string PairedCommand)
{
var commandArray = StringToByteArray(PairedCommand);
uint taskResult = 0;
try
{
if (_serialPort != null)
{
if (_dataWriter == null)
{
_dataWriter = new DataWriter(_serialPort.OutputStream);
taskResult = await WriteAsync(WriteCancellationTokenSource.Token, commandArray);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
_dataWriter.DetachStream();
_dataWriter.Dispose();
_dataWriter = null;
}
return taskResult;
}
public async Task<uint> WriteAsync(CancellationToken cancellationToken, byte[] data)
{
Task<uint> storeAsyncTask;
try
{
lock (_writeCancelLock)
{
cancellationToken.ThrowIfCancellationRequested();
_dataWriter.WriteBytes(data);
storeAsyncTask = _dataWriter.StoreAsync().AsTask(cancellationToken);
}
}
catch (Exception ex)
{
throw ex;
}
return await storeAsyncTask;
}
public async Task<byte[]> Listen(uint bufferLength)
{
var listen = new byte[bufferLength];
try
{
if (_serialPort != null)
{
if (_dataReader == null)
{
_dataReader = new DataReader(_serialPort.InputStream);
}
listen = await ReadAsync(ReadCancellationTokenSource.Token, bufferLength);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (_dataReader != null)
{
_dataReader.DetachStream();
_dataReader.Dispose();
_dataReader = null;
}
}
return listen;
}
public byte[] StringToByteArray(string str)
{
var enc = new ASCIIEncoding();
return enc.GetBytes(str);
}
public void CancelReadTask()
{
lock (_readCancelLock)
{
if (ReadCancellationTokenSource == null) return;
if (ReadCancellationTokenSource.IsCancellationRequested) return;
ReadCancellationTokenSource.Cancel();
ReadCancellationTokenSource = new CancellationTokenSource();
}
}
private void CancelWriteTask()
{
lock (_writeCancelLock)
{
if (WriteCancellationTokenSource == null) return;
if (WriteCancellationTokenSource.IsCancellationRequested) return;
WriteCancellationTokenSource.Cancel();
WriteCancellationTokenSource = new CancellationTokenSource();
}
}
}
And I call it with a code similar to this (short version):
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
await ReadWriteAdapter.Current.WriteCommand("getData");
byte[] recievedArray = await ReadWriteAdapter.Current.Listen(100);
ReadWriteAdapter.Current.Semaphore.Release();
return recievedArray;
}
}
The simplest way to solve this issue is using the ReadTimeOut itself, but performing the reading independently of what was received.
Read ReadTimeOut Information in Microsoft WebSite
Definition: If ReadTimeout is set to TimeSpan.FromMilliseconds(ulong.MaxValue) (see TimeSpan), then a read request completes immediately with the bytes that have already been received, even if no bytes have been received.
In other words, this way it will immediately read whatever is in the serial buffer.
So you can implement it this way:
SerialDevice SerialDeviceComm;
Task.Run(async () => { SerialDeviceComm = await SerialDevice.FromIdAsync(_serialConnectionInfo[_indexPort].PortID); }).Wait();
SerialDeviceComm.BaudRate = _baudRate;
SerialDeviceComm.Parity = _parity;
SerialDeviceComm.StopBits = _stopBitCount;
SerialDeviceComm.DataBits = _dataBit;
SerialDeviceComm.Handshake = _serialHandshake;
// This will make that regardless of receiving bytes or not it will read and continue.
SerialDeviceComm.ReadTimeout = TimeSpan.FromMilliseconds(uint.MaxValue); // Secret is here
dataReader = new DataReader(SerialDeviceComm.InputStream)
{
InputStreamOptions = InputStreamOptions.Partial
};
string _tempData = "";
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
while (sw.ElapsedMilliseconds < timeOut)
{
uint receivedStringSize = 0;
Task.Delay(50).Wait(); // Time to take some bytes
dataReader.InputStreamOptions = InputStreamOptions.Partial;
Task.Run(async () => { receivedStringSize = await dataReader.LoadAsync(200); }).Wait();
_tempData += dataReader.ReadString(receivedStringSize);
}
You can see a example code in my git: GitHub with example
I'm currently developing a WIn IoT app for the Raspberry Pi and I have a significant problem there...I could really need some help solving that...
The problem I'm having is that I have different commands I send over the serial port and every command recieves different responses.
After sending a few commands (can be just one or it can be 15...it's basically random -.-) my app just stops accepting the commands to send or completely crashes when I invoke one of the commands. It happens rather randomly and when I restart the app it's working again.
At least most of the time... Sometimes the first restart doesn't do anything and I have to restart again...
Since the app will probably be used in a bigger project I want to guarantee flawless execution. I don't want to have a customer (or the tool we use to communicate with the Pi) to restart the whole app. It should just abort it's non-working operation, log an error and then be back up running.
I think it may be a possibility that the buffer overflows for whatever reason... But I don't know of any way to clear the buffer from code in UWP. And I can't seem to find anything regarding that.
If you can help me I would be really happy. I'm trying to get this to work for almost a week but I can't find the problem...
My colleague rewrote some of my code last week. I originally passed every info I needed through tuples but he told me it's not good. So he wrote the class containing the info for me. But since then I run into a lot of problems. But I really don't know why it doesn't...
If you want to know more about my code feel free to ask. I really need this to work :/
Some of the code I'm using:
This is the block containing my data:
public class InfoBlock
{
public List<InfoBlockValue> Names { get; set; }
public byte[] ReceivedCrcSum { get; set; }
public byte[] CalculatedCrcSum { get; set; }
public bool IsChecksumEqual { get; set; } = false;
}
public class InfoBlockValue
{
public string InfoString { get; set; }
public InfoBlockValue(string info)
{
this.InfoString = info;
}
public override string ToString()
{
return InfoString;
}
}
This is the class for my Read and Write operations:
public class GetInfoBlock
{
public string SendCommand { get; set; } = "##getnames";
public async Task<uint> Write()
{
byte CarriageReturn = 0x0D;
byte[] WriteArray = StringToByteArray(SendCommand);
byte[] WriteArrayCR = new byte[WriteArray.Length + 1];
WriteArray.CopyTo(WriteArrayCR, 0);
WriteArrayCR[WriteArray.Length] = CarriageReturn;
return await ReadWriteAdapter.Current.WriteAsync(WriteArrayCR);
}
public async Task<InfoBlock> Read()
{
InfoBlock block = new InfoBlock();
byte[] ListenOut = await ReadWriteAdapter.Current.Listen(5802);
byte[] NameCrcSource = new byte[5800];
byte[] NameCrc16 = new byte[2];
Array.Copy(ListenOut, 0, NameCrcSource, 0, 5800);
block.Names = ConvertFromBytes(NameCrcSource);
block.ReceivedCrcSum = new byte[] { ListenOut[5801], ListenOut[5800] };
block.CalculatedCrcSum = Crc16Ccitt.ComputeChecksumBytes(NameCrcSource);
block.IsChecksumEqual = ValidateDataCRC.ValidateData(NameCrcSource, block.ReceivedCrcSum);
return block;
}
public List<InfoBlockValue> ConvertFromBytes(byte[] dataFromDrive)
{
List<InfoBlockValue> InfoValue = new List<InfoBlockValue>();
string[] allConvertedIntegers = new String[100];
int lastReadByte = 0;
int parameterIndex = 0;
int parameterByteIndex = 0;
for (parameterIndex = 0; parameterIndex < 99; parameterIndex++)
{
byte[] allBytesOfOneParameter = new byte[28];
Array.Copy(dataFromDrive, lastReadByte + 1, allBytesOfOneParameter, 0, 28);
InfoValue.Add(new InfoBlockValue(System.Text.Encoding.UTF8.GetString(allBytesOfOneParameter)));
parameterByteIndex = 0;
lastReadByte += 29;
}
return InfoValue;
}
public byte[] StringToByteArray(string str)
{
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
}
}
This is the code where I use the operations:
public class ReadInfo
{
private GetInfoBlock InfoBlock = null;
public async Task<Tuple<string, InfoBlock>> ReadNamesBlock()
{
if (InfoBlock == null)
{
InfoBlock = new GetInfoBlock();
}
await ReadWriteAdapter.semaphore.WaitAsync();
string returnString = string.Empty;
uint writeTuple = await InfoBlock.Write();
try
{
InfoBlock readTuple = await NamesBlock.Read();
bool validationResult = readTuple.IsChecksumEqual;
if (validationResult)
{
returnString += $"Checksum {BitConverter.ToString(readTuple.CalculatedCrcSum)} ReceivedCrcSum: {BitConverter.ToString(readTuple.ReceivedCrcSum)}";
//await ValidateDataCRC.SendAck();
}
else
{
returnString += "Checksum error";
await ValidateDataCRC.SendNack();
}
return new Tuple<string, InfoBlock>(returnString, readTuple);
}
catch (Exception ex)
{
string exception = $"Error while reading the parameternames from the device: {ex.Message}";
return new Tuple<string, InfoBlock>(exception, null);
}
finally
{
NamesBlock = null;
ReadWriteAdapter.semaphore.Release();
}
}
}
And the last one is my ReadWriteAdapter:
public class ReadWriteAdapter
{
public static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private static readonly Object lockObject = new object();
private static ReadWriteAdapter instance;
public static ReadWriteAdapter Current
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ReadWriteAdapter();
}
}
}
return instance;
}
}
private DataWriter dataWriter = null;
private DataReader dataReader = null;
private CancellationTokenSource ReadCancellationTokenSource;
private SerialDevice serialPort = null;
public bool IsDeviceInitialized()
{
return serialPort != null;
}
public async Task<string> Init()
{
try
{
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);
if (devices.Any())
{
if (devices[0].Id.Contains("FTDI"))
{
string deviceId = devices[0].Id;
await OpenPort(deviceId);
}
else
{
string deviceId = devices[1].Id;
await OpenPort(deviceId);
}
ReadCancellationTokenSource = new CancellationTokenSource();
dataWriter = new DataWriter(serialPort.OutputStream);
dataReader = new DataReader(serialPort.InputStream);
return "found port";
}
return "nodevices";
}
catch (Exception ex)
{
return ex.Message;
}
}
private async Task OpenPort(string deviceId)
{
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort != null)
{
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(500);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(500);
serialPort.BaudRate = 19200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
}
}
private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint ReadBufferLength)
{
Task<uint> loadAsyncTask;
byte[] returnArray = new byte[ReadBufferLength];
dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
uint bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
dataReader.ReadBytes(returnArray);
}
return returnArray;
}
public async Task<uint> WriteAsync(byte[] data)
{
if (serialPort == null)
{
throw new ArgumentNullException("device");
}
if (dataWriter == null)
{
throw new ArgumentNullException("device");
}
if (data.Length != 0)
{
dataWriter.WriteBytes(data);
// Launch an async task to complete the write operation
Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();
return await storeAsyncTask;
}
else
{
return 0;
}
}
public async Task<byte[]> Listen(uint BufferLength)
{
byte[] listen = new byte[BufferLength];
try
{
if (serialPort != null)
{
dataReader = new DataReader(serialPort.InputStream);
listen = await ReadAsync(ReadCancellationTokenSource.Token, BufferLength);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader = null;
}
}
return listen;
}
}
Found the answer myself...
I had to dispose the dataReader after I used it.
I guess the amount of streams I had on the reader just overflowed or whatever.
Changed this in ReadWriteAdapter:
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader = null;
}
}
To this:
finally
{
if (dataReader != null) // Cleanup once complete
{
dataReader.DetachStream();
dataReader.Dispose(); //Added this part
dataReader = null;
}
}
When BeginGetBlockHeight is called, it only ever runs asynchronously and i think its because the first Async call does actually complete, but the WaitHandle is unaware of the deeper async calls, hence it 'thinks' all the work is complete, and the main thread continues. however, i need to wait until all sub level tasks complete. How can this be done? i understand it likely means implementing a custom IAsyncResult that contains the state of each child thread, but how to do that i really dont know. any help would be greatly appreciated.
NOTE: i cannot use Task or Await, or anything that isnt compatible with .NET2.0
the whole code snippet can be seen here:
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using CSharp2nem;
using Newtonsoft.Json;
public class Program
{
public class Wrapper
{
private byte[] Buffer = new byte[20000];
public Wrapper()
{
Con = new Connection();
}
private void ReadStream(Action<ResponseAccessor<string>> callback, Stream stream, int len)
{
stream.BeginRead(Buffer, len, 20000, ar =>
{
try
{
int readLen = stream.EndRead(ar);
if (readLen == 0)
{
stream.Close();
callback(new ResponseAccessor<string>
{
Content = Encoding.UTF8.GetString(Buffer, 0, len)
});
}
else
{
var temp = Buffer;
Array.Resize(ref temp, Buffer.Length + readLen);
Buffer = temp;
len += readLen;
ReadStream(callback, stream, len);
}
}
catch (Exception ex)
{
stream.Close();
callback(new ResponseAccessor<string>
{
Ex = ex
});
}
}, null);
}
internal IAsyncResult RequestGet(Action<ResponseAccessor<string>> callback, string path, string query = "")
{
var http = (HttpWebRequest)WebRequest.Create(Con.GetUri(path, query).Uri);
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "GET";
return http.BeginGetResponse(ar =>
{
Stream stream = null;
try
{
var response = http.EndGetResponse(ar);
stream = response.GetResponseStream();
ReadStream(callback, stream, 0);
}
catch (Exception ex)
{
stream?.Close();
callback(new ResponseAccessor<string>
{
Ex = ex
});
}
}, null);
}
public IAsyncResult BeginGetChainHeight(Action<ResponseAccessor<BlockData.BlockHeight>> callback)
{
return RequestGet(body =>
{
if (body.Ex != null)
{
if (body.Ex.Message.Contains("The remote name could not be resolved") && Con.AutoHost)
{
Con.SetNewHost();
BeginGetChainHeight(callback);
}
else throw body.Ex;
}
else
{
callback(new ResponseAccessor<BlockData.BlockHeight>
{
Content = body.Content == null ? null : JsonConvert.DeserializeObject<BlockData.BlockHeight>(body.Content),
Ex = body.Ex
});
}
}, "/chain/height");
}
public Connection Con { get; set; }
}
public static void Main()
{
new Wrapper().BeginGetChainHeight(obj =>
{
Console.WriteLine("inside");
Console.WriteLine(obj.Content.height);
}).AsyncWaitHandle.WaitOne();
Console.WriteLine("outside");
Console.ReadLine();
}
}
Is there any way to use a socks proxy with WebClient? Specifically with the DownloadString method that it provides?
I don't want to use any third party stuff like privoxy, freecap whatever and I can't use commercial libraries like those from Chilkat. I tried using stuff from http://www.mentalis.org/ in fact I used their WebRequest implementation but they don't seem to have something similar for WebClient.
SOCKS is not supported directly by the WebRequest/WebResponse classes and by extension, the WebClient class (it relies on WebRequest to do its work).
It really can't, as it works on the transport layer (TCP/IP) and not through a simple redirect to a server that forwards HTTP requests (which is the level that the WebRequest/WebResponse classes work on).
You can create a specialized derivation of WebRequest/WebResponse (that uses ProxySocket to handle the low-level handshaking and then) and then create a specialized WebClient class which overrides the GetWebRequest and GetWebResponse methods.
Once you have that class substituted for your WebClient instances, it should work as normal (you might have to set up the proxy in each case where you use it though).
Here is how I ended up doing it, thank you casperOne for the answer
public class SocksWebClient : WebClient
{
public IProxyDetails ProxyDetails { get; set; }
public string UserAgent { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest result = null;
if (ProxyDetails != null)
{
if (ProxyDetails.ProxyType == ProxyType.Proxy)
{
result = (HttpWebRequest)WebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
else if (ProxyDetails.ProxyType == ProxyType.Socks)
{
result = SocksHttpWebRequest.Create(address);
result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress);
//TODO: implement user and password
}
else if (ProxyDetails.ProxyType == ProxyType.None)
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
}
else
{
result = (HttpWebRequest)WebRequest.Create(address);
if (!string.IsNullOrEmpty(UserAgent))
((HttpWebRequest)result).UserAgent = UserAgent;
}
return result;
}
}
The SocksHttpWebRequest class is taken from the blog linked to by #casperOne, the code for which is as follows:
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Org.Mentalis.Network.ProxySocket;
namespace Ditrans
{
public class SocksHttpWebRequest : WebRequest
{
#region Member Variables
private readonly Uri _requestUri;
private WebHeaderCollection _requestHeaders;
private string _method;
private SocksHttpWebResponse _response;
private string _requestMessage;
private byte[] _requestContentBuffer;
// darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb)
static readonly StringCollection validHttpVerbs =
new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" };
#endregion
#region Constructor
private SocksHttpWebRequest(Uri requestUri)
{
_requestUri = requestUri;
}
#endregion
#region WebRequest Members
public override WebResponse GetResponse()
{
if (Proxy == null)
{
throw new InvalidOperationException("Proxy property cannot be null.");
}
if (String.IsNullOrEmpty(Method))
{
throw new InvalidOperationException("Method has not been set.");
}
if (RequestSubmitted)
{
return _response;
}
_response = InternalGetResponse();
RequestSubmitted = true;
return _response;
}
public override Uri RequestUri
{
get { return _requestUri; }
}
public override IWebProxy Proxy { get; set; }
public override WebHeaderCollection Headers
{
get
{
if (_requestHeaders == null)
{
_requestHeaders = new WebHeaderCollection();
}
return _requestHeaders;
}
set
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
_requestHeaders = value;
}
}
public bool RequestSubmitted { get; private set; }
public override string Method
{
get
{
return _method ?? "GET";
}
set
{
if (validHttpVerbs.Contains(value))
{
_method = value;
}
else
{
throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value));
}
}
}
public override long ContentLength { get; set; }
public override string ContentType { get; set; }
public override Stream GetRequestStream()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
if (_requestContentBuffer == null)
{
_requestContentBuffer = new byte[ContentLength];
}
else if (ContentLength == default(long))
{
_requestContentBuffer = new byte[int.MaxValue];
}
else if (_requestContentBuffer.Length != ContentLength)
{
Array.Resize(ref _requestContentBuffer, (int) ContentLength);
}
return new MemoryStream(_requestContentBuffer);
}
#endregion
#region Methods
public static new WebRequest Create(string requestUri)
{
return new SocksHttpWebRequest(new Uri(requestUri));
}
public static new WebRequest Create(Uri requestUri)
{
return new SocksHttpWebRequest(requestUri);
}
private string BuildHttpRequestMessage()
{
if (RequestSubmitted)
{
throw new InvalidOperationException("This operation cannot be performed after the request has been submitted.");
}
var message = new StringBuilder();
message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host);
// add the headers
foreach (var key in Headers.Keys)
{
message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]);
}
if (!string.IsNullOrEmpty(ContentType))
{
message.AppendFormat("Content-Type: {0}\r\n", ContentType);
}
if (ContentLength > 0)
{
message.AppendFormat("Content-Length: {0}\r\n", ContentLength);
}
// add a blank line to indicate the end of the headers
message.Append("\r\n");
// add content
if(_requestContentBuffer != null && _requestContentBuffer.Length > 0)
{
using (var stream = new MemoryStream(_requestContentBuffer, false))
{
using (var reader = new StreamReader(stream))
{
message.Append(reader.ReadToEnd());
}
}
}
return message.ToString();
}
private SocksHttpWebResponse InternalGetResponse()
{
var response = new StringBuilder();
using (var _socksConnection =
new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var proxyUri = Proxy.GetProxy(RequestUri);
var ipAddress = GetProxyIpAddress(proxyUri);
_socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port);
_socksConnection.ProxyType = ProxyTypes.Socks5;
// open connection
_socksConnection.Connect(RequestUri.Host, 80);
// send an HTTP request
_socksConnection.Send(Encoding.ASCII.GetBytes(RequestMessage));
// read the HTTP reply
var buffer = new byte[1024];
var bytesReceived = _socksConnection.Receive(buffer);
while (bytesReceived > 0)
{
response.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
bytesReceived = _socksConnection.Receive(buffer);
}
}
return new SocksHttpWebResponse(response.ToString());
}
private static IPAddress GetProxyIpAddress(Uri proxyUri)
{
IPAddress ipAddress;
if (!IPAddress.TryParse(proxyUri.Host, out ipAddress))
{
try
{
return Dns.GetHostEntry(proxyUri.Host).AddressList[0];
}
catch (Exception e)
{
throw new InvalidOperationException(
string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e);
}
}
return ipAddress;
}
#endregion
#region Properties
public string RequestMessage
{
get
{
if (string.IsNullOrEmpty(_requestMessage))
{
_requestMessage = BuildHttpRequestMessage();
}
return _requestMessage;
}
}
#endregion
}
}
Note that, as #casperOne pointed out, this makes use of a (free) third party library called ProxySocket.
I came across this aswell and found the nice BetterHttpClient
It derives from WebClient and allows you to specify a socks proxy:
BetterHttpClient.HttpClient client = new BetterHttpClient.HttpClient(new BetterHttpClient.Proxy("IP address", port, BetterHttpClient.ProxyTypeEnum.Socks));
I was looking for library in order to do this. finally i found the MihaZupan/HttpToSocks5Proxy it's a real lifesaver. just like this you can use it:
using MihaZupan;
var proxy = new HttpToSocks5Proxy("127.0.0.1", 1080,
"username", "password" // optional
);
var handler = new HttpClientHandler { Proxy = proxy };
HttpClient httpClient = new HttpClient(handler, true);
var result = await httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Get, "https://httpbin.org/ip"));
Console.WriteLine("HTTPS GET: " + await result.Content.ReadAsStringAsync());
If you want a more convenient, WebClient-esque API for reading the content, you can wrap the response-read calls into an extension method:
public static class ExtensionMethods
{
public static async Task<string> DownloadStringAsync(this HttpClient client, string url)
{
var response = await client.SendAsync(new HttpRequestMessage(
HttpMethod.Get, url));
return await response.Content.ReadAsStringAsync();
}
}
For future readers:
Since .NET 6, socks proxies are supported natively with HttpClient.
var handler = new HttpClientHandler
{
Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);
Note:
In time of writing this answer, WebClient is considered as obsolete. HttpClient should be used instead.
Try Yove.Proxy. Example:
using (var w = new WebClient())
{
using (var proxy = new ProxyClient("67.201.33.10", 25283, ProxyType.Socks5))
{
w.Proxy = proxy;
Console.WriteLine(w.DownloadString("https://api.ipify.org"));
}
}
Update: with help from Henk, determined that public Dispose() is being called, which in turn calls private Dispose(true). This is my first implementation of IDisposable interface, so not sure if it is correct. I don't call Dispose explicitly anywhere. It seems that WCF architecture is calling it on exit from each OperationContract member.
Took unmanaged cleanup code out of Dispose for now, and multiple calls are able to access the static data. It seems that Dispose() is called on all locally allocated objects on return from calls, even if there is a reference to object in static storage. Not sure in .net world how to get around this so that IDisposable interface will get callled correctly. I'm guessing that these objects will get garbage collected at some point also.
Here is call stack on return from 1st call when Dispose is being called:
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose(bool
bDisposing = true) Line 119 C#
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose()
Line 107 + 0xd bytes C#
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParametersCore()
+ 0x56 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParameters()
+ 0xf bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc =
{System.ServiceModel.Dispatcher.MessageRpc})
+ 0x135 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc =
{System.ServiceModel.Dispatcher.MessageRpc})
+ 0x1bf bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x80 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x36 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0xd7 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool
isOperationContextSet = false) + 0x9b
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc, bool isOperationContextSet) +
0x2d bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext
request =
{System.ServiceModel.Security.SecuritySessionServerSettings.SecuritySessionRequestContext},
bool cleanThread,
System.ServiceModel.OperationContext
currentOperationContext) + 0x20c
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext
request,
System.ServiceModel.OperationContext
currentOperationContext) + 0xdf bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult
result) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnContinueAsyncReceive(object
state) + 0x45 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
+ 0x46 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object
o) + 0x28 bytes
mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext
securityContext,
System.Threading.ContextCallback
callback, object state) + 0x55 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
+ 0x4d bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
+ 0x180 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(object
state) + 0x7a bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0xf bytes
SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint
error, uint bytesRead,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0x3d bytes
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
pOVERLAP) + 0x54 bytes
I read some posts on caching static data in a WCF service implementation class, and was having a problem with the GC calling dispose on the objects in a static Dictionary. I am referencing some activex objects from IBM iSeries Access, so I implemented IDisposable interface to clean up connection to iSeries. My problem is the GC is Disposing of objects in the Static members of the Service class. Not sure all the code was required, but here it is anyway. The problem is that on return from each OperationContract method, the GC is calling Dispose on the ISeriesSystem or Queue object that was added to the associated Dictionary, but the ISeriesSystem Dictionary is static, so I thought that it held a reference to the object, so GC wouldn't be done until it is removed from Dictionary.
Service Interface:
[ServiceContract(Namespace="BossISeriesCwbxServices")]
public interface IDataQueueService
{
[OperationContract]
ISeriesSystem SystemInitialize(string sISeriesName);
[OperationContract(Name="FinalizeSystemByName")]
void SystemFinalize(string sISeriesName);
[OperationContract]
void SystemFinalize(ISeriesSystem oISeriesSystem);
[OperationContract]
Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract(Name="FinalizeQueueByName")]
void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract]
void QueueFinalize(Queue oDataQueue);
[OperationContract (Name="QueueWriteByName")]
void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage);
[OperationContract]
void QueueWrite(Queue oDataQueue, string sMessage);
[OperationContract (Name="QueueReadByName")]
string QueueRead(string sQueueName, string sLibrary, string sISeriesName);
[OperationContract]
string QueueRead(Queue oDataQueue);
}
Service Implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class DataQueueService : IDataQueueService
{
private static Dictionary<string, ISeriesSystem> mdictISeriesSystems = new Dictionary<string, ISeriesSystem>();
public static IDictionary<string, ISeriesSystem> ISeriesDict
{
get { return mdictISeriesSystems; }
}
public ISeriesSystem SystemInitialize(string sISeriesName)
{
ISeriesSystem oISeriesSystem = AddSystem(sISeriesName);
return oISeriesSystem;
}
public void SystemFinalize(string sISeriesName)
{
}
public void SystemFinalize(ISeriesSystem oISeriesSystem)
{
SystemFinalize(oISeriesSystem.Name);
}
public Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName)
{
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
try
{
oISeriesSystem = AddSystem(sISeriesName);
oDataQueue = oISeriesSystem.AddQueue(sQueueName, sLibrary);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
oDataQueue = null;
}
return oDataQueue;
}
public Queue QueueInitialize(string sQueueName, string sLibrary, ISeriesSystem oISeriesSystem)
{
return QueueInitialize(sQueueName, sLibrary, oISeriesSystem.Name);
}
public void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue.Dispose();
oDataQueue = null;
oISeriesSystem.DataQueueDict.Remove(sDataQueueKey);
}
if (oISeriesSystem.DataQueueDict.Count == 0)
{
oISeriesSystem.Dispose();
oISeriesSystem = null;
}
}
}
public void QueueFinalize(Queue oDataQueue)
{
QueueFinalize(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);
}
public void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue.Write(sMessage);
}
}
}
public void QueueWrite(Queue oDataQueue, string sMessage)
{
QueueWrite(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName, sMessage);
}
public string QueueRead(string sQueueName, string sLibrary, string sISeriesName)
{
string sISeriesKey = sISeriesName.Trim();
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
return oDataQueue.Read();
}
}
return "";
}
public string QueueRead(Queue oDataQueue)
{
return QueueRead(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);
}
ISeriesSystem AddSystem(string sISeriesName)
{
ISeriesSystem oISeriesSystem = null;
string sISeriesKey = sISeriesName.Trim();
if (!DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
{
oISeriesSystem = new ISeriesSystem(sISeriesName);
DataQueueService.ISeriesDict[sISeriesKey] = oISeriesSystem;
}
return oISeriesSystem;
}
ISeriesSystem DataContract:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using cwbx;
namespace BossISeriesCwbxServices.DataContracts
{
public class ISeriesSystem : IDisposable
{
private string msName;
[DataMember]
public string Name
{
get { return msName; }
set { msName = value; }
}
private Dictionary<string, Queue> mdictDataQueues = new Dictionary<string, Queue>();
public IDictionary<string, Queue> DataQueueDict
{
get { return mdictDataQueues; }
}
private cwbx.AS400System mcwbxISeriesSystem = new AS400System();
private cwbx.AS400System CwbxISeriesSystem
{
get { return mcwbxISeriesSystem; }
set { mcwbxISeriesSystem = value; }
}
private bool bDisposed = false;
public ISeriesSystem()
{
}
public ISeriesSystem(string sISeriesName)
{
try
{
// Set DataContract properties.
this.Name = sISeriesName;
// Connect to iSeries, Logon and connect to iSeries services that may be used.
this.CwbxISeriesSystem.Define(sISeriesName);
this.CwbxISeriesSystem.UserID = "APP1DAK";
this.CwbxISeriesSystem.Password = "DONNA99";
this.CwbxISeriesSystem.Signon();
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceDataQueues);
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceSecurity);
this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
}
~ISeriesSystem()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
// Only Dispose of the object 1 time.
if (!this.bDisposed)
{
// If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
if (bDisposing)
{
// Dispose managed resources, calling object Dispose method for objects
// that implement IDisposable interface.
}
try
{
if (this.CwbxISeriesSystem.IsConnected(cwbcoServiceEnum.cwbcoServiceAny) == 1)
{
this.CwbxISeriesSystem.Disconnect(cwbcoServiceEnum.cwbcoServiceAll);
}
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
// Mark disposing as being done.
bDisposed = true;
}
}
public Queue AddQueue(string sQueueName, string sLibrary)
{
Queue oDataQueue = null;
string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();
if (!this.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
{
oDataQueue = new Queue(sQueueName, sLibrary, this.CwbxISeriesSystem);
this.DataQueueDict[sDataQueueKey] = oDataQueue;
}
return oDataQueue;
}
}
}
Queue DataContract:
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
using cwbx;
namespace BossISeriesCwbxServices.DataContracts
{
[DataContract]
public class Queue : IDisposable
{
private string msName;
[DataMember]
public string Name
{
get { return msName; }
set { msName = value; }
}
private string msLibrary;
[DataMember]
public string Library
{
get { return msLibrary; }
set { msLibrary = value; }
}
private string msISeriesName;
[DataMember]
public string ISeriesName
{
get { return msISeriesName; }
set { msISeriesName = value; }
}
private short miWaitTime = 10;
[DataMember]
public short WaitTime
{
get { return miWaitTime; }
set { miWaitTime = value; }
}
private short miNumberOfAttempts = 1;
[DataMember]
public short NumberOfAttempts
{
get { return miNumberOfAttempts; }
set { miNumberOfAttempts = value; }
}
private short miMaxQueueIndex = 1;
public short MaxQueueIndex
{
get { return miMaxQueueIndex; }
set { miMaxQueueIndex = value; }
}
private short miCurrentQueueIndex = 1;
public short CurrentQueueIndex
{
get { return miCurrentQueueIndex; }
set { miCurrentQueueIndex = value; }
}
private cwbx.DataQueue mcwbxDataQueue = new cwbx.DataQueue();
private cwbx.DataQueue CwbxDataQueue
{
get { return mcwbxDataQueue; }
set { mcwbxDataQueue = value; }
}
private bool bDisposed = false;
public Queue()
{
}
public Queue(string sQueueName, string sLibrary, cwbx.AS400System cwbxISeriesSystem)
{
this.Name = sQueueName;
this.Library = sLibrary;
this.ISeriesName = cwbxISeriesSystem.SystemName;
this.CwbxDataQueue.QueueName = sQueueName;
this.CwbxDataQueue.LibraryName = sLibrary;
this.CwbxDataQueue.system = cwbxISeriesSystem;
}
~Queue()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
// Only Dispose of the object 1 time.
if (!this.bDisposed)
{
// If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
if (bDisposing)
{
// Dispose managed resources, calling object Dispose method for objects
// that implement IDisposable interface.
}
// Call the appropriate methods to clean up unmanaged resources here.
try
{
this.CwbxDataQueue = null;
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
// Mark disposing as being done.
bDisposed = true;
}
}
public void Write(string sMessage)
{
try
{
cwbx.StringConverter cwbxStringConverter = new cwbx.StringConverter();
Object oBytes = cwbxStringConverter.ToBytes(sMessage);
this.CwbxDataQueue.Write(oBytes, false);
}
catch (Exception ex)
{
// ToDo: Log ex to WCF service log and remove from Console.
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
}
}
public string Read()
{
try
{
Object oObject = null;
return (new cwbx.StringConverter()).FromBytes(this.CwbxDataQueue.Read(this.WaitTime * this.NumberOfAttempts, out oObject));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
{
Console.WriteLine(cwbxError.Text);
Console.WriteLine(cwbxError.ToString());
}
return "";
}
}
}
}
Client Code:
ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;
oISeriesSystem = DQService.SystemInitialize("A2029D2.AS400.US.UPS.COM");
oDataQueue = DQService.QueueInitialize("SMTLST020", "IB5EXE", oISeriesSystem.Name);
oISeriesSystem.DataQueueDict.Add(oDataQueue.Library + oDataQueue.Name, oDataQueue);
ISeriesSystemDict.Add(oISeriesSystem.Name, oISeriesSystem);
DQService.QueueWrite(oDataQueue, "Testing cwbx.DataQueue WCF service");
string sMessage = DQService.QueueRead(oDataQueue);
Exe Hosted Service:
Uri baseAddress = new Uri("http://localhost:8080/BossISeriesCwbxServices");
//Instantiate new ServiceHost
moServiceHost = new ServiceHost(typeof(BossISeriesCwbxServices.DataQueueService), baseAddress);
// Add Endpoint
moServiceHost.AddServiceEndpoint(typeof(BossISeriesCwbxServices.IDataQueueService), new WSHttpBinding(), "IDataQueueService");
// Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
moServiceHost.Description.Behaviors.Add(smb);
//Open moServiceHost
moServiceHost.Open();
Console.WriteLine("The IDataQueueService is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Does it happen regularly or every now and then? Does it happen under development or only (after some time) on the production server?
When you host this under IIS, the server might decide to 'recycle' your app. Basic advice: don't use static in server apps. It's not reliable and not scalable.
Edit
OK, I've read a little more (but not all).
The problem is that on return from
each OperationContract method, the GC
is calling Dispose on the
ISeriesSystem or Queue object
You should verify that in great detail. Is it really the GC that calls your Finalizer(s) (aka destructors)? You should use logging or debugging to verify that the overload Dispose(false) is being called. If Dispose(true) is called (and I see a lot of code involved in doing that) you should stacktrace to the actual cause.
Can you put the following code :
[ServiceContract(SessionMode = SessionMode.Required)]
on your service contract just to make sure the transport session is being created? If it isn't this should throw an error.
WSHttpBinding does not create a transport session without security and reliable session.
EDIT :
Other than that, since you are creating a Single Instance service, why would you need static members? There is only going to be one instance alive, so, any instance members of that instance will live with it and will be kind of static. Have you thought about that or there is a specific reason for using static members?
I know it's a bit late but have you tried adding the [OperationBehavior(AutoDisposeParameters = false)] attribute?
Link to MSDN Forum