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.
Related
Im using WebApi to Deserialize Object on client side, witch contains some lightweight images, the code reads:
private void Button_Click(object sender, object e)
{
LoadApi();
}
private async void LoadApi()
{
using (var client = new HttpClient())
{
var responseMessage = await client.GetAsync("http://" +
TxtIP.Text + "/api/prod");
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
List<ClsProd> lstData = new List<ClsProd>();
var jsonResponse = await
responseMessage.Content.ReadAsStringAsync();
if (jsonResponse != null)
{
lstData = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ClsProd>>(jsonResponse);
}
ListView1.ItemsSource = lstData;
}
}
}
my ClsProd looks witch get all data from Web Api is:
public class ClsProd : System.ComponentModel.INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int IAuto { get; set; }
public int IDevc { get; set; }
public string SName { get; set; }
public string SImax { get; set; }
public ImageSource ImgPg { get; set; }
public ClsProd(int auto, int devc, string name, string imax)
{
IAuto = auto;
IDevc = devc;
SName = name;
SImax = imax;
ClsImgBase64 CImg = new ClsImgBase64();
CImg.EvtResult += CImg_EvtResult;
CImg.Start(imax);
}
private void CImg_EvtResult(ImageSource e)
{
ImgPg = e;
NotifyPropertyChanged("ImgPg");
}
}
All data is properly fetch and displayed on list, including string SImax witch is image encoded as Base64 string. The only problem is image conversion from base64 string to image is not happening.
Here is my class it does not pass the 1st statment on Task.Run, please help me find what is wrong. Also same funcition works when called from async void.
public class ClsImgBase64
{
public event Action<ImageSource> EvtResult;
public ClsImgBase64()
{
}
public void Start(string s)
{
System.Threading.Tasks.Task.Run(async () =>
{
//read stream
byte[] bytes = Convert.FromBase64String(s);
var image = bytes.AsBuffer().AsStream().AsRandomAccessStream();
//decode image
//var decoder = await BitmapDecoder.CreateAsync(image);
image.Seek(0);
//create bitmap
var output = new WriteableBitmap(1, 1);
await output.SetSourceAsync(image);
if (EvtResult != null)
{
EvtResult(output);
}
});
}
}
As per async void there's probably an Exception thrown which was lost and not displayed bacause the executing code is not awaited. Let's fix it.
Web part
avoid async void in methods that's aren't event handlers, also handle all possible exceptions in async void method
HttpClient is intended to be instantiated once per app rather than per use
HttpResponseMessage is IDisposable
private async void Button_Click(object sender, object e)
{
try
{
await LoadDataAsync();
}
catch (Exception ex)
{
// show ex.Message here in UI or log it
}
}
private static readonly HttpClient _client = new HttpClient();
private async Task LoadDataAsync()
{
using var response = await _client.GetAsync($"http://{TxtIP.Text}/api/prod");
string json = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
List<ClsProd> data = JsonConvert.DeserializeObject<List<ClsProd>>(json);
ListView1.ItemsSource = data;
await DecodeAllImagesAsync(data);
}
// decoding all at once asynchronously, see implementation below
private Task DecodeAllImagesAsync(List<ClsProd> data)
{
return Task.WhenAll(data.Select(item => item.DecodeImageAsync()).ToArray());
}
Consider using System.Text.Json to deserealize instead of old Newtonsoft.Json. It would allow to deserealize response.Content as Stream, faster with less memory consumption e.g:
using var stream = await response.EnsureSuccessStatusCode().Content.ReadAsStreamAsync();
List<ClsProd> data = await JsonSerializer.DeserealizeAsync<List<ClsProd>>(stream);
Data part
Use using directives at the beggining of the code to attach namespaces that will help not to repeat namespaces in the code explicitly
using System.ComponentModel;
It makes possible to write INotifyPropertyChanged instead of System.ComponentModel.INotifyPropertyChanged. I'll remove inlined namespaces below.
don't start long-running job from a constructor, it's unpredictable behavior because costructor must be always successful. Start loading images later. Also constructor cannot await asynchronous tasks. Separate method can.
public class ClsProd : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ImageSource _imgPg;
public int IAuto { get; set; }
public int IDevc { get; set; }
public string SName { get; set; }
public string SImax { get; set; }
public ImageSource ImgPg
{
get => _imgPg;
set
{
_imgPg = value;
NotifyPropertyChanged();
}
}
public ClsProd(int auto, int devc, string name, string imax)
{
IAuto = auto;
IDevc = devc;
SName = name;
SImax = imax;
}
public async Task DecodeImageAsync()
{
ImgPg = await ClsImgBase64.DecodeAsync(SImax);
}
}
Decoder
As now it's awaitable and doesn't need a callback, decoding method doesn't interact with the instance data. So, it can be static.
public static class ClsImgBase64
{
public static async Task<ImageSource> DecodeAsync(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
using var stream = bytes.AsBuffer().AsStream().AsRandomAccessStream();
// stream.Seek(0); // not sure if it needed
var decoder = await BitmapDecoder.CreateAsync(stream);
var pixelData = await decoder.GetPixelDataAsync();
var pixelArray = pixelData.DetachPixelData();
var bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
await bitmap.PixelBuffer.AsStream().WriteAsync(pixelArray, 0, pixelArray.Length);
return bitmap;
}
}
Decoder's code based on this answer.
If it will be laggy, try to wrap 2 Decoder's lines with Task.Run. Only if it will be laggy.
using var stream = await Task.Run(() =>
{
byte[] bytes = Convert.FromBase64String(base64);
return bytes.AsBuffer().AsStream().AsRandomAccessStream();
});
Finally: give classes, methods and other things more clear names, that would make the code maintainable.
This question already has answers here:
My console app shutdown prematurely when using async / await?
(4 answers)
Program exits upon calling await
(3 answers)
Closed 2 years ago.
Before you all go on a rampage about how this is a duplicate question, I have spent two days working on this issue, watching youtube tutorials on asynchronous programming, surfing similar stackoverflow posts etc, and I cannot for the life of me figure out how to apply Asynchronous Parallel Downloading of files into my project.
First things first, some background:
I am creating a program that, when given a query input via the user, will make a call to the twitch API and download clips.
My program is two parts
1- A web scraper that generates a .json file with all details needed to download files and
2 - A downloader.
Part 1 works perfectly fine and generates the .json files no trouble.
My Downloader contains reference to a Data class that is a handler for common properties and methods like my ClientID, Authentication, OutputPath, JsonFile, QueryURL. It also contains methods to give values to these properties.
Here are the two methods of my FileDownloader.cs that are the problem:
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach(ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
This is only one of my many attempts of downloading files, one which I got the idea from this post:
stackoverflow_link
I have also tried methods like the following from a YouTube video by IAmTimCorey:
video_link
I have spent many an hour tackling this problem, and I honestly can't figure out why it won't work with any of my attempts. I would vastly appreciate your help.
Thanks,
Ben
Below is the entirety of my code, should anyone need it for any reason.
Code Structure:
The only external libraries I have downloaded is Newtonsoft.Json
ClipInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Downloader
{
public class ClipInfo
{
public string id { get; set; }
public string url { get; set; }
public string embed_url { get; set; }
public string broadcaster_id { get; set; }
public string broadcaster_name { get; set; }
public string creator_id { get; set; }
public string creator_name { get; set; }
public string video_id { get; set; }
public string game_id { get; set; }
public string language { get; set; }
public string title { get; set; }
public int view_count { get; set; }
public DateTime created_at { get; set; }
public string thumbnail_url { get; set; }
}
}
Pagination.cs
namespace Downloader
{
public class Pagination
{
public string cursor { get; set; }
}
}
Root.cs
using System.Collections.Generic;
namespace Downloader
{
public class Root
{
public List<ClipInfo> data { get; set; }
public Pagination pagination { get; set; }
}
}
Data.cs
using System;
using System.IO;
namespace Downloader
{
public class Data
{
private static string directory = Directory.GetCurrentDirectory();
private readonly static string defaultJsonFile = directory + #"\clips.json";
private readonly static string defaultOutputPath = directory + #"\Clips\";
private readonly static string clipsLink = "https://api.twitch.tv/helix/clips?";
public string OutputPath { get; set; }
public string JsonFile { get; set; }
public string ClientID { get; private set; }
public string Authentication { get; private set; }
public string QueryURL { get; private set; }
public Data()
{
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = defaultOutputPath;
JsonFile = defaultJsonFile;
}
public Data(string clientID, string authentication, string outputPath)
{
ClientID = clientID;
Authentication = authentication;
OutputPath = directory + #"\" + outputPath + #"\";
JsonFile = OutputPath + outputPath + ".json";
}
public void GetQuery()
{
Console.Write("Please enter your query: ");
QueryURL = clipsLink + Console.ReadLine();
}
public void GetClientID()
{
Console.WriteLine("Enter your client ID");
ClientID = Console.ReadLine();
}
public void GetAuthentication()
{
Console.WriteLine("Enter your Authentication");
Authentication = Console.ReadLine();
}
public void OutputFolderExists()
{
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
}
}
}
JsonGenerator.cs
using System;
using System.IO;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Linq;
namespace Downloader
{
public static class JsonGenerator
{
// This class has no constructor.
// You call the Generate methods, passing in all required data.
// The file will then be generated.
private static Data data;
public static async Task Generate(Data clientData)
{
data = clientData;
string responseContent = null;
// Loop that runs until the api request goes through
bool authError = true;
while (authError)
{
authError = false;
try
{
responseContent = await GetHttpResponse();
}
catch (HttpRequestException)
{
Console.WriteLine("Invalid authentication, please enter client-ID and authentication again!");
data.GetClientID();
data.GetAuthentication();
authError = true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
authError = true;
}
}
data.OutputFolderExists();
GenerateJson(responseContent);
}
// Returns the contents of the resopnse to the api call as a string
private static async Task<string> GetHttpResponse()
{
// Creating client
HttpClient client = new HttpClient();
if (data.QueryURL == null)
{
data.GetQuery();
}
// Setting up request
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, data.QueryURL);
// Adding Headers to request
requestMessage.Headers.Add("client-id", data.ClientID);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", data.Authentication);
// Receiving response to the request
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage);
// Gets the content of the response as a string
string responseContent = await responseMessage.Content.ReadAsStringAsync();
return responseContent;
}
// Generates or adds to the .json file that contains data on each clip
private static void GenerateJson(string responseContent)
{
// Parses the data from the response to the api request
Root responseResult = JsonConvert.DeserializeObject<Root>(responseContent);
// If the file doesn't exist, we need to create it and add a '[' at the start
if (!File.Exists(data.JsonFile))
{
FileStream file = File.Create(data.JsonFile);
file.Close();
// The array of json objects needs to be wrapped inside []
File.AppendAllText(data.JsonFile, "[\n");
}
else
{
// For a pre-existing .json file, The last object won't have a comma at the
// end of it so we need to add it now, before we add more objects
string[] jsonLines = File.ReadAllLines(data.JsonFile);
File.WriteAllLines(data.JsonFile, jsonLines.Take(jsonLines.Length - 1).ToArray());
File.AppendAllText(data.JsonFile, ",");
}
// If the file already exists, but there was no [ at the start for whatever reason,
// we need to add it
if (File.ReadAllText(data.JsonFile).Length == 0 || File.ReadAllText(data.JsonFile)[0] != '[')
{
File.WriteAllText(data.JsonFile, "[\n" + File.ReadAllText(data.JsonFile));
}
string json;
// Loops through each ClipInfo object that the api returned
for (int i = 0; i < responseResult.data.Count; i++)
{
// Serializes the ClipInfo object into a json style string
json = JsonConvert.SerializeObject(responseResult.data[i]);
// Adds the serialized contents of ClipInfo to the .json file
File.AppendAllText(data.JsonFile, json);
if (i != responseResult.data.Count - 1)
{
// All objects except the last require a comma at the end of the
// object in order to correctly format the array of json objects
File.AppendAllText(data.JsonFile, ",");
}
// Adds new line after object entry
File.AppendAllText(data.JsonFile, "\n");
}
// Adds the ] at the end of the file to close off the json objects array
File.AppendAllText(data.JsonFile, "]");
}
}
}
FileDownloader.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Downloader
{
public class FileDownloader
{
private static Data data;
private static List<Task> tasks;
public async static void DownloadAllFiles(Data clientData)
{
data = clientData;
data.OutputFolderExists();
// Deserialize .json file and get ClipInfo list
List<ClipInfo> clips = JsonConvert.DeserializeObject<List<ClipInfo>>(File.ReadAllText(data.JsonFile));
tasks = new List<Task>();
foreach (ClipInfo clip in clips)
{
tasks.Add(DownloadFilesAsync(clip));
}
await Task.WhenAll(tasks);
}
private static void GetData()
{
if (data.ClientID == null)
{
data.GetClientID();
}
if (data.Authentication == null)
{
data.GetAuthentication();
}
if (data.QueryURL == null)
{
data.GetQuery();
}
}
private static string GetClipURL(ClipInfo clip)
{
// Example thumbnail URL:
// https://clips-media-assets2.twitch.tv/AT-cm%7C902106752-preview-480x272.jpg
// You can get the URL of the location of clip.mp4
// by removing the -preview.... from the thumbnail url */
string url = clip.thumbnail_url;
url = url.Substring(0, url.IndexOf("-preview")) + ".mp4";
return url;
}
private async static Task DownloadFilesAsync(ClipInfo clip)
{
WebClient client = new WebClient();
string url = GetClipURL(clip);
string filepath = data.OutputPath + clip.id + ".mp4";
await client.DownloadFileTaskAsync(new Uri(url), filepath);
}
private static void FileDownloadComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
tasks.Remove((Task)sender);
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using Downloader;
namespace ClipDownloader
{
class Program
{
private static string clientID = "{your_client_id}";
private static string authentication = "{your_authentication}";
async static Task Main(string[] args)
{
Console.WriteLine("Enter your output path");
string outputPath = Console.ReadLine();
Data data = new Data(clientID, authentication, outputPath);
Console.WriteLine(data.OutputPath);
//await JsonGenerator.Generate(data);
FileDownloader.DownloadAllFiles(data);
}
}
}
The example query I usually type in is "game_id=510218"
async void is your problem
Change
public static async void DownloadAllFiles(Data clientData)
To
public static async Task DownloadAllFiles(Data clientData)
Then you can await it
await FileDownloader.DownloadAllFiles(data);
The longer story:
async void runs unobserved (fire and forget). You can't wait for them to finish. In essence as soon as your program starts the task, it finishes, and tears down the App Domain and all your sub tasks, leading you to believe nothing is working.
I'm trying to stay on topic here as best as I can, but when using JsonConvert.DeserializeObject{T}, isn't T suppose to be an encapsulating root object type? I have never used it the way you're using it, so I'm just curious if that might be your bug. I could be completely wrong, and spare me if i am, but JSON is key:value based. Deserializing directly to a List doesn't really make sense. Unless there is a special case in the deserializer? List would be a file that's purely an array of ClipInfo values being deserialized into the members of List{T}(private T[] _items, private int _size, etc.) It needs a parent root object.
// current JSON file format implication(which i dont think is valid JSON?(correct me please)
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
// correct(?) JSON file format
{ // { } is the outer encasing object
clips:
[
// clip 1
{ "id": "", "url": "" },
// clip N
{ "id": "", "url": "" },
]
}
class ClipInfoJSONFile
{
public List<ClipInfo> Info { get; set; }
}
var clipInfoList = JsonConverter.DeserializeObject<ClipInfoJSONFile>(...);
I'm using RestAPI to get image location then attach it on adapter and use Recyclerview to show the data. The image is stored on server not on phone. Recyclerview works well but the image always change, and when i try to scroll quickly to the end of the data, it will be force close and pop up like this
"Java.Lang.OutOfMemoryError: Failed to allocate a 2380812 byte allocation with 976640 free bytes and 953KB until OOM".
Here is the video on youtube how my app works.
Here is my code, this is my adapter CrewMemberRecyclerViewAdapter.cs :
using System;
using System.Collections.Generic;
using Android.Graphics;
using Android.Support.V7.Widget;
using Android.Views;
using Ardiles_App.Resources;
using System.Net;
namespace my_APP
{
public class CrewMemberRecyclerViewAdapter : RecyclerView.Adapter
{
//Create an Event so that our our clients can act when a user clicks
//on each individual item.
public event EventHandler<int> ItemClick;
private List<CrewMember> _crewMembers;
private readonly ImageManager _imageManager;
public CrewMemberRecyclerViewAdapter(List<CrewMember> crewMembers, Android.Content.Res.Resources resources)
{
_crewMembers = crewMembers;
_imageManager = new ImageManager(resources);
}
//Must override, just like regular Adapters
public override int ItemCount
{
get
{
return _crewMembers.Count;
}
}
//Must override, this inflates our Layout and instantiates and assigns
//it to the ViewHolder.
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
//Inflate our CrewMemberItem Layout
View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.DetailItem, parent, false);
//Create our ViewHolder to cache the layout view references and register
//the OnClick event.
var viewHolder = new CrewMemberItemViewHolder(itemView, OnClick);
return viewHolder;
}
//Must override, this is the important one. This method is used to
//bind our current data to your view holder. Think of this as the equivalent
//of GetView for regular Adapters.
public override async void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
var viewHolder = holder as CrewMemberItemViewHolder;
var currentCrewMember = _crewMembers[position];
//Bind our data from our data source to our View References
viewHolder.CrewMemberName.Text = currentCrewMember.nama;
viewHolder.RankAndPosting.Text = String.Format("{0}\n{1}", "Rp." + currentCrewMember.harga, currentCrewMember.bahan);
//INI FOTO SEPATU
string linkGambar = "http://my.server.com/example/images/" + currentCrewMember.fotou;
Bitmap _bimage;
// _bimage = GetImageBitmapFromUrl(linkGambar);
var webClient = new WebClient();
var imageBytes = await webClient.DownloadDataTaskAsync(new Uri(linkGambar));
_bimage = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
BitmapFactory.Options option = new BitmapFactory.Options();
option.InBitmap = _bimage;
Bitmap _bfinal;
_bfinal = getRoundedShape(_bimage, 150, 150);
//var photoBitmap = await _imageManager.GetScaledDownBitmapFromResourceAsync(currentCrewMember.PhotoResourceId, 120, 120);
viewHolder.CrewMemberPhoto.SetImageBitmap(_bfinal);
}
//This will fire any event handlers that are registered with our ItemClick
//event.
private void OnClick(int position)
{
if (ItemClick != null)
{
ItemClick(this, position);
}
}
//Since this example uses a lot of Bitmaps, we want to do some house cleaning
//and make them available for garbage collecting as soon as possible.
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (_imageManager != null)
{
_imageManager.Dispose();
}
}
public Bitmap getRoundedShape(Bitmap scaleBitmapImage, int width, int height)
{
int targetWidth = width;
int targetHeight = height;
Bitmap targetBitmap = Bitmap.CreateBitmap(targetWidth,
targetHeight, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(targetBitmap);
/*
Android.Graphics.Path path = new Android.Graphics.Path();
path.AddCircle(((float)targetWidth - 1) / 2,
((float)targetHeight - 1) / 2,
(Math.Min(((float)targetWidth),
((float)targetHeight)) / 2),
Android.Graphics.Path.Direction.Ccw);*/
// canvas.ClipPath(path);
Bitmap sourceBitmap = scaleBitmapImage;
canvas.DrawBitmap(sourceBitmap,
new Rect(0, 0, sourceBitmap.Width,
sourceBitmap.Height),
new Rect(0, 0, targetWidth, targetHeight), null);
return targetBitmap;
}
}
}
This is CrewManifest.cs
using System.Collections.Generic;
using Android.Content;
using System.Net;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
namespace my_APP
{
public class CrewManifest
{
public static async Task<List<CrewMember>> GetAllCrewAsync(string kategori)
{
string url = "http://my.server.com/example/API/" + kategori;
List<CrewMember> crewList = JsonConvert.DeserializeObject<List<CrewMember>>(await FetchUserAsync(url));
return crewList;
}
private static async Task<string> FetchUserAsync(string url)
{
// Create an HTTP web request using the URL:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "GET";
// Send the request to the server and wait for the response:
using (WebResponse response = await request.GetResponseAsync())
{
// Get a stream representation of the HTTP web response:
using (var sr = new StreamReader(response.GetResponseStream()))
{
string strContent = sr.ReadToEnd();
return strContent;
}
}
}
}
}
This is my class
public class CrewMember
{
public string no { get; set; }
public string nama { get; set; }
public string uk { get; set; }
public string fotou { get; set; }
public string bahan { get; set; }
public string poin { get; set; }
public string harga { get; set; }
public string warna1 { get; set; }
public string warna2 { get; set; }
public string warna3 { get; set; }
public string warna4 { get; set; }
public string warna5 { get; set; }
}
And this is my JSON data:
[{"no":"167","nama":"Elok","uk":"36- 40","fotou":"167-1.jpg","bahan":"","poin":"50","harga":"61800.00","warna1":"Black","warna2":"Burgundy","warna3":"Brown","warna4":"Violet","warna5":null},{"no":"168","nama":"Glow","uk":"37- 40","fotou":"168-1.jpg","bahan":"","poin":"60","harga":"60800.00","warna1":"","warna2":"","warna3":"Brown","warna4":"Violet","warna5":null},{"no":"169","nama":"Hera","uk":"37- 40","fotou":"169-1.jpg","bahan":"","poin":"60","harga":"62800.00","warna1":"","warna2":"","warna3":"Brown","warna4":"Violet","warna5":null},{"no":"170","nama":"Kilau","uk":"32- 35","fotou":"170-1.jpg","bahan":"","poin":"50","harga":"53800.00","warna1":"","warna2":"","warna3":"Brown","warna4":"Violet","warna5":null},{"no":"171","nama":"Sari","uk":"37- 40","fotou":"171-1.jpg","bahan":"","poin":"60","harga":"62800.00","warna1":"","warna2":"","warna3":"Brown","warna4":"Violet","warna5":null},{"no":"172","nama":"Xena","uk":"36- 40","fotou":"172-1.jpg","bahan":"","poin":"50","harga":"54800.00","warna1":"Red","warna2":"","warna3":"Brown","warna4":"Violet","warna5":null}]
I'm using Visual Studio 2015 and Xamarin to build this app.
Thanks in advance
Try to use some image cache library for fetching images ...
E.g https://components.xamarin.com/view/square.picasso
Usage:
Picasso.With(context)
.Load("http://i.imgur.com/DvpvklR.png")
.Into(imageView);
If u need resize image use .Resize(50, 50)
Picasso.With(context)
.Load(url)
.Resize(50, 50)
.Into(imageView);
I think you should read some at Androids Developer page, specially here:Displaying Bitmaps Efficiently
how can i upload a large file with ASP.NET MVC4 Web Api
and also get a progress?
i saw this post and i understand how to handle the uploaded file but how i can get the progress data?
How To Accept a File POST
please don't send me links to upload products.
i want to understand how handle this in the MVC4 Web Api way...
here is an example code of handling a file upload in MVC4 WebApi
public async Task<HttpResponseMessage> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
var path = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(path);
await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
});
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
now when
await Request.Content.ReadAsMultipartAsync(provider)
how can i get how bytes loaded?
There is a limitation to the size of files to be uploaded by default at two places. One at the request level, and second , if you hosting on IIS, then on web server level. I added couple of configs as mentioned in this blog, and i was able to upload a 36mb file without any issues. I have posted the snippet below.
Basically
1.
<system.web>
<httpRuntime maxRequestLength="2097152"/>
</system.web>
2.
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="2147483648" />
</requestFiltering>
</security><system.webServer>
Its easy to find the size of the file loaded into the server if you wish. In your code
while reading through the filedata in the stream, for each item in you file data, you can read the local file name as shown below.
string savedFile = fileData.LocalFileName;
// use the file info class to derive properties of the uploaded file
FileInfo file = new FileInfo(savedFile);
//this will give the size of the uploaded file
long size = file.length/1024
Hope this helps. I wonder why this was marked down?
I use this solution:
public class UploadController : ApiController
{
private static ConcurrentDictionary<string, State> _state = new ConcurrentDictionary<string, State>();
public State Get(string id)
{
State state;
if (_state.TryGetValue(id, out state))
{
return state;
}
return null;
}
public async Task<HttpResponseMessage> Post([FromUri] string id)
{
if (Request.Content.IsMimeMultipartContent())
{
var state = new State(Request.Content.Headers.ContentLength);
if (!_state.TryAdd(id, state))
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Conflict));
var path = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data");
var provider = new FileMultipartStreamProvider(path, state.Start, state.AddBytes);
await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
{
_state.TryRemove(id, out state);
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
});
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
}
public class State
{
public long? Total { get; set; }
public long Received { get; set; }
public string Name { get; set; }
public State(long? total = null)
{
Total = total;
}
public void Start(string name)
{
Received = 0;
Name = name;
}
public void AddBytes(long size)
{
Received = size;
}
}
public class FileMultipartStreamProvider : MultipartStreamProvider
{
private string _rootPath;
private Action<string> _startUpload;
private Action<long> _uploadProgress;
public FileMultipartStreamProvider(string root_path, Action<string> start_upload, Action<long> upload_progress)
: base()
{
_rootPath = root_path;
_startUpload = start_upload;
_uploadProgress = upload_progress;
}
public override System.IO.Stream GetStream(HttpContent parent, System.Net.Http.Headers.HttpContentHeaders headers)
{
var name = (headers.ContentDisposition.Name ?? "undefined").Replace("\"", "").Replace("\\", "_").Replace("/", "_").Replace("..", "_");
_startUpload(name);
return new WriteFileStreamProxy(Path.Combine(_rootPath, name), _uploadProgress);
}
}
public class WriteFileStreamProxy : FileStream
{
private Action<long> _writeBytes;
public WriteFileStreamProxy(string file_path, Action<long> write_bytes)
: base(file_path, FileMode.Create, FileAccess.Write)
{
_writeBytes = write_bytes;
}
public override void EndWrite(IAsyncResult asyncResult)
{
base.EndWrite(asyncResult);
#if DEBUG
System.Threading.Thread.Sleep(100);
#endif
if (_writeBytes != null)
_writeBytes(base.Position);
}
public override void Write(byte[] array, int offset, int count)
{
base.Write(array, offset, count);
#if DEBUG
System.Threading.Thread.Sleep(100);
#endif
if (_writeBytes != null)
_writeBytes(base.Position);
}
}
and small configure for non-buffered input stream:
config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomPolicy());
implemented this:
public class CustomPolicy : System.Web.Http.WebHost.WebHostBufferPolicySelector
{
public override bool UseBufferedInputStream(object hostContext)
{
return false;
}
}
I Ended Up using an HttpModule but even the HttpModule won't show the progress bar
I found out something very interesting it's seems that when i upload the file in a Secure Protocol(over https://) then the progress are working but in non secure protocl (http://) the progress is not working and the file is fully buffered i don't know way is like that i believe it's a bug somewhere between the IIS to Asp.net Framework when the Request are get Procced.
now because i success make it work over https with an HttpModule i believe it is possible to make it work also with Mvc Web Api but i currently don't have the time to check that.
for parsing Mutlipart form data i used Nancy HttpMultipart parser here:
https://github.com/NancyFx/Nancy/tree/master/src/Nancy
just grabbed the classes:
HttpMultipart.cs
HttpMultipartBoundary.cs
HttpMultipartBuffer.cs
HttpMultipartSubStream.cs
here is the HttpModule Source:
public class HttpUploadModule : IHttpModule
{
public static DateTime lastClean = DateTime.UtcNow;
public static TimeSpan cleanInterval = new TimeSpan(0,10,0);
public static readonly object cleanLocker = new object();
public static readonly Dictionary<Guid,UploadData> Uploads = new Dictionary<Guid,UploadData>();
public const int KB = 1024;
public const int MB = KB * 1024;
public static void CleanUnusedResources( HttpContext context)
{
if( lastClean.Add( cleanInterval ) < DateTime.UtcNow ) {
lock( cleanLocker )
{
if( lastClean.Add( cleanInterval ) < DateTime.UtcNow )
{
int maxAge = int.Parse(ConfigurationManager.AppSettings["HttpUploadModule.MaxAge"]);
Uploads.Where(u=> DateTime.UtcNow.AddSeconds(maxAge) > u.Value.createdDate ).ToList().ForEach(u=>{
Uploads.Remove(u.Key);
});
Directory.GetFiles(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/'))).ToList().ForEach(f=>{
if( DateTime.UtcNow.AddSeconds(maxAge) > File.GetCreationTimeUtc(f)) File.Delete(f);
});
lastClean = DateTime.UtcNow;
}
}
}
}
public void Dispose()
{
}
public void Init(HttpApplication app)
{
app.BeginRequest += app_BeginRequest;
}
void app_BeginRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
Guid uploadId = Guid.Empty;
if (context.Request.HttpMethod == "POST" && context.Request.ContentType.ToLower().StartsWith("multipart/form-data"))
{
IServiceProvider provider = (IServiceProvider)context;
HttpWorkerRequest wr = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
FileStream fs = null;
MemoryStream ms = null;
CleanUnusedResources(context);
string contentType = wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
NameValueCollection queryString = HttpUtility.ParseQueryString( wr.GetQueryString() );
UploadData upload = new UploadData { id = uploadId ,status = 0, createdDate = DateTime.UtcNow };
if(
!contentType.Contains("boundary=") ||
/*AT LAST 1KB */ context.Request.ContentLength < KB ||
/*MAX 5MB */ context.Request.ContentLength > MB*5 ||
/*IS UPLOADID */ !Guid.TryParse(queryString["upload_id"], out uploadId) || Uploads.ContainsKey( uploadId )) {
upload.id = uploadId;
upload.status = 2;
Uploads.Add(upload.id, upload);
context.Response.StatusCode = 400;
context.Response.StatusDescription = "Bad Request";
context.Response.End();
}
string boundary = Nancy.HttpMultipart.ExtractBoundary( contentType );
upload.id = uploadId;
upload.status = 0;
Uploads.Add(upload.id, upload);
try {
if (wr.HasEntityBody())
{
upload.bytesRemaining =
upload.bytesTotal = wr.GetTotalEntityBodyLength();
upload.bytesLoaded =
upload.BytesReceived = wr.GetPreloadedEntityBodyLength();
if (!wr.IsEntireEntityBodyIsPreloaded())
{
byte[] buffer = new byte[KB * 8];
int readSize = buffer.Length;
ms = new MemoryStream();
//fs = new FileStream(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/')+'/' + uploadId.ToString()), FileMode.CreateNew);
while (upload.bytesRemaining > 0)
{
upload.BytesReceived = wr.ReadEntityBody(buffer, 0, readSize);
if(upload.bytesRemaining == upload.bytesTotal) {
}
ms.Write(buffer, 0, upload.BytesReceived);
upload.bytesLoaded += upload.BytesReceived;
upload.bytesRemaining -= upload.BytesReceived;
if (readSize > upload.bytesRemaining)
{
readSize = upload.bytesRemaining;
}
}
//fs.Flush();
//fs.Close();
ms.Position = 0;
//the file is in our hands
Nancy.HttpMultipart multipart = new Nancy.HttpMultipart(ms, boundary);
foreach( Nancy.HttpMultipartBoundary b in multipart.GetBoundaries()) {
if(b.Name == "data") {
upload.filename = uploadId.ToString()+Path.GetExtension( b.Filename ).ToLower();
fs = new FileStream(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/')+'/' + upload.filename ), FileMode.CreateNew);
b.Value.CopyTo(fs);
fs.Flush();
fs.Close();
upload.status = 1;
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
context.Response.Write( context.Request.ApplicationPath.TrimEnd('/') + "/images/temp/" + upload.filename );
}
}
}
}
}
catch(Exception ex) {
upload.ex = ex;
}
if(upload.status != 1)
{
upload.status = 2;
context.Response.StatusCode = 400;
context.Response.StatusDescription = "Bad Request";
}
context.Response.End();
}
}
}
public class UploadData {
public Guid id { get;set; }
public string filename {get;set;}
public int bytesLoaded { get; set; }
public int bytesTotal { get; set; }
public int BytesReceived {get; set;}
public int bytesRemaining { get;set; }
public int status { get;set; }
public Exception ex { get;set; }
public DateTime createdDate { get;set; }
}
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();
}