I have an object that I am sending to my server from my client on Unity. The server is able to reassemble the data into it's natural type and read from the properties.
Client MessagaData
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[Serializable]
public class MessagaData
{
public int type { get; set; }
public string dataString { get; set; }
public string User { get; set; }
public string Password { get; set; }
}
Server MessagaData
using System;
namespace Server
{
[Serializable]
public class MessagaData
{
public int type { get; set; }
public string dataString { get; set; }
public string User { get; set; }
public string Password { get; set; }
}
}
When the user presses the login button it will first create a MessagaData object and assign it some properties.
public void sendLoginDetails()
{
if (string.IsNullOrEmpty (username.text))
return;
if (string.IsNullOrEmpty (password.text))
return;
MessagaData md = new MessagaData ();
md.type = 1;
md.User = username.text;
md.Password = password.text;
Client.Send (md);
}
Then the Static function is called: where md is passed.
static public void Send(MessagaData msgData)
{
if (singleton.client == null)
return;
string output = JsonConvert.SerializeObject(msgData);
byte[] data = new byte[1024];
NetworkStream ns = singleton.client.GetStream ();
data = Encoding.ASCII.GetBytes (output);
ns.Write (data, 0, data.Length);
ns.Flush ();
ReadThis ();
}
After sending the code the object, this function, ReadMe is invoked, where the error is obtained;
{"type":1,"dataString":null,"User":null,"Password":null}
Tracking the debugger on the code
throw CreateJsonReaderException("After parsing a value an unexpected character was encountered: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
it appears that what I receive from the server is something I Json canto read.
The Server:
private void myMethod(object obj)
{
int type = (int)obj;
MessagaData msgData = new MessagaData();
msgData.type = type;
string output = JsonConvert.SerializeObject(msgData);
byte[] data = new byte[1024];
NetworkStream ns = client.GetStream();
data = System.Text.Encoding.ASCII.GetBytes(output);
ns.Write(data, 0, data.Length);
ns.Flush();
}
Why does my client crash when a MessagaData is trying to be built?
EDIT -- 01/03/2016
1) What does your ReadMe() method look like?
public static void ReadThis()
{
if (singleton.client == null)
return;
NetworkStream ns = singleton.client.GetStream ();
byte[] data = new byte[1024];
ns.Read (data, 0, data.Length);
string output = System.Text.Encoding.ASCII.GetString(data);
Debug.LogWarning (output);
MessagaData md = JsonConvert.DeserializeObject<MessagaData>(output); //i am the error
switch(md.type)
{
case 1:
Debug.LogWarning ("Success");
break;
case 2:
Debug.LogWarning ("Failed");
//Thread.Sleep (500);
break;
}
}
Since it's the server where the exception is thrown, can you share that code?
The exception is thrown in the client side of the code, in the ReadMe() method
2) Can you give the full ToString() output of the exception including the message and traceback? It would be helpful to know the current character, line number, and line position, and stack trace inside Json.NET.
This is on the client;
try {
MessagaData md = JsonConvert.DeserializeObject<MessagaData> (output);
switch(md.type)
{
case 1:
Debug.LogWarning ("Success");
break;
case 2:
Debug.LogWarning ("Failed");
//Thread.Sleep (500);
break;
}
} catch (System.Exception ex) {
Debug.Log (ex.ToString ());
}
Debug shows:
> Newtonsoft.Json.JsonReaderException: After parsing a value an unexpected character was encountered:
UnityEngine.Debug:Log(Object)
Client:ReadThis() (at Assets/Scripts/Client.cs:73)
Client:Send(MessagaData) (at Assets/Scripts/Client.cs:45)
SendLoginInformation:sendLoginDetails() (at Assets/Scripts/SendLoginInformation.cs:26)
UnityEngine.EventSystems.EventSystem:Update()
3) Is the server using unity3d also, or using Microsoft .Net?
The server is C# Microsoft .NET If you like I can zip it and upload it since it is a very small program
To DBC.
Related
I have having troubles sending objects back and forth (duplex communication) using named pipes. The code:
Dto:
[ProtoContract]
public class ServerResponse
{
[ProtoMember(1)]
public string FirstName { get; set; }
[ProtoMember(2)]
public string LastName { get; set; }
}
[ProtoContract]
public class ServerRequest
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public int Age { get; set; }
}
Server:
static void Main(string[] args)
{
Console.WriteLine("Starting Server");
StartServer();
Console.WriteLine("Press any key to stop server..");
Console.ReadKey();
}
static void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("MyPipes");
server.WaitForConnection();
Console.WriteLine("Connected");
//StreamReader reader = new StreamReader(server);
//StreamWriter writer = new StreamWriter(server);
while (true)
{
//string line = reader.ReadLine();
//Console.WriteLine(line);
//writer.WriteLine(line.ToUpper());
//writer.Flush();
var request = Serializer.Deserialize<ServerRequest>(server);
Console.WriteLine($"Name: {request.Name}, Age: {request.Age}");
var response = new ServerResponse() { FirstName = request.Name, LastName = "Always this name!" };
Serializer.Serialize(server, response);
server.Flush();
}
});
}
Client:
static void Main(string[] args)
{
Console.WriteLine("Client");
var client = new NamedPipeClientStream("MyPipes");
client.Connect();
//StreamReader reader = new StreamReader(client);
//StreamWriter writer = new StreamWriter(client);
while (true)
{
//string input = Console.ReadLine();
//if (String.IsNullOrEmpty(input)) break;
//writer.WriteLine(input);
//writer.Flush();
//Console.WriteLine(reader.ReadLine());
string firstName = Console.ReadLine();
var request = new ServerRequest() { Name = firstName, Age = firstName.Length };
Serializer.Serialize(client, request);
client.Flush();
var response = Serializer.Deserialize<ServerResponse>(client);
Console.WriteLine($"Name: {response.FirstName}, Age: {response.LastName}");
}
Console.WriteLine("Done");
Console.ReadKey();
}
It works just fine, when sending a single line of string, but the protobuf fails on me.
For some reason, Deserialize method seems to never stop reading data from stream, therefore I am never able to decode the incoding server request. But if the client is forcefully stopped, then the request gets received.
I also tried using SteamWriter and StreamReader to no avail.. Is it possible to transmit protobuf objects in duplex communication? I would appreciate if somebody would point out what I am doing wrong..
protobuf is not a self-terminating data format; you need to use some kind of framing. Fortunately, the library includes a few basic implementations for your convenience, so : if you use SerializeWithLengthPrefix and DeserializeWithLengthPrefix (making sure to use the same configuration on both sides): it should work.
Without framing: the nature of protobuf is "read until the end of the stream", i.e. exactly what you're seeing. The reason for this is that protobuf is desiged to be concatenatable (is that a word?) even for single messages.
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.
I use this packet class (.dll) for my multithreaded client-server application.
BUT I don't know how to send strings with my code - pls help:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net.NetworkInformation;
namespace ServerData
{
[Serializable]
public class Packet
{
public List<string> Gdata;
public int packetInt;
public bool packetBool;
public string senderID;
public PacketType packetType;
public Packet(PacketType type, string senderID)
{
Gdata = new List<string>();
this.senderID = senderID;
this.packetType = type;
}
public Packet(byte[] packetbytes)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(packetbytes);
Packet p = (Packet)bf.Deserialize(ms);
ms.Close();
this.Gdata = p.Gdata;
this.packetInt = p.packetInt;
this.packetType = p.packetType;
this.senderID = p.senderID;
this.packetBool = p.packetBool;
}
public byte[] ToBytes()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
byte[] bytes = ms.ToArray();
ms.Close();
return bytes;
}
public static string GetIP4Address()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress i in ips)
{
if (i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
return i.ToString();
}
}
return "127.0.0.1";
}
public static string GetIP6Address()
{
string macAddresses = string.Empty;
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus == OperationalStatus.Up)
{
macAddresses += nic.GetPhysicalAddress().ToString();
break;
}
}
return macAddresses;
}
}
public enum PacketType
{
Registration,
Command,
Download,
Inquiry,
Reponse,
Confirmation
}
}
the send function in the server/client:
public void SendRegistrationPacket() // for example this send an packet from the type registration but in this moment i can "know" whats in the packet by "asking" what type it is...
{
Packet p = new Packet(PacketType.Registration, id);
clientSocket.Send(p.ToBytes()); //clientSocket is the socket where the client is connected^^
}
the manager for the incoming data (not reading only handling the readed data):
void DataManager(Packet p)
{
switch (p.packetType)
{
case PacketType.Registration:
listBox1.Items.Add("Got Registration Packet");
listBox1.Items.Add("Sending Registration...");
break;
}
}
how can i send strings and "encoding" them with these packets?
This class provides a public List<string> Gdata;. You can add any number of strings to this object, and the serializer will take care of it. So e.g. you could write
Packet p = new Packet(PacketType.Command, 123);
p.Gdata.Add("SomeString which I want to execute");
clientSocket.Send(p.ToBytes());
On the receiving end, you will deserialize the Packet object and use the Gdata field as usual.
byte[] received = new byte[1024];
int num_received = someSocket.Receive(received);
Packet decoded = new Packet(received.Take(num_received).ToArray());
Console.WriteLine("The first string in the list: " + decoded.Gdata[0]);
So I am just trying to create a basic Stack Overflow Client using WebClient. When I run the program as is, I get an empty string result, even if I sleep and wait. However when I open up Fiddler2 the program works... All I have to do is open Fiddler... Here is the relevant code.
public partial class MainWindow : Window
{
public ObservableCollection<question> questions { get; set; }
public MainWindow()
{
questions = new ObservableCollection<question>();
this.DataContext = this;
InitializeComponent();
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
MessageBox.Show(e.Result); //Right here is the difference. When
<BREAK POINT HERE OR IT BREAKS>
string data = data = e.Result.Substring(e.Result.IndexOf("class=\"question-summary narrow\"") + 31);
string content = data.Substring(0, data.IndexOf("class=\"question-summary narrow\""));
string v, a, t, b, tgs, link;
questions.Add(new question
{
//votes = v,
//answers = a,
//title = t.ToUpper(),
//body = b,
////tags = tgs
//href = link
});
}
private void button1_Click(object sender, RoutedEventArgs e)
{
WebClient wc = new WebClient();
wc.DownloadStringAsync(new Uri(#"http://api.stackoverflow.com/1.1/questions"));
wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
}
}
public class question
{
public string votes { get; set; }
public string answers { get; set; }
public string title { get; set; }
public string body { get; set; }
public string tags { get; set; }
public string href { get; set; }
}
Also worth noting is the fidler results
When I load http://api.stackoverflow.com/1.1/questions in the browser fiddler shows
GET http://api.stackoverflow.com/1.1/questions
200 OK (application/json)
and
GET http://api.stackoverflow.com/favicon.ico
503 Service Unavailable (text/html)
When I load it in my program though only this shows
GET http://api.stackoverflow.com/1.1/questions
200 OK (application/json)
Looks like the problem is with the API itself. Even though you are not telling it that you accept GZipped content, it's GZipping it anyway, and apparently Fiddler deals with that and unzips it for you. In your app, you have to deal with this by unzipping the content. Here's a simple example of how to do that:
var wc = new WebClient();
var bytes = wc.DownloadData(new Uri(#"http://api.stackoverflow.com/1.1/questions"));
string responseText;
using (var outputStream = new MemoryStream())
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var gzip = new GZipStream(memoryStream, CompressionMode.Decompress))
{
byte[] buffer = new byte[1024];
int numBytes;
while ((numBytes = gzip.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, numBytes);
}
}
responseText = Encoding.UTF8.GetString(outputStream.ToArray());
}
}
Console.WriteLine(responseText);
Whether or not it will always be GZipped, who knows - you can check the Content-Encoding HTTP header to see if it's gzip, and if so, then run this code, and if not, then you can convert the bytes directly into text.
I've got a server and client set up using TcpListener and TcpClient.
I want to send an object to my server application for processing.
I've discovered the using System.Runtime.Serialization and the following documentation, but I didn't want to faff around to find that I'm doing it in long winded way.
The question: What is the best way to process and send an object over the TCP stream?
Sending and receiving.
Here's an example of my object:
// Create a new house to send
house newHouse = new house();
// Set variables
newHouse.street = "Mill Lane";
newHouse.postcode = "LO1 BT5";
newHouse.house_number = 11;
newHouse.house_id = 1;
newHouse.house_town = "London";
Assuming you have a class House (available on both sides of your connection) looking like this:
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
You can serialize the class into a MemoryStream. You can then use in your TcpClient connection like this:
// Create a new house to send house and set values.
var newHouse = new House
{
Street = "Mill Lane",
ZipCode = "LO1 BT5",
Number = 11,
Id = 1,
Town = "London"
};
var xmlSerializer = new XmlSerializer(typeof(House));
var networkStream = tcpClient.GetStream();
if (networkStream.CanWrite)
{
xmlSerializer.Serialize(networkStream, newHouse);
}
Of course you have to do a little more investigation to make the program running without exception. (e.g. Check memoryStream.Length not to be greater than an int, a.s.o.), but I hope I gave you the right suggestions to help you on your way ;-)
First create a empty ServerApplication and ClientApplication as Console Application to simplify the example.
Then, put the definition for the serializable object into a separate assembly and then add a reference to the shared assembly to each project (server and client). Is necesary share the same object, not just an identical class copy.
To Generate DLL >
Right clic in Solution 'ServerApplication' in the Solution Explorer > Add New Project... -> select Class Library
(e.g. name this project MySharedHouse)
Rename the default Class1 to House and complete it
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
Right clic in MySharedHouse and Build.
Now the dll is build and we need to add it in Server Project and Client Project.
Right clic in ServerApplication > Add Reference > Browse and find the dll, for this example
Projects\ServerApplication\MySharedHouse\bin\Debug\MySharedHouse.dll
Repeat the process in ClientApplication using the same dll (same path).
Now you can use instances of House class in ServerApplication and ClientApplication as a single object, simply adding the sentence "using MySharedHouse" at the top.
SERVER CODE
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ServerApplication
{
class Program
{
static void Main(string[] args)
{
MessageServer s = new MessageServer(515);
s.Start();
}
}
public class MessageServer
{
private int _port;
private TcpListener _tcpListener;
private bool _running;
private TcpClient connectedTcpClient;
private BinaryFormatter _bFormatter;
private Thread _connectionThread;
public MessageServer(int port)
{
this._port = port;
this._tcpListener = new TcpListener(IPAddress.Loopback, port);
this._bFormatter = new BinaryFormatter();
}
public void Start()
{
if (!_running)
{
this._tcpListener.Start();
Console.WriteLine("Waiting for a connection... ");
this._running = true;
this._connectionThread = new Thread
(new ThreadStart(ListenForClientConnections));
this._connectionThread.Start();
}
}
public void Stop()
{
if (this._running)
{
this._tcpListener.Stop();
this._running = false;
}
}
private void ListenForClientConnections()
{
while (this._running)
{
this.connectedTcpClient = this._tcpListener.AcceptTcpClient();
Console.WriteLine("Connected!");
House house = new House();
house.Street = "Evergreen Terrace";
house.ZipCode = "71474";
house.Number = 742;
house.Id = 34527;
house.Town = "Springfield";
_bFormatter.Serialize(this.connectedTcpClient.GetStream(), house);
Console.WriteLine("send House!");
}
}
}
}
CLIENT CODE
using System;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ClientApplication
{
class Program
{
static void Main(string[] args)
{
MessageClient client = new MessageClient(515);
client.StartListening();
}
}
public class MessageClient
{
private int _port;
private TcpClient _tcpClient;
private BinaryFormatter _bFormatter;
private Thread _listenThread;
private bool _running;
private House house;
public MessageClient(int port)
{
this._port = port;
this._tcpClient = new TcpClient("127.0.0.1", port);
this._bFormatter = new BinaryFormatter();
this._running = false;
}
public void StartListening()
{
lock (this)
{
if (!_running)
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
else
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
}
}
private void ListenForMessage()
{
Console.WriteLine("Reading...");
try
{
while (this._running)
{
this.house = (House)this._bFormatter.Deserialize(this._tcpClient.GetStream());
Console.WriteLine(this.house.Street);
Console.WriteLine(this.house.ZipCode);
Console.WriteLine(this.house.Number);
Console.WriteLine(this.house.Id);
Console.WriteLine(this.house.Town);
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
}
}
Wooala! the first house to be sent over TCP/IP
You can simply decorate your House class with the [Serializable] attribute. (You do not need to define all the other stuff as posted in the other answer)
You can then send this object on the wire by serializing it using the BinaryFormatter class.
Have you considered setting up a WCF service instead of using TcpListener and TcpClient ? Makes life a lot easier.
For instance you could define a service that returned a house
[ServiceContract]
public interface IService
{
[OperationContract]
House GetHouse(int houseId);
}
See this real world example.
Your answer implies the following object (it is common practice to name classes using PascalCase):
[Serializable]
class House:ISerializable
{
public string Street {get; set;}
public string PostalCode {get; set;}
public int HouseNumber {get; set;}
public int HouseID {get; set;}
public string City {get; set;}
public House() { }
protected House(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
Street = (string)info.GetValue("Street ", typeof(string));
PostalCode = (string)info.GetValue("PostalCode", typeof(string));
HouseNumber = (int)info.GetValue("HouseNumber", typeof(int));
HouseID = (int)info.GetValue("HouseID", typeof(int));
City = (string)info.GetValue("City", typeof(string));
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags=SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
info.AddValue("Street ", Street);
info.AddValue("PostalCode", PostalCode);
info.AddValue("HouseNumber", HouseNumber);
info.AddValue("HouseID", HouseID );
info.AddValue("City", City);
}
}
Now you can serialize your objects:
void Send(Stream stream)
{
BinaryFormatter binaryFmt = new BinaryFormatter();
House h = new House()
{
Street = "Mill Lane",
PostalCode = "LO1 BT5",
HouseNumber = 11,
HouseID = 1,
City = "London"
};
binaryFmt.Serialize(stream, h);
}
How would you deserialize the xml House stream back to a House object on the receiving end?
I'm refering to the solution given in Fischermaen's answer.
On my recieving end I can see a string representation in my Output window by using the following:
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
Thank you in advance.
EDIT *
Ok well this solution has worked for me. Might need some tidying up.
Here's a method to deserialize a string:
public static T DeserializeFromXml<T>(string xml)
{
T result;
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader tr = new StringReader(xml))
{
result = (T)ser.Deserialize(tr);
}
return result;
}
Then from my TPC/IP Recieving end I call the method like so:
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
House house = DeserializeFromXml<House>(encoder.GetString(message, 0, bytesRead));
//Send Message Back
byte[] buffer = encoder.GetBytes("Hello Client - " + DateTime.Now.ToLongTimeString());
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
tcpClient.Close();
}