Convert Bitmap to Array and send the Array via Networkstream - c#

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);

Related

Read First 6 Bytes From NetworkStream?

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();

C# : MemoryStream problems in sending and receiving image bytes

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);

unable to receive full file in tcp client server

Here is my server code for reading a mp4 file and sending to the client
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 3400);
sock.Bind(ep);
sock.Listen(10);
sock = sock.Accept();
FileStream fs = new FileStream(#"E:\Entertainment\Songs\Video song\song.mp4",FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] data = new byte[fs.Length];
br.Read(data, 0, data.Length);
sock.Send(data);
fs.Close();
sock.Close();
Here is the client code
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3400);
sock.Connect(ep);
MemoryStream ms = new MemoryStream();
int size = 3190551; // I know the file size is about 30 mb
int rec;
while (size > 0)
{
byte[] buffer;
if (size < sock.ReceiveBufferSize)
{
buffer = new byte[size];
}
else
{
buffer = new byte[sock.ReceiveBufferSize];
}
rec = sock.Receive(buffer, 0, buffer.Length, 0);
size = size - rec;
ms.Write(buffer, 0, buffer.Length);
}
byte[] data = ms.ToArray();
FileStream fs = new FileStream("E:/song.mp4",FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs)
bw.Write(data);
fs.Close();
sock.Close();
**At the end i just get the data in between 3 to 4 mb.... im new to socket programming and I don't know where the problem is... whether its sending side or receiving !!!! it looks like I just receive a single chunk of data from the server side **
I think the problem is here
int size = 3190551; // I know the file size is about 30 mb
you are reading just 3190551 byte which is 3.04mb not 30mb.
try to send length of your file at the beginning of your message so client will know how many bytes it should get from server.

Parameter is not valid - error when sending a bitmap via TCP with C#

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.

Streaming image over ssl socket doesn't come across correctly

I'm trying to securely transfer files between 2 devices, so I'm using an SslStream attached to a TcpClient. Documents and text files come across just fine, but image files don't show up correctly. The following is the server code:
listener = new TcpListener(IPAddress.Any, 1337);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString());
try
{
sslStream.AuthenticateAsServer(certificate, true, SslProtocols.Default, true);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
var messageData = ReadMessage(sslStream);
var mode = messageData[0];
var tokenBytes = messageData.Splice(1, 16);
var fileNameBytes = messageData.Splice(17, 128);
var fileBytes = messageData.Splice(146);
var fileName = Encoding.ASCII.GetString(fileNameBytes).TrimEnd('\0');
using (var tempFile = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
{
tempFile.Write(fileBytes, 0, fileBytes.Length);
tempFile.Flush();
}
if (mode == 0)
tempFiles.Add(fileName);
Process.Start(fileName);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
}
And ReadMessage is defined as follows:
private static byte[] ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
MemoryStream stream = new MemoryStream();
int bytes = -1;
while (bytes != 0)
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
stream.Write(buffer, 0, bytes);
}
return stream.ToArray();
}
And then the client code is this:
TcpClient client = new TcpClient();
client.Connect(new IPEndPoint(IPAddress.Parse(ip), 1337));
SslStream sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidationCallback), new LocalCertificateSelectionCallback(CertificateSelectionCallback));
var certificate = Connection.GetClientCertificate(ip);
try
{
sslStream.AuthenticateAsClient(ip, new X509CertificateCollection() { certificate }, SslProtocols.Default, false);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
sslStream.Write(data);
}
catch (AuthenticationException e)
{
MessageBox.Show("The other side failed to authenticate.");
}
finally
{
sslStream.Close();
client.Close();
}
And the code that calls into it just does:
var fileBytes = File.ReadAllBytes(file);
var tokenBytes = Encoding.UTF8.GetBytes(token);
var fileNameBytes = Encoding.UTF8.GetBytes(Path.GetFileName(file));
var buffer = new byte[145 + fileBytes.Length];
buffer[0] = 1;
for (int i = 0; i < 16; i++)
{
buffer[i + 1] = tokenBytes[i];
}
for (int i = 0; i < fileNameBytes.Length; i++)
{
buffer[i + 17] = fileNameBytes[i];
}
for (int i = 0; i < fileBytes.Length; i++)
{
buffer[i + 145] = fileBytes[i];
}
SocketConnection.Send(ip, buffer);
Is there anything inherently wrong with what I'm doing, or do I need to do something different for images?
EDIT: I have changed it to reflect the current code, and also, after doing a dump of the raw bytes on both ends, it looks like for some reason the bytes are getting rearranged when they come over the wire. Is there any way to ensure that the bytes come across in the original order?
In ReadMessage you're writing bytes.Length bytes to the stream, regardless of the number of bytes that were actually read. Try:
private static byte[] ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
MemoryStream stream = new MemoryStream();
int bytes = -1;
while (bytes != 0)
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use "bytes" instead of "buffer.Length" here
stream.Write(buffer, 0, bytes);
}
return stream.ToArray();
}
Based on your follow-up, you're also taking the file data from the wrong point in the buffer, and so you're losing the first byte of the file.
Your code should be:
var fileBytes = messageData.Splice(145); // File data starts at 145, not 146
Is this possibly a conflict between endianness? If the bytes from the server are ABCDEF and the client is seeing the image bytes as BADCFE then that's the issue.
I haven't worked with image files, but when I read a short or an int instead of a String from the bytes coming in over the wire, I do something like this:
int intFromStream = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(byteArrayWithLength4, 0));

Categories

Resources