I want to send and receive JSON over TCP.
QUESTION: I have to send and receive JSON in my TCP client-server. How can I achieve it?
I use TcpListener and TcpClient to connect and I have this code:
NetworkStream stream = client.GetStream();
var serializer = new JsonSerializer();
var sr = new StreamReader(stream, new UTF8Encoding(), false);
var jsonTextReader = new JsonTextReader(sr);
var data = serializer.Deserialize(jsonTextReader).ToString();
Console.WriteLine("Received: {0}", data);
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
JsonWriter writer = new JsonTextWriter(sw);
writer.WriteValue('1');
byte[] buffer = Encoding.ASCII.GetBytes(writer.ToString());
stream.Write(buffer, 0, buffer.Length);
Can I do it better? The client has to receive JSON(I use Newtonsoft.Json) and I don't know if it is even good code. Maybe you write me some good practices? Or maybe some tips.
EDIT.
Now I wrote something like this:
public static T DeserializeFromStream<T>(Stream stream)
{
using (var sr = new StreamReader(stream))
using (var jsonTextReader = new JsonTextReader(sr))
{
return new JsonSerializer().Deserialize<T>(jsonTextReader);
}
}
And it doesn't work because Java client send me array like: [{"name" : "logo", "session" : "i3fnj34njn780"}] So how can I fix this problem? I want call it this way: Method ar = DeserializeFromStream<Method>(client.GetStream()); Trim and Replace doesn't work for me here.
byte[] buffer = Encoding.ASCII.GetBytes(writer.ToString());
This is incorrect. Per RFC4627:
Encoding
JSON text SHALL be encoded in Unicode. The default encoding is
UTF-8.
You are not sending JSON.
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
JsonWriter writer = new JsonTextWriter(sw);
This is unnecessarily writing a SB. Write directly into the network stream:
using (NetworkStream stream = client.GetStream()) {
using (TextWriter tw = new StreamWriter(stream, Encoding.UTF8)) {
using (JsonWriter writer = new JsonTextWriter(tw)) {
...
}
}
}
Also, use using.
Plus, everything #CodeCaster says. This should be a proper Web API, not some rogue TCP server. Not only the obvious issue of having more than one request type (ie. routing), but you have to consider proxies (none will allow some arbitrary port), server authentication (you must tunnel through HTTPS and validate the server cert in the Android APP), make allowance for web caching, HTTP headers etc etc. And you need proper error states and error codes for your 'protocol', which HTTP provides out-of-the-box. And a good job would be to model a proper REST API, and likely a good data model on top of JSON, like JSON-API.
Related
I don't know where to look or what to check but ask my question in title. Xamarin forms app works when sending json data over virtual phone instance but doesn't send data over physical no matter which platform i use , it is the same with iOS and Android.
static async Task phoneInfo()
{
string url = "http://blabla.com/api/blabla";
string sContentType = "application/json";
JObject jsonObject = new JObject();
jsonObject.Add("DeviceModel", DeviceInfo.Model);
jsonObject.Add("DeviceManufacturer", DeviceInfo.Manufacturer);
jsonObject.Add("DeviceName", DeviceInfo.Name);
jsonObject.Add("DeviceVersion", DeviceInfo.VersionString);
jsonObject.Add("DevicePlatform", DeviceInfo.Platform);
jsonObject.Add("DeviceIdiom", DeviceInfo.Idiom);
jsonObject.Add("DeviceType", DeviceInfo.DeviceType.ToString());
jsonObject.Add("AreaOne", DateTime.UtcNow.ToString());
jsonObject.Add("Deleted", false);
HttpClient oHttpClient = new HttpClient();
var oTaskPostAsync = await oHttpClient.PostAsync(url, new StringContent(jsonObject.ToString(), Encoding.UTF8, sContentType));
}
usage is simple like code. just put await phoneInfo(); where i want to take info.
i have accesswifistate and internet permission over Android and NSAppTransportSecurity for non https connection with iOS.
Any ideas where am i doing wrong?
I have a WCF service with a webHttpBinding defined. The interface has a single method:
[OperationContract(Action = "*")]
[WebGet(UriTemplate = "/",RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
Stream GetServerInfo();
Which returns a stream with encoded JSON:
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
ServerData r = ServerData.Instance;
r.upTime = (DateTime.Now - r._startupTime).ToString(#"dd\.hh\:mm\:ss");
using (Process proc = Process.GetCurrentProcess())
{
r.usedMemory = ((double)proc.PrivateMemorySize64) / 1024 / 1024;
}
r.activeSessions = getServiceData().Count();
string jsCode = "displayData" + "("+
new JavaScriptSerializer().Serialize(r)
+")";
WebOperationContext.Current.OutgoingResponse.ContentType = "application/javascript";
Console.WriteLine(jsCode);
return new MemoryStream(Encoding.UTF8.GetBytes(jsCode));
When used on my devel PC it works fine. I get sth like:
displayData({"_startupTime":"\/Date(1435867525056)\/","serverVersion":"1.0.0.8","startUpTime":"2. 7. 2015 22:05:25","acceptedConnections":0,"upTime":"00.00:00:00","usedMemory":21.265625,"activeSessions":0})
However, after deploy to a remote server I get only following response and I want to get a JSON:
<GetServerInfoResponse xmlns="http://tempuri.org/"><GetServerInfoResult>ZGlzcGxheURhdGEoeyJfc3RhcnR1cFRpbWUiOiJcL0RhdGUoMTQzNTg2ODUwMjc5NClcLyIsInNlcnZlclZlcnNpb24iOiIxLjAuMC44Iiwic3RhcnRVcFRpbWUiOiI3LzIvMjAxNSAxOjIxOjQyIFBNIiwiYWNjZXB0ZWRDb25uZWN0aW9ucyI6MCwidXBUaW1lIjoiMDAuMDA6MDA6MDAiLCJ1c2VkTWVtb3J5IjoyNy40NzY1NjI1LCJhY3RpdmVTZXNzaW9ucyI6MH0p</GetServerInfoResult></GetServerInfoResponse>
Note I call for the request locally directly on the server. But on remote call over network the response is the same. If I put a log output of the string to console I can see the output string is correct.
The config files are identical (except for addresses).
--edit
With the try-fail method I found out the string inside the XML response is the JSON string encoded in Base64.
Can somebody please help me whats wrong?
After some research I did not find any solution. However, after restarting OS (Win Server 2012) and rebuilding it just works like a charm.
The requirement serialize a class and send it to the server.
Dev environment:
MonoDevelop 3.0.6
Runtime:
Mono 2.10.9 (tarball)
GTK 2.24.10
GTK# (2.12.0.0)
Operating System:
Mac OS X 10.7.4
The class contains a string with an escaped double quote in it.
class CustomClass
{
public string foo = "hi!\"";
}
The issue is that when I serialize it, encode it and create a URI object, the backslash used to escape double quote in the variable foo is converted into a forward slash, thus breaking the json.
Below are values of the different variables of the URI instance
Uri:
http://myserver/hello_world/0/{"foo":"hi!/""}
AbsoluteUri:
http://myserver/hello_world/0/%7B%22foo%22%3A%22hi%21/%22%22%7D
OriginalString:
http://myserver/hello_world/0/%7B%22foo%22%3A%22hi%21%5C%22%22%7D
The HttpWebRequest send the value "http://myserver/hello_world/0/{"foo":"hi!/""}" to the server, but for my requirement it should use the OriginalString to get a valid response from the server.
If i test the code on .NET the OriginalString is being sent to the server by the HttpWebRequest class, but there is additional code (hack) which doesn't work on mono mac
string paq = requestUri.PathAndQuery;
FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
ulong flags = (ulong)flagsFieldInfo.GetValue(requestUri);
flags &= ~((ulong)0x30); // Flags.PathNotCanonical|Flags.QueryNotCanonical
flagsFieldInfo.SetValue(requestUri, flags);
The code :
object messageObject = new CustomClass();
//try 1
//string jsonString = Uri.EscapeUriString(JsonConvert.SerializeObject(message2));
//try 2
//string jsonString = Uri.EscapeDataString(JsonConvert.SerializeObject(message2));
//try 3
string jsonString = System.Web.HttpUtility.UrlEncode(JsonConvert.SerializeObject(messageObject));
Uri uri = new Uri(string.Format("http://myserver/hello_world/0/{0}", jsonString));
//try 4:
//HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri.AbsoluteUri);
//try 5
//HttpWebRequest request =(HttpWebRequest)WebRequest.Create(string.Format("http://myserver/hello_world/0/{0}",jsonString));
//try 6
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri.OriginalString);
try
{
using (HttpWebResponse resp = request.GetResponse() as HttpWebResponse)
{
var reader = new StreamReader(resp.GetResponseStream());
string jsonResponse = reader.ReadToEnd();
}
}
catch (WebException wex)
{
Debug.WriteLine(wex.ToString());
}
As you can see I have tried using Uri.EscapeUriString, Uri.EscapeDataString and System.Web.HttpUtility.UrlEncode and using the AbsouluteUri and OriginalString to create the WebRequest instance and also creating it using a string.
I have also tried using the following code in the config.
<uri>
<schemeSettings>
<clear/>
<add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes"/>
</schemeSettings>
</uri>
But none of the methods work and the encoding is lost when the request in created.
Any ideas to get this to work will be appreciated.
Update:
I have tried a lot of things including testing with some 3rd party open source code, but still faced the same issue when sending in an encoded url.
I modified the code to use WebClient instead of WebRequest but still no luck.
So the solution that worked for me on both MonoMac an MonoTouch(simulator, i haven't tested it on the device) was to create the request using TcpClient.
using (TcpClient tc = new TcpClient()) {
tc.Connect ("myserver", 80);
using (NetworkStream ns = tc.GetStream()) {
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(ns)) {
using (System.IO.StreamReader sr = new System.IO.StreamReader(ns)) {
sw.Write("GET /hello_world/0/%7B%22foo%22:%22hi!%5C%22%22%7D HTTP/1.1 Host:myserver \r\n\r\n");
sw.Flush ();
string line;
while ((line=sr.ReadLine())!=null)
Console.Write (line);
}
}
}
}
I have tried a lot of things including testing with some 3rd party open source code, but still faced the same issue when sending in an encoded url.
I modified the code to use WebClient instead of WebRequest but still no luck.
So the solution that worked for me on both MonoMac an MonoTouch(simulator, i haven't tested it on the device) was to create the request using TcpClient.
using (TcpClient tc = new TcpClient()) {
tc.Connect ("myserver", 80);
using (NetworkStream ns = tc.GetStream()) {
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(ns)) {
using (System.IO.StreamReader sr = new System.IO.StreamReader(ns)) {
sw.Write("GET /hello_world/0/%7B%22foo%22:%22hi!%5C%22%22%7D HTTP/1.1 Host:myserver \r\n\r\n");
sw.Flush ();
string line;
while ((line=sr.ReadLine())!=null)
Console.Write (line);
}
}
}
}
am getting rather frustrated at the lack of information out there on how to post data to a WCF REST method in JSON form. I have tried almost everything out there. Google is just returning purple links at this point...
What I am looking for is a way to send a JSON object to my method so it can shelve all of the data for me. What I am currently trying to get working is below:
The Method header:
[WebInvoke(Method = "POST", UriTemplate = "role/new", ResponseFormat = WebMessageFormat.Json)]
void AddNewRole(Stream streamdata)
The logic from the client app:
string json = JsonConvert.SerializeObject(role);
byte[] buffer = Encoding.UTF8.GetBytes(json);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://IP/InfoService/role/new");
request.Method = "POST";
request.Credentials = new NetworkCredential("", "");
request.ContentType = "application/x-www-form-urlencoded";
using (StreamWriter sw = new StreamWriter(request.GetRequestStream()))
{
sw.Write(json);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
MessageBox.Show(sr.ReadToEnd());
}
Using this method I get an accepted response from the service, but it is a blank response and no new data gets added to the database. Granted, it is possible that there is a problem with my database update methods. Either way, can anyone help me figure out where I am going wrong?
EDIT: I now have fiddler able to test on the test client, and as expected there are problems with my DB queries. Ill post back soon.
EDIT2: Finally worked out the database problems, I have now successfully invoked through fiddler.
EDIT3: Its working now, thanks to nobody.
The code above has been edited, it works.
From what I understand, the HttpWebRequest class always cache the downloaded data. Now I don't mind this, but after a throughly reparsing the same URL through HttpWebRequest during the app duration, I've noticed that the data becomes corrupted (as in the downloaded JSON data becomes unparsable). After rebooting the Phone Emulator, it all goes smoonthy until it happens again.
Now I am just wondering if it possible to turn off the caching in HttpWebRequest.
Here is some of the code I am using to make a httpwebrequest call:
var request = (HttpWebRequest)WebRequest.Create(string.Format(uri));
request.BeginGetResponse(a =>
{
var response = request.EndGetResponse(a);
var responseStream = response.GetResponseStream();
using (var sr = new StreamReader(responseStream))
{
string json = sr.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
//Newtonsoft.Json.Linq;
JObject artistObject = JObject.Parse(json);
//...etc
});
}
}, null);
}
A common technique to get around this caching is to add an parameter to the query string that is incremented on successive calls. This thread discusses the silverlight behaviour in more detail, and covers some server handling you can look at too if you have that control.
WebClient Caching Problem
With that said, have you been able to produce a simple repro of the corruption you're experiencing? It might be worth getting that looked into.