Best way to implement a request response pattern that is extendable - c#

OK,
I have to create a C# library that can send commands to a device and process the command specific responses and broadcasts over a serial port (or other communications method). The library must also be able to handle request and response extensions held in other libraries as certain devices implement an extended command set, but it must be possible to choose whether these extended commands are utilised (I guess using reflection in the client app). I have created a class of type Packet that is able to create the packet and add its payload, calculate its checksum and write the packet to the stream.
public class Packet
{
internal PacketHeaderType Header { get; private set; }
internal List<byte> Payload { get; private set; }
protected int PayloadLength { get { return Payload.Count; } }
protected byte HeaderByte { get { return (byte)((Convert.ToByte(Header) << 4) | PayloadLength); } } //we need to add the packet length to the lower nibble of the header before sending
public Packet(PacketHeaderType header, List<byte> payload)
{
this.Header = header;
this.Payload = new List<byte>(payload);
}
public Packet(PacketHeaderType headerByte)
{
this.Header = headerByte;
this.Payload = new List<byte>();
}
internal byte XorByte
{
get
{
Byte xorByte = Convert.ToByte(HeaderByte);
for (int i = 0; i < PayloadLength; i++)
xorByte ^= Payload.ToArray()[i];
return xorByte;
}
}
public async Task WriteAsync(Stream stream, bool flush = true, CancellationToken token = default(CancellationToken))
{
var buffer = new List<byte>();
buffer.Add(HeaderByte);
if (Payload != null && PayloadLength > 0)
{
buffer.AddRange(Payload);
}
buffer.Add(XorByte);
await stream.WriteAsync(buffer.ToArray(), 0, buffer.Count);
if (flush)
{
await stream.FlushAsync();
}
}
}
I have also created child classes that implement Type packet for each of the valid commands.
Finally I have also created a class of type PacketHandler that is able to read bytes from a stream and create it into a packet object.
The way I envisage using the library would be like this:
public async string GetCmdStnSoftwareVersion()
{
var msgReq = new CmdStnSoftwareVersionReqMessage();
await msgReq.WriteAsync(sPort.BaseStream);
await var response = msgReq.GetResponse(5); //5 = timeout in seconds!
return String.Format("{0}.{1}", response.Major, response.Minor);
}
What I am stuck on is a good pattern and/or example implementation for handling responses which is compatible with implementing the extension libraries. Can anyone provide input?

Related

How can I return XML from BeforeSendRequest and AfterReceiveReply to the calling method in a thread-safe way?

We have a console application using the Azure WebJob SDK. The WebJob relies on a WCF service using SOAP, which it accesses through a DLL we wrote that wraps the auto-generated WCF types in something a bit more friendly.
For logging purposes, we want to save the request and response XML bodies for requests that we make. These XML bodies would be saved in our database. But, because the WCF code lives in a low-level DLL, it has no concept of our database and can't save to it.
The DLL uses Microsoft's DI extensions to register types, and the WebJob calls into it like this:
class WebJobClass
{
IWCFWrapperClient _wcfWrapperClient;
public WebJobClass(IWCFWrapperClient wcfWrapperClient)
{
_wcfWrapperClient = wcfWrapperClient;
}
public async Task DoThing()
{
var callResult = await _wcfWrapperClient.CallWCFService();
}
}
IWCFWrapperClient looks like this:
class WCFWrapperClient : IWCFWrapperClient
{
IWCF _wcf; // auto-generated by VS, stored in Reference.cs
public async Task<object> CallWCFService()
{
return await _wcf.Call(); // another auto-generated method
}
}
I've implemented an IClientMessageInspector, and it works fine to get me the XML request/response, but I don't have a way to pass it back up to WCFWrapperClient.CallWCFService so that it can be returned to WebJobClass.DoThing(), who could then save it to the database.
The problem is multithreading. WebJobs, IIRC, will run multiple requests in parallel, calling into the DLL from multiple threads. This means we can't, say, share a static property LastRequestXmlBody since multiple threads could overwrite it. We also can't, say, give each call a Guid or something since there's no way to pass anything from IWCFWrapperClient.CallWCFService into the auto-generated IWCF.Call except what was auto-generated.
So, how can I return XML to WebJobClass.DoThing in a thread-safe way?
I was able to find a solution that uses ConcurrentDictionary<TKey, TValue>, but it's a bit ugly.
First, I amended the auto-generated classes in Reference.cs with a new property Guid InternalCorrelationId. Since the auto-generated classes are partial, this can be done in separate files that aren't changed when the client is regenerated.
public partial class AutoGeneratedWCFType
{
private Guid InternalCorrelationIdField;
[System.Runtime.Serialization.DataMember()]
public Guid InternalCorrelationId
{
get { return InternalCorrelationIdField; }
set { InternalCorrelationIdField = value; }
}
}
Next, I made all my request DTO types derive from a type named RequestBase, and all my response DTO types derive from a typed named ResponseBase, so I could handle them generically:
public abstract class RequestBase
{
public Guid InternalCorrelationId { get; set; }
}
public abstract class ResponseBase
{
public string RequestXml { get; set; }
public string ResponseXml { get; set; }
}
I then added a type RequestCorrelator that simply holds on to a ConcurrentDictionary<Guid, XmlRequestResponse>:
public sealed class RequestCorrelator : IRequestCorrelator
{
public ConcurrentDictionary<Guid, XmlRequestResponse> PendingCalls { get; }
public RequestCorrelator() => PendingCalls = new ConcurrentDictionary<Guid, XmlRequestResponse>();
}
public sealed class XmlRequestResponse
{
public string RequestXml { get; set; }
public string ResponseXml { get; set; }
}
RequestCorrelator is its own type for DI purposes - you may just be able to use a ConcurrentDictionary<TKey, TValue> directly.
Finally, we have the code that actually grabs the XML, a type implementing IClientMessageInspector:
public sealed class ClientMessageProvider : IClientMessageInspector
{
private readonly IRequestCorrelator _requestCorrelator;
public ClientMessageProvider(IRequestCorrelator requestCorrelator) =>
_requestCorrelator = requestCorrelator;
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var requestXml = request.ToString();
var internalCorrelationId = GetInternalCorrelationId(requestXml);
if (internalCorrelationId != null)
{
if (_requestCorrelator.PendingCalls.TryGetValue(internalCorrelationId.Value,
out var requestResponse))
{
requestResponse.RequestXml = requestXml;
}
request = RemoveInternalCorrelationId(request);
}
return internalCorrelationId;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// WCF can internally correlate a request between BeforeSendRequest and
// AfterReceiveReply. We reuse the same correlation ID we added to the
// XML as our correlation state.
var responseXml = reply.ToString();
var internalCorrelationId = (correlationState is Guid guid)
? guid
: default;
if (_requestCorrelator.PendingCalls.TryGetValue(internalCorrelationId,
out var requestResponse))
{
requestResponse.ResponseXml = responseXml;
}
}
private static Guid? GetInternalCorrelationId(string requestXml)
{
var document = XDocument.Parse(requestXml);
var internalCorrelationIdElement = /* You'll have to write this yourself;
every WCF XML request is different. */
return internalCorrelationIdElement != null
? Guid.Parse(internalCorrelationIdElement.Value)
: null;
}
private static Message RemoveInternalCorrelationId(Message oldMessage)
{
// https://stackoverflow.com/a/35639900/2709212
var buffer = oldMessage.CreateBufferedCopy(2 * 1024 * 1024);
var tempMessage = buffer.CreateMessage();
var dictionaryReader = tempMessage.GetReaderAtBodyContents();
var document = new XmlDocument();
document.Load(dictionaryReader);
dictionaryReader.Close();
var internalCorrelationIdNode = /* You'll also have to write this yourself. */
var parent = internalCorrelationIdNode.ParentNode;
parent.RemoveChild(internalCorrelationIdNode);
var memoryStream = new MemoryStream();
var xmlWriter = XmlWriter.Create(memoryStream);
document.Save(xmlWriter);
xmlWriter.Flush();
xmlWriter.Close();
memoryStream.Position = 0;
var xmlReader = XmlReader.Create(memoryStream);
var newMessage = Message.CreateMessage(oldMessage.Version, null, xmlReader);
newMessage.Headers.CopyHeadersFrom(oldMessage);
newMessage.Properties.CopyProperties(oldMessage.Properties);
return newMessage;
}
}
In short, this type:
Finds the correlation ID in the XML request.
Finds the XmlRequestResponse with the same correlation ID and adds the request to it.
Removes the correlation ID element so that the service doesn't get elements they didn't expect.
After receiving a reply, uses correlationState to find the XmlRequestResponse and write the response XML to it.
Now all we have to do is change IWCFWrapperClient:
private async Task<TDtoResult> ExecuteCallWithLogging<TDtoRequest,
TWcfRequest,
TWcfResponse,
TDtoResult>(TDtoRequest request,
Func<TDtoRequest, TWcfRequest> dtoToWcfConverter,
Func<TWcfRequest, Task<TWcfResponse>> wcfCall,
Func<TWcfResponse, TDtoResult> wcfToDtoConverter)
where TDtoRequest : CorrelationBase
where TDtoResult : WcfBase
{
request.InternalCorrelationId = Guid.NewGuid();
var xmlRequestResponse = new XmlRequestResponse();
_requestCorrelator.PendingCalls.GetOrAdd(request.InternalCorrelationId,
xmlRequestResponse);
var response = await contractingCall(dtoToWcfConverter(request));
_requestCorrelator.PendingCalls.TryRemove(request.InternalCorrelationId, out _);
return wcfToDtoConverter(response).WithRequestResponse(xmlRequestResponse);
}
public async Task<DoThingResponseDto> DoThing(DoThingRequestDto request) =>
await ExecuteCallWithLogging(request,
r => r.ToWcfModel(),
async d => await _wcf.Call(d),
d => d.ToDtoModel());
WithRequestResponse is implemented as follows:
public static T WithRequestResponse<T>(this T item, XmlRequestResponse requestResponse)
where T : ResponseBase
{
item.RequestXml = requestResponse?.RequestXml;
item.ResponseXml = requestResponse?.ResponseXml;
return item;
}
And there we go. WCF calls that return their XML in the response object rather than just something you can print to console or log to a file.

WebSocketReceiveResult force to receive binary

I am using Websocket.Client which is a wrapper around ClientWebSocket, within its internals I can see WebSocketReceiveResult and it is using it to detect the message contents result.MessageType == WebSocketMessageType.Text.
Is there any way to force a web socker to only to receive binary messages (not do any conversion to string?), or is the data format specified by the server?
namespace Websocket.Client
{
//
// Summary:
// Received message, could be Text or Binary
public class ResponseMessage
{
//
// Summary:
// Received text message (only if type = WebSocketMessageType.Text)
public string Text { get; }
//
// Summary:
// Received text message (only if type = WebSocketMessageType.Binary)
public byte[] Binary { get; }
//
// Summary:
// Current message type (Text or Binary)
public WebSocketMessageType MessageType { get; }
//
// Summary:
// Create binary response message
public static ResponseMessage BinaryMessage(byte[] data);
//
// Summary:
// Create text response message
public static ResponseMessage TextMessage(string data);
//
// Summary:
// Return string info about the message
public override string ToString();
}
}
This feature was implemented by the author of Websocket.Client within a few hours of posting on the github page... amazing!
using var client = new WebsocketClient()
{
IsTextMessageConversionEnabled = false
};

.NET SslStream: How to extract session key?

I'm writing a desktop application and wish to give users ability to verify network traffic, so they know they are not being abused. My application establishes a TLS connection to servers using .NET's SslStream with the AuthenticateAsClient method. Wireshark users can decode TLS traffic using NSS key logs. I can see that both Firefox and Chrome have options to logging encryption keys. How can I do the same in my .NET application? i.e. How can I extract the session key from SslStream, programatically?
As of this writing, there is no way of doing that with dotnet's SslStream. Here's how to export the session keys using BouncyCastle:
internal static class BouncyCastleTls
{
public static Stream WrapWithTls(Stream stream)
{
var client = new MyTlsClient();
var tlsClientProtocol = new TlsClientProtocol(stream, new SecureRandom());
tlsClientProtocol.Connect(client);
return tlsClientProtocol.Stream;
}
}
internal sealed class MyTlsClient : DefaultTlsClient
{
public override TlsAuthentication GetAuthentication()
{
return new MyTlsAuthentication();
}
public override void NotifyHandshakeComplete()
{
var clientRandom = mContext.SecurityParameters.ClientRandom;
var masterSecret = mContext.SecurityParameters.MasterSecret;
Console.WriteLine("CLIENT_RANDOM {0} {1}", ToHex(clientRandom), ToHex(masterSecret));
}
private static string ToHex(byte[] bytes)
{
var sb = new StringBuilder(bytes.Length * 2);
for (var i = 0; i < bytes.Length; ++i)
sb.Append($"{bytes[i]:x2}");
return sb.ToString();
}
}
internal sealed class MyTlsAuthentication : TlsAuthentication
{
public void NotifyServerCertificate(Certificate serverCertificate)
{
}
public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
{
return null;
}
}

Is there a construct or pattern similar to C# `using` which will return an object?

I have a WCF message inspector which inspects requests and responses: Message. The inspector works fine. A Message object can only be read once so once you read it, you cannot simply propagate as WCF will complain that the message has been read. Therefore, I am creating a brand new copy of the message and propagating that.
I have designed a class that allows message reading and after the caller has read whatever they want, they need to call Close which will return a copy of the message. Here is the skeleton of my class:
using System.ServiceModel.Channels;
internal abstract class MessageReader
{
internal string ReadSomething(string id)
{
// Return string
}
internal string ReadSomethingElse(string id)
{
// Return string
}
internal Message Close()
{
// Create copy and return it.
}
}
Users of my class may forget to call Close() which is fine because WCF will yell at them. Right now I have documentation to let users know they need to call Close().
Here is the question
Is there a pattern, or something similar, to C#'s using construct but one which returns an object at the end? This will be really convenient because then users of my class can just use a construct like that and at the end it will return the copy of the message. Something like this:
UsingSomeConstruct(var reader = new MessageReader(ref originalMessage))
{
var a = reader.ReadSomething("something");
var b = reader.ReadSomethingElse("something");
// Do something with what was read
}
// At this point originalMessage will be the copy of the message and no longer the original message.
EDIT
I thought about hacking IDisposable to achieve this but I am NOT going to do it that way so looking for other ideas.
There is no such language construct of course.
What I could suggest is to use IDisposable for cleaning up, and add ref Message message argument to each ReadXXX method. I know it will not be so convenient for your users, but from the other side they cannot forget passing the parameter.
So the implementation would be something like this:
internal class MessageReader : IDisposable
{
private MessageBuffer buffer;
private Message message;
private void Release()
{
if (buffer == null) return;
buffer.Close();
buffer = null;
message = null;
}
protected void OnReadRequest(ref Message message)
{
if (message == null) throw new ArgumentNullException("message");
if (this.message == message) return;
Release();
this.buffer = message.CreateBufferedCopy(int.MaxValue);
message = this.message = buffer.CreateMessage();
}
public void Dispose()
{
Release();
}
internal string ReadSomething(ref Message message, string id)
{
OnReadRequest(ref message);
// Return string
}
internal string ReadSomethingElse(ref Message message, string id)
{
OnReadRequest(ref message);
// Return string
}
}
and the sample usage:
using (var reader = new MessageReader())
{
var a = reader.ReadSomething(ref originalMessage, "something");
var b = reader.ReadSomethingElse(ref originalMessage, "something");
// Do something with what was read
}
// At this point originalMessage will be the copy of the message and no longer the original message.
The way I'd do this is as follows:
public MessageReader: IDisposable
{
public static MessageReader Create(ref Message message)
{
var buffer = message.CreateBufferedCopy(/*whatever is fit*/);
try
{
var reader = new MessageReader(buffer);
message = buffer.CreateMessage();
return reader;
}
catch
{
buffer.Close();
throw;
}
}
private readonly MessageBuffer buffer;
private bool disposed;
private MessageReader(MessageBuffer buffer) { this.buffer = buffer; }
public void Dispose()
{
if (disposed)
return;
buffer.Close();
disposed = true;
}
public string Read(string id)
{
var newCopy = buffer.CreateMessage();
//work with new copy...
}
}
And you'd simply use it like this:
using (var reader = MessageReader.Create(ref message))
//message here is already an untouched copy with no need of user active
//intervention and is never touched again by the reader.
{
var a = reader.Read("something"); //reads copy
...
}
IMHO, this is as clean as it can be. Note that MessageReader implements IDisposable exclusively because it holds a reference to the disposable private MessageBuffer.
Thanks to all the help from #InBetween, #quetzalcoatl, and #Ivan Stoev. Upvoted your answers because it helped me arrive at the following.
In the constructor, I create a copy of the message and set the original message to the copy. Since the status of this message is Created WCF will be happy propogating it. I create another copy and use that for reading multiple times.
#Ivan said but what if the user does not ask for anything to be read then the copying was wasted work. That is a good point but in my case, this is an interceptor and all messages are intercepted to be read.
Here is the code I ended up with suggestions from all of you:
public class MessageReader : IDisposable {
private readonly Message message;
public MessageReader(ref Message originalMessage) {
using( var buffer = originalMessage.CreateBufferedCopy( int.MaxValue ) ) {
// Keep original message for reading
this.message = buffer.CreateMessage();
// Set original message to a copy of the original
originalMessage = buffer.CreateMessage();
}
}
public int ReadSomething(string id) {
// Read from this.message;
}
public int ReadSomethingElse(string id) {
// Read from this.message;
}
public void Close() {
this.Dispose();
}
public void Dispose() {
this.message.Close();
}
}
The caller can either use it in a using block or without it. The using block is used for good reasons and not as a hack.
public object AfterReceiveRequest(ref Message request, IClientChannel channel,
InstanceContext instanceContext) {
try {
using( var rdr = new MessageReader(ref request) ) {
var value= rdr.ReadSomething( someIdentifier );
return value;
}
}
catch( System.Exception ex ) {
throw CreateFault( ex, request );
}
}
Nope, there is no such construct. It is simply too specific to exist there out of the box. There are extension methods which often are very helpful, but you won't be able to use them on this ref Message parameter..
However, if you are willing to use ref at all, then why dont simply include all that logic it in Reader's constructor?
Here's an example, somewhat contrived, but it should show what I mean. Like others mentioned in comments, I also suggest implementing IDisposable on the Reader object instead of Close, so I included that already.
TL;DR: In example below, the most important thing is in Reader(ref msg) constructor which clones the message, copies the data, and replaces the original message with a safe-message class which can be read many times..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
// real-world variables, keep them typed as base Message
// to be able to silently replace them with different objects
Message original1;
Message original2;
// let's construct some one-time readable messages
{
var tmp1 = new OneTimeMessage();
tmp1.data["mom"] = "dad";
tmp1.data["cat"] = "dog";
original1 = tmp1;
var tmp2 = new OneTimeMessage();
tmp2.data["mom"] = "dad";
tmp2.data["cat"] = "dog";
original2 = tmp2;
}
// test1 - can't read twice
Console.WriteLine("test0A:" + original1.GetData("mom"));
//Console.WriteLine("test0B:" + original1.GetData("mom")); // fail
// test2 - can read twice with Reader's help
var backup1 = original2;
using(var rd1 = new Reader(ref original2))
{
Console.WriteLine("test1A:" + rd1.ReadSomething("mom"));
}
var backup2 = original2;
using(var rd2 = new Reader(ref original2))
{
Console.WriteLine("test1A:" + rd2.ReadSomething("mom"));
//^ ok - becase Reader replaced 'original2' with SafeMessage
}
// test3: Reader's ctor is intelligent
// so no more SafeMessages created during future usage
var backup3 = original2;
using(var rd3 = new Reader(ref original2))
{
}
var backup4 = original2;
using(var rd4 = new Reader(ref original2))
{
}
Console.WriteLine("checking for copies:" + (original2 == backup1));
Console.WriteLine("checking for copies:" + (original2 == backup2));
Console.WriteLine("checking for copies:" + (original2 == backup3));
Console.WriteLine("checking for copies:" + (original2 == backup4));
}
}
}
public abstract class Message
{
public abstract string GetData(string id);
}
public class OneTimeMessage : Message // this models your current one-time-readable message
{
public IDictionary<string, string> data = new Dictionary<string, string>();
public override string GetData(string id)
{
var tmp = data[id];
data.Remove(id);
// that's nonsense, but I want to show that you can't
// read the same thing twice from this object
return tmp;
}
}
public class SafeMessage : Message
{
public IDictionary<string, string> data;
public override String GetData(string id)
{
return data[id];
}
public SafeMessage(Message msg)
{
// read out the full msg's data and store it
// since this is example, we can do it in a pretty simple way
// in your code that will probably be more complex
this.data = new Dictionary<string,string>(((OneTimeMessage)msg).data);
}
}
public class Reader : IDisposable
{
private Message message;
public Reader(ref Message src)
{
src = src is SafeMessage ? src : new SafeMessage(src);
this.message = src;
}
public string ReadSomething(string id){ return message.GetData(id); }
public void Dispose(){ Close(); }
public void Close(){ message=null; Console.WriteLine("reader closed"); }
}
EDIT: improved example
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Channels;
using System.Text.RegularExpressions;
using System.Xml;
namespace MyProgram
{
public class Program
{
public static void Main(string[] args)
{
// real-world variables, keep them typed as base Message
// to be able to silently replace them with different objects
Message original1;
Message original2;
// let's construct some one-time readable messages
{
original1 = new TheMessage("dad", "dog");
original2 = new TheMessage("dad", "dog");
}
// test1 - can't read twice
Console.WriteLine("test0A:" + original1.GetReaderAtBodyContents().ReadOuterXml());
// Console.WriteLine("test0B:" + original1.GetReaderAtBodyContents().ReadOuterXml()); // fail: InvalidOperationException - it was already read
// test2 - can read ONCE with Reader's help, but the message is replaced and is usable again
var backup1 = original2;
using (var rd1 = new ReaderOnce(ref original2))
{
Console.WriteLine("is message replaced after opening Reader:" + (original2 != backup1));
Console.WriteLine("test1A:" + rd1.ReadBodyXml());
// Console.WriteLine("test1B:" + rd1.ReadBodyXml()); // fail: InvalidOperationException - it was already read
}
// test3 - can read MANY TIMES with ReaderMany's help
// also note we use 'original2' again, which was already used above, so in fact ReaderOnce really works as well
var backup2 = original2;
using (var rd1 = new ReaderMany(ref original2))
{
Console.WriteLine("is message replaced after opening Reader:" + (original2 != backup2));
Console.WriteLine("test2A:" + rd1.ReadBodyXml());
Console.WriteLine("test2B:" + rd1.ReadBodyXml()); // ok
}
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}
// solution1
public class ReaderOnce : IDisposable
{
private Message localCopy;
public ReaderOnce(ref Message src)
{
// create a WCF MessageBuffer to assist in copying messages
// btw. I suppose you should set some sane limit instead of that below
using (var tempBuffer = src.CreateBufferedCopy(int.MaxValue))
{
src = tempBuffer.CreateMessage(); // FIRST copy for outer use
localCopy = tempBuffer.CreateMessage(); // SECOND copy for internal use in the Reader
}
}
public void Dispose() { Close(); }
public void Close()
{
localCopy.Close(); // but that does NOT affect FIRST copy sent to outer scope outside reader
Console.WriteLine("reader closed");
}
public string ReadBodyXml() // careful: that's again ONE TIME readable
{
return localCopy.GetReaderAtBodyContents().ReadOuterXml();
}
}
// solution2
public class ReaderMany : IDisposable
{
private MessageBuffer localBuffer;
public ReaderMany(ref Message src)
{
localBuffer = src.CreateBufferedCopy(int.MaxValue);
src = localBuffer.CreateMessage(); // FIRST copy for outer use
}
public void Dispose() { Close(); }
public void Close()
{
localBuffer.Close();
Console.WriteLine("reader closed");
}
public string ReadBodyXml() // this is readable multiple times
{
using (var tmp = localBuffer.CreateMessage())
return tmp.GetReaderAtBodyContents().ReadOuterXml();
}
}
// let's fake some Message type to have something to test the Reader on
public class TheMessage : Message
{
public override MessageHeaders Headers => _mh;
public override MessageProperties Properties => _mp;
public override MessageVersion Version => _mv;
private MessageHeaders _mh;
private MessageProperties _mp;
private MessageVersion _mv;
private string data1;
private string data2;
// btw. below: surprise! XmlDictionaryWriter is in "System.Runtime.Serialization", not in "System.Xml"
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("foo");
writer.WriteAttributeString("data1", data1);
writer.WriteAttributeString("data2", data2);
writer.WriteEndElement();
}
public TheMessage(string data1, string data2)
{
// remember, this class is just an example, you will work on your own messages you already have
_mv = MessageVersion.Soap12;
_mh = new MessageHeaders(_mv);
_mp = new MessageProperties();
// below: yeah, that's super-naive and wrong, but that's an example
this.data1 = data1;
this.data2 = data2;
}
}
There is no language construct in c# that does what you are asking. As stated in comments, you could abuse IDisposable and the language and use a using block to achieve what you want.
But, I fail see what you are gaining, you are just punting the problem; now users will need to remember to use usinginstead of Close. The latter is simple and clean, the former uses a very known language construct to do something different to what it was thought for, something that will potentially be very confusing.

writing a TCP listener with an object as a param

I understand how to send an object through a TCP connection in C#; I'm using StreamWriter.
How can I recieve this object from the other side?
I have tried StreamReader but it doesn't contain a parameter of type object.
How can this be done?
IMO best solution is to use BinaryReader to read from the stream. Moreover you should write the stream using BinaryWriter class.
If your object is not one of basic types then you have to serialize before sending it through TCP connection.
I can really recommend using ProtoBuf to transform your objects to/from bytes that you can send over the network (i.e. serialization). It's all around much better than the built-in binary serializers (speed, versioning).
It also has friendly helpers for including a length/size prefix to the data written, which is helpful when you need to read the data on the receiving side.
The following is a helper class I've used to do the serialization and network communication, once the connection has been established:
public class Messenger
{
private readonly TcpClient client;
private readonly NetworkStream stream;
public IPEndPoint RemoteEndPoint { get { return (IPEndPoint) client.Client.RemoteEndPoint; } }
public Messenger( TcpClient client )
{
this.client = client;
stream = client.GetStream();
}
#region Send and Receive
public TResponse SendReceive<TRequest, TResponse>( TRequest request ) where TRequest : Message where TResponse : Message
{
Send( request );
return Receive<TResponse>();
}
public void Send<TRequest>( TRequest request ) where TRequest : Message
{
using( var ms = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix( ms, request, PrefixStyle.Fixed32 );
stream.Write( ms.GetBuffer(), 0, (int) ms.Length );
stream.Flush();
}
}
public TResponse Receive<TResponse>() where TResponse : Message
{
try
{
return GetMessage<TResponse>();
}
catch (Exception ex)
{
if (ex is IOException || ex is InvalidOperationException)
{
stream.Dispose();
}
throw;
}
}
#endregion
#region Helpers
private TMessage GetMessage<TMessage>() where TMessage : Message
{
int messageLength = BitConverter.ToInt32(GetBytes(stream, 4), 0);
byte[] data = GetBytes(stream, messageLength);
using (var ms = new MemoryStream(data))
{
return Serializer.Deserialize<TMessage>(ms);
}
}
private static byte[] GetBytes(NetworkStream stream, int length)
{
int bytesRequired = length;
int bytesRead = 0;
var bytes = new byte[length];
do
{
while( !stream.DataAvailable )
Thread.Sleep( 100 );
int read = stream.Read(bytes, bytesRead, bytesRequired);
bytesRequired -= read;
bytesRead += read;
}
while (bytesRequired > 0);
return bytes;
}
#endregion
}
Note: the Serializer class is from the ProtoBuf library.
If you don't want or can't use BinaryFormatter, you will have to deserialize the object yoursekf
Modified example from msdn for Binary Formatter:
Let's say you have you model:
[Serializable] //you could also make the class implement ISerializable
class SomeModel
{
public String Name
{
get;
set;
}
}
you have your NetworkStream:
NetworkStream ns;
basic example of how you'd do the serialization part:
void Serialize()
{
SomeModel myModel = new SomeModel()
{
Name = "mooo"
};
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(ns, myModel);
}
catch (SerializationException e)
{
throw e;
}
}
and as for deserializing
void Deserialize()
{
SomeModel myModel;
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Deserialize the object from the stream and
// assign the reference to the local variable.
myModel = (SomeModel) formatter.Deserialize(ns);
}
catch (SerializationException e)
{
throw e;
}
}
There's one thing you have to take care off.
You have to know when you deserialize, you can't just throw the stream in it and get something back, you'll have to know that what you are going to read is in fact a serialized SomeModel-class.
I don't know how your code looks like so I don't know if this is an issue or not and if it is how to avoid it.
Anyway, your second choice would be to use a BinaryReader.
You'd have to create an instance of you object, and read the data manually.
using(BinaryWriter writer =
new BinaryWriter(ns))
{
binWriter.Write(myModel.Name);
}
reading:
using(BinaryReader reader =
new BinaryReader(ns))
{
try
{
SomeModel myModel = new SomeModel();
myModel.Name = binReader.ReadString();
}
// If the end of the stream is reached before reading
// the four data values, ignore the error and use the
// default settings for the remaining values.
catch(EndOfStreamException e)
{
Console.WriteLine("{0} caught and ignored. " +
"Using default values.", e.GetType().Name);
}
}

Categories