Sending Image through TCP - c#

im trying to send an image through tcp, It works about 50% of the time, the other 50% is just giving me a black image and if i send 2 in the space of like 3 seconds it crashes. does anyone know why? and how i can fix this
Client:
while ((i = stream.Read(datalength, 0, 4)) != 0)
{
byte[] data = new byte[BitConverter.ToInt32(datalength, 0)];
stream.Read(data, 0, data.Length);
this.Invoke((MethodInvoker)delegate
{
try
{
Image Screenshot = byteArrayToImage(data);
pictureBox1.Image = Screenshot;
}
catch { }
});
}
This is the function which converts the byte array to image
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
Server:
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
byte[] data = imageToByteArray(Image);
stream = client.GetStream();
int length = data.Length;
byte[] datalength = new byte[4];
datalength = BitConverter.GetBytes(length);
stream.Write(datalength, 0, 4);
stream.Write(data, 0, data.Length);
This is the function that converts the image to byte array
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}

You have to make sure that you read the entire "packet". It is not certain that you receive everything just because you request it. The Read() method will return how many bytes have been read, store that and loop until you've received the right amount of bytes.
Replace your current Read() with this:
int bytesReceived = 0;
while(bytesReceived < data.Length)
{
bytesReceived += stream.Read(data, bytesReceived, data.Length - bytesReceived);
}
This will read until your whole image has been received.
EDIT: Fixed a problem in code, thanks to Ivan's answer.

You're reading 1000 bytes when you should be reading four. You're reading and throwing away a large part of the image following the length word.
You're also apparently ignoring the result of read(), and assuming that it fills the buffer.

Related

How to convert RednerTargetBitmap image into byteArray for uploading on server in UWP

I have a grid that covers whole screen and i want to make that grid as image and then this image i want to send on server. I have used RenderTargetBitmap for this purpose and successfully make writeablebitmap to save using FileSave Picker. Image save is at normal size as expected 700kb but these bytes are too big for uploading on server. Expected bytes for 700kb image could be 200 thousand but in my situation bytes are more than 30 hundred thousand. There is definitely issue with bytes.
Here is code that i use to save grid as image using file save picker.
await bitmap.RenderAsync(testgrid);
var pixelBuffer = await bitmap.GetPixelsAsync();
byte[] pixels = pixelBuffer.ToArray();
var wb = new WriteableBitmap((int)bitmap.PixelWidth, (int)bitmap.PixelHeight);
using (stream2 = wb.PixelBuffer.AsStream())
{
await stream2.WriteAsync(pixels, 0, pixels.Length);
}
FileSavePicker picker = new FileSavePicker();
picker.FileTypeChoices.Add("JPG File", new List<string>() { ".jpg" });
StorageFile file = await picker.PickSaveFileAsync();
if (file != null)
{
await (wb as WriteableBitmap).SaveAsync(file);
}
The above code successfully save image to given location with normal size.
but when i use above pixel byte array to server. Http send task cancelled exception while send request and i have detail checked on it. It is due to huge amount of Bytes array.
Also if i send a very small grid of 50x50 then uploading done successfully because it has 30 thousand bytes only but image uploaded on the server is empty or corrupted.
Also i have used above converted writeablebitmap to make its bytes array using this method...
using (Stream stream = mywriteablebitmap.PixelBuffer.AsStream())
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
it also returns same number of bytes Array and same error occurred from server.
Please tell me the correct way of making byte array from RenderTargetBitmap and that can easily be uploaded on server.
but when i use above pixel byte array to server. Http send task cancelled exception while send request and i have detail checked on it. It is due to huge amount of Bytes array.
I totally agree with Clemens,you need to encode your image before uploading it. You can use the following codes to encode your image:
private async Task<String> ToBase64(byte[] image, uint height, uint width, double dpiX = 96, double dpiY = 96)
{
var encoded = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, encoded);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, height, width, dpiX, dpiY, image);
await encoder.FlushAsync();
encoded.Seek(0);
var bytes = new byte[encoded.Size];
await encoded.AsStream().ReadAsync(bytes, 0, bytes.Length);
var base64String=Convert.ToBase64String(bytes);
return base64String;
}
And call it in your codes:
var pixelBuffer = await bitmap.GetPixelsAsync();
byte[] pixels = pixelBuffer.ToArray();
await ToBase64(pixels, (uint)bitmap.PixelHeight, (uint)bitmap.PixelWidth);
For details about encoding Image in WinRT you can refer to Reading and Writing Base64 in the Windows Runtime.
After lot of searching i save the renderTargetBitmap in to isolatedstorage and then access the image file from storage and make its stream byte array using this method.
var stream = await imageStorageFile.OpenStreamForReadAsync();
imageBytes=ReadFully(stream);
public byte[] ReadFully(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
now this method works perfect.

Screenshot not transferring fully

my program right now is sending a screen shot from one computer to the other but the result on the receiving side the img looks like:
Bitmap bmpScreenShot = new Bitmap(screenWidth, screenHeight);
Graphics gfx = Graphics.FromImage((Image)bmpScreenShot);
gfx.CopyFromScreen(0, 0, 0, 0, new Size(screenWidth, screenHeight));
bmpScreenShot.Save("pic.jpg", ImageFormat.Jpeg);
MemoryStream ms = new MemoryStream();
bmpScreenShot.Save(ms, ImageFormat.Jpeg);
bmpbyte = ms.ToArray();
bmpScreenShot.Dispose();
ms.Close();
///////////////////////////
Send_Text("" + screenHeight);
textBox3.Text += ("\r\nSending Hight=" + screenHeight);
Send_Text("" + screenWidth);
textBox3.Text += ("\r\nSending Width=" + screenWidth);
System.Threading.Thread.Sleep(200);
Send_Text("" + bmpbyte.Length);
textBox3.Text += ("\r\nSending size of:" + bmpbyte.Length);
textBox3.Text += "\r\nTransmiting the Screenshot";
stm.Write(bmpbyte, 0, bmpbyte.Length);
textBox3.Text += "\r\nSent IMG :)";
the code on top is the client side of it
im sending the size of the pic (hight and width and array length) and it transfers properly
but the server as i said has trobles getting the full pic
textBox2.Text += "\r\n Getting h";
byte[] buffer = new byte[320];
s.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Array.Clear(buffer, 0, buffer.Length);
int h = int.Parse(str);
textBox2.Text += "="+h+"\r\n Getting w";
s.Receive(buffer);
str = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Array.Clear(buffer, 0, buffer.Length);
int w = int.Parse(str);
textBox2.Text += "="+w+"\r\n Getting p";
s.Receive(buffer);
str = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
Array.Clear(buffer, 0, buffer.Length);
int p = int.Parse(str);
textBox2.Text += "=" + p;
byte[] asd = new byte[p];
Bitmap cc = new Bitmap(h, w);
s.Receive(asd);
MemoryStream ms = new MemoryStream(asd);
Image bmp = Image.FromStream(ms);
bmp.Save("End.jpg", ImageFormat.Jpeg);
pictureBox1.Image = bmp;
plz note that alll of the undefined things are defind some where in the code
and the text box lines are just for the user interface
one more thing im using the localhost \local network and it all tcp
You're not telling us how you send the information (over network? serial? TCP?), but from your code I can see one thing:
You do receive the actual number of bytes to wait for, but you're not waiting for the actual number of bytes.
byte[] asd = new byte[p];
s.Receive(asd);
This does create a byte array large enough to keep all the bytes, but does s.Receive(asd) really receive all the bytes?
After you edited your question, let me clarify one thing: TCP communication can be fragmented. Just because you send 4000 bytes in one go does not guarantee the receiver to receive 4000 bytes in one go. He probably won't. That's why the Receive method returns the actual number of bytes received.
So what your receiver needs to do is this (pseudo code):
int totalBytesRead = 0;
do
{
int bytesRead = s.Receive(buffer);
totalBytesRead += bytesRead;
if (bytesRead == 0)
{
// Stream was closed - no more bytes
break;
}
else
{
// Write bytesRead bytes from buffer to memory stream
}
}
while (totalBytesRead < expectedNumberOfBytes);
if (totalBytesRead < expectedNumberOfBytes)
throw new Exception("Premature end of transmission");
Actually, thinking things through again and looking at your code I noticed that you're actually sending JPEG bytes to the receiver. It's highly improbable that an uninitialized buffer on the receiver's side should be valid JPEG.
So while all the above is still true, I now doubt that the receiver is actually the one doing things wrong. It seems now that saving the bytes to the memory stream doesn't work properly.
I see from your code you're saving the image both to a file and to a memory stream. Does the file contain a valid picture? If not you need to look for the cause of the screenshot not being created properly. If the picture is valid, you could try two other things:
Read the JPEG bytes from the file instead of creating a memory stream
Don't dispose of the memory stream before sending the bytes to the client, but after that
There are questions on SO that indicate that timing seems to be an issue when saving images to a memory stream and getting the bytes from the stream. Sometimes it won't work.

"Artifacts" while streaming images in c#

I wanted to create a project for streaming multiple images through net. I just wanted to start with small, functional code but already encountered a funky problem for me. Image is received, but it contains graphical bugs like its only a part of it.
Sadly cant show images cause of low rputation, here is link.
http://img543.imageshack.us/img543/1508/buggedy.jpg
Of course, million dollars for answer. ^^
TcpListener listener;
TcpClient client;
TcpClient datatoclient;
NetworkStream stream;
Host part:
listener = new TcpListener(5000);
listener.Start();
datatoclient = listener.AcceptTcpClient();
NetworkStream nowystream = datatoclient.GetStream();
MemoryStream ms = new MemoryStream();
byte[] image = File.ReadAllBytes("default.jpg");
switch (trackBar1.Value)
{
case 0:
image = File.ReadAllBytes("mirrion.jpg");
break;
case 1:
image = File.ReadAllBytes("tenis.jpg");
break;
case 2:
image = File.ReadAllBytes("marisasold.jpg");
break;
}
// get the image size in bytes
int numberOfBytes = image.Length;
// put the size into an array
byte[] numberOfBytesArray = BitConverter.GetBytes(numberOfBytes);
// send the image size
nowystream.Write(numberOfBytesArray, 0, numberOfBytesArray.Length);
// send the image
nowystream.Write(image, 0, numberOfBytes);
Client part:
client = new TcpClient("127.0.0.1", 5000);
stream = client.GetStream();
byte[] data = new byte[4];
// read the size
stream.Read(data, 0, data.Length);
int size = BitConverter.ToInt32(data, 0);
label1.Text = size.ToString();
// prepare buffer
data = new byte[size];
// load image
stream.Read(data, 0, data.Length);
// save image to file for test
File.WriteAllBytes("received.jpg", data);
MemoryStream MS = new MemoryStream(data);
pictureBox1.Image = Image.FromStream(MS);
stream.Read doesn't guarantee that it will read data.Length bytes. Instead it returns number of bytes read. So you should check its return value and continue reading till you get all the bytes.
See http://msdn.microsoft.com/en-us/library/system.io.stream.read(v=vs.90).aspx (Section Return Value)
Thr read method can be something like this
void Read(Stream stream, byte[] buffer,int offset,int len)
{
int read = 0;
while (read < len)
{
read += stream.Read(buffer, offset + read, len-read);
}
}

GZIP Java vs .NET

Using the following Java code to compress/decompress bytes[] to/from GZIP.
First text bytes to gzip bytes:
public static byte[] fromByteToGByte(byte[] bytes) {
ByteArrayOutputStream baos = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = new GZIPOutputStream(baos);
byte[] buffer = new byte[1024];
int len;
while((len = bais.read(buffer)) >= 0) {
gzos.write(buffer, 0, len);
}
gzos.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
return(baos.toByteArray());
}
Then the method that goes the other way compressed bytes to uncompressed bytes:
public static byte[] fromGByteToByte(byte[] gbytes) {
ByteArrayOutputStream baos = null;
ByteArrayInputStream bais = new ByteArrayInputStream(gbytes);
try {
baos = new ByteArrayOutputStream();
GZIPInputStream gzis = new GZIPInputStream(bais);
byte[] bytes = new byte[1024];
int len;
while((len = gzis.read(bytes)) > 0) {
baos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
return(baos.toByteArray());
}
Think there is any effect since I'm not writing out to a gzip file?
Also I noticed that in the standard C# function that BitConverter reads the first four bytes and then the MemoryStream Write function is called with a start point of 4 and a length of input buffer length - 4. So is that effect the validity of the header?
Jim
I tryed it out, and I cant reproduce your 'Invalid GZip Header' issue. Here is what I did:
Java side
I took your Java compression method together with this java snippet:
public static String ToHexString(byte[] bytes){
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < bytes.length; i++)
hexString.append((i == 0 ? "" : "-") +
Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
return hexString.toString();
}
So that this minimalistic java application, taking the bytes of a test string, compressing it, and converting it to a hex string of the compressed data...:
public static void main(String[] args){
System.out.println(ToHexString(fromByteToGByte("asdf".getBytes())));
}
... outputs the following (I added annotations):
1f-8b-08-00-00-00-00-00-00-00-4b-2c-4e-49-03-00-bd-f3-29-51-04-00-00-00
^------- GZip Header -------^ ^----------- Compressed data -----------^
C# side
I wrote two methods for compressing and uncompressing a byte array to another byte array (compression method is just for completeness, and my testings):
public static byte[] Compress(byte[] uncompressed)
{
using (MemoryStream ms = new MemoryStream())
using (GZipStream gzs = new GZipStream(ms, CompressionMode.Compress))
{
gzs.Write(uncompressed, 0, uncompressed.Length);
gzs.Close();
return ms.ToArray();
}
}
public static byte[] Decompress(byte[] compressed)
{
byte[] buffer = new byte[4096];
using (MemoryStream ms = new MemoryStream(compressed))
using (GZipStream gzs = new GZipStream(ms, CompressionMode.Decompress))
using (MemoryStream uncompressed = new MemoryStream())
{
for (int r = -1; r != 0; r = gzs.Read(buffer, 0, buffer.Length))
if (r > 0) uncompressed.Write(buffer, 0, r);
return uncompressed.ToArray();
}
}
Together with a small function that takes a hex string and turns it back to a byte array... (also just for testing purposes):
public static byte[] ToByteArray(string hexString)
{
hexString = hexString.Replace("-", "");
int NumberChars = hexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
return bytes;
}
... I did the following:
// Just hardcoded the output of the java program, convert it back to byte[]
byte[] fromjava = ToByteArray("1f-8b-08-00-00-00-00-00-00-00-" +
"4b-2c-4e-49-03-00-bd-f3-29-51-04-00-00-00");
// Decompress it with my function above
byte[] uncompr = Decompress(fromjava);
// Get the string out of the byte[] and print it
Console.WriteLine(System.Text.ASCIIEncoding.ASCII
.GetString(uncompr, 0, uncompr.Length));
Et voila, the output is:
asdf
Works perfect for me. Maybe you should check your decompression method in your c# application.
You said in your previous question you are storing those byte arrays in a database, right? Maybe you want to check whether the bytes come back from the database the way you put them in.
Posting this as an answer so the code looks decent.
Note a couple things:
First, the round trip to the database did not appear to have any effect. Java on both sides produced exactly what I put in. Java in C# out worked fine with the Ionic API, as did C# in and Java out. Which brings me to the second point.
Second, my original decompress was on the order of:
public static string Decompress(byte[] gzBuffer)
{
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length – 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
{
zip.Read(buffer, 0, buffer.Length);
}
return Encoding.UTF8.GetString(buffer);
}
}
Which depended on the internal byte count, yours reads the whole file regardless of internal value. Don't know what the Ionic algorithm is. Yours works the same as the Java methods I've used. That's the only difference I see. Thanks very much for doing all that work. I will remember that way of doing it.
Thanks,
Jim

Problem with C# Decompression

Have some data in a sybase image type column that I want to use in a C# app. The data has been compressed by Java using the java.util.zip package. I wanted to test that I could decompress the data in C#. So I wrote a test app that pulls it out of the database:
byte[] bytes = (byte[])reader.GetValue(0);
This gives me a compressed byte[] of 2479 length.
Then I pass this to a seemingly standard C# decompression method:
public static byte[] Decompress(byte[] gzBuffer)
{
MemoryStream ms = new MemoryStream();
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
byte[] buffer = new byte[msgLength];
ms.Position = 0;
GZipStream zip = new GZipStream(ms, CompressionMode.Decompress);
zip.Read(buffer, 0, buffer.Length);
return buffer;
}
The value for msgLength is 1503501432 which seems way out of range. The original document should be in the range of 5K -50k. Anyway when I use that value to create "buffer" not surprisingly I get an OutOfMemoryException.
What is happening?
Jim
The Java compress method is as follows:
public byte[] compress(byte[] bytes) throws Exception {
byte[] results = new byte[bytes.length];
Deflater deflator = new Deflater();
deflater.setInput(bytes);
deflater.finish();
int len = deflater.deflate(results);
byte[] out = new byte[len];
for(int i=0; i<len; i++) {
out[i] = results[i];
}
return(out);
}
As I cant see your java code, I can only guess you are compressing your data to a zip file stream. Therefore it will obviously fail if you are trying to decompress that stream with a gzip decompression in c#. Either you change your java code to a gzip compression (Example here at the bottom of the page), or you decompress the zip file stream in c# with an appropriate library (e.g. SharpZipLib).
Update
Ok now, I see you are using deflate for the compression in java. So, obviously you have to use the same algorithm in c#: System.IO.Compression.DeflateStream
public static byte[] Decompress(byte[] buffer)
{
using (MemoryStream ms = new MemoryStream(buffer))
using (Stream zipStream = new DeflateStream(ms,
CompressionMode.Decompress, true))
{
int initialBufferLength = buffer.Length * 2;
byte[] buffer = new byte[initialBufferLength];
bool finishedExactly = false;
int read = 0;
int chunk;
while (!finishedExactly &&
(chunk = zipStream.Read(buffer, read, buffer.Length - read)) > 0)
{
read += chunk;
if (read == buffer.Length)
{
int nextByte = zipStream.ReadByte();
// End of Stream?
if (nextByte == -1)
{
finishedExactly = true;
}
else
{
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
}
if (!finishedExactly)
{
byte[] final = new byte[read];
Array.Copy(buffer, final, read);
buffer = final;
}
}
return buffer;
}

Categories