So, I finally managed to make a conection but now im trying to pass images over the server.
The code looks like this:
private void Form1_Load(object sender, EventArgs e)
{
//StartConnectionVideo();
Client = new TcpClient();
Client.Connect("10.0.0.3", 456);
//Connect to 127.0.0.1 on port 3700
readingThread = new Thread(new ThreadStart(StartReading));
//Start a new thread to receive images
readingThread.Start();
}
private void StartReading()
{
while (true)
{
MessageBox.Show("D");
NetworkStream stream = Client.GetStream();
BinaryFormatter formatter = new BinaryFormatter();
Image img = Image.FromStream(stream);
MessageBox.Show("D");
//Deserialize the image from the NetworkStream
MessageBox.Show(img.Width.ToString());
pictureBox1.Image = img; //Show the image in the picturebox
}
}
}
the first message box work(directlry after the loop) but on the second its stuck.
it's just hangs on this line
Image img = Image.FromStream(stream);
the server side is this..
Image img=Image.FromFile(#"C:\Users\איתמר\Desktop\air\amumu_0.jpg");
VideoServer a = new VideoServer(img ,this);
a.StartListening();
//a.padre.pictureBox1.Image = img;
a.SendImage(img);
videoserver is a class i wrote... i'll write here the main important code pieces
public void StartListening()
{
// Create the TCP listener object using the IP of the server and the specified port
tlsClient = new TcpListener (IPAddress.Any, 456);
// Start the TCP listener and listen for connections
tlsClient.Start();
// The while loop will check for true in this before checking for connections
ServRunning = true;
thrListener = new Thread(KeepListening);
thrListener.Start();
}
public void SendImage(Image img)
{
for (int i = 0; i < ClientList.Count; i++)
{
TcpClient tempClient = (TcpClient)ClientList[i];
if (tempClient.Connected) //If the client is connected
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(tempClient.GetStream(), img);
//Serialize the image to the tempClient NetworkStream
}
else
{
ClientList.Remove(tempClient);
i--;
}
}
i think i've showed the main code... the rest isnt so important because im prettry sure the problem is in this code...
i appriciate any help of you guys im breaking my head over a week :D
You are serializing the image using BinaryFormatter but deserializing it using Image.FromStream. These are two incompatible formats. You must use the same format both times.
As far as I'm aware Image.FromStream is not documented to cope with infinite streams. In fact I'm quite sure it doesn't because by principle it cannot avoid reading two much (except if it were reading byte-wise which is a performance nightmare). Therefore, you can't use Image.FromStream directly.
I'd do this by first converting the image to a byte[] or a MemoryStream. Then I would first write the length of the image data and then the data. A length prefix is a common way of serializing things.
Or, use a higher-level abstraction such as protobuf.
Related
I have a function that add an Image file to stream like this:
//sourceImage is a url for an image
public Stream AddImageToStream(string sourceImage)
{
try
{
using (WebClient webClient = new WebClient())
{
byte[] data = webClient.DownloadData(sourceImage);
using (MemoryStream mem = new MemoryStream(data))
{
using (var yourImage = Image.FromStream(mem))
{
Image img = yourImage;
var ms = img.ToStream(ImageFormat.Png);
img.Dispose();
return ms;
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
I can't find Video class in C#.
How can implement this function for video files?
Basically you already know how to turn the downloaded content into a MemoryStream:
public Stream DownloadToStream(string url)
{
using (var webClient = new System.Net.WebClient())
{
byte[] data = webClient.DownloadData(url);
var ms = new System.IO.MemoryStream(data);
return ms;
}
}
But this will need the video or whatever to fit into memory, there is no progress handling and nothing asynchronous. Also you should Dispose() the returned stream when you finished your operations with it.
There is no Video class in the basic .NET framework as videos are a complex topic. If you want to process it, you should look into some of the frameworks/libraries for that and definitely have to learn about asynchronous processing and streams.
Update: If you can use current versions of the framework you might be interested in using HttpClient.GetStreamAsync. This will not consume all your memory. Still if you want to edit the video data you will require a framework specialized on video editing.
I have to develop a simple TCP File Transfer Client as part of an exercise/demonstration but I am having trouble with data delivery.
I am already provided with the TCP File server which will receive the incoming files and I have to code a Client that will connect to and send a file to the server. Thus far I have been successful as far as converting the data of the selected file into to a format ready to transferring and successfully opening a connection and then sending of the data, however even encounter a problem on server that is receiving - I am not allowed to change the code of the server and should thus change my client code in such a way that the data sent can be interpreted by the server. Here is the code I use (some is left out which is simple overhead, like input-box to get IP address etc.):
Link for TCP Server VS Solution (if you prefer): https://www.mediafire.com/?682owf9wtdzmxac
TCP File Transfer Client Sending method:
private void TransferFile(string _sFileName, string _sIPAdress)
{
//Convert data for transfer
Stream strmfilestream = File.OpenRead(_sFileName);
Byte[] bFileBuffer = new Byte[strmfilestream.Length];
//Open TCP/IP Connection
TcpClient tcpClientSocket = new TcpClient(_sIPAdress,8080);
NetworkStream nsNetworkStream = tcpClientSocket.GetStream();
nsNetworkStream.Write(bFileBuffer,0,bFileBuffer.GetLength(0));
nsNetworkStream.Close();
}
*Note: _sFileName is just the full file path + File name from a OpenFileDialog.
Here is the Load Method of the server to get things going:
if (!Directory.Exists(#"C:\TCPFileServer"))
Directory.CreateDirectory(#"C:\TCPFileServer");
//Get Ip address of server host machine
IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());
lblServerIP.Text = IPHost.AddressList[5].ToString();
lstSockets = new ArrayList();
Thread thdListener = new Thread(new ThreadStart(listenerThread));
thdListener.IsBackground = true; //This will enabe the thread to terminate when application is closed
thdListener.Start();
Here is the listener thread method:
public void listenerThread()
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, 8080);
tcpListener.Start();
while (true)
{
Socket handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
this.Invoke((Action)(() => lstConnections.Items.Add(handlerSocket.RemoteEndPoint.ToString() + " connected.")));
lock (this)
{
lstSockets.Add(handlerSocket);
}
ThreadStart thdsHandler = new ThreadStart(handlerThread);
Thread thdHandler = new Thread(thdsHandler);
thdHandler.Start();
}
}
}
And then lasty here is the handler thread method:
public void handlerThread()
{
try
{
int iBlockSize = 1024 * 3000; //3mb block size
Byte[] dataByte = new Byte[iBlockSize];
Byte[] rcvdData = new Byte[128000 * 1024];//128mb File Limit
Socket handlerSocket = (Socket)lstSockets[lstSockets.Count - 1];
NetworkStream networkStream = new NetworkStream(handlerSocket);
int i = 0;
int iRcvdBytes = 0;
while (true)
{
//Read from socket and store to buffer 'dataByte'
iRcvdBytes = networkStream.Read(dataByte, 0, iBlockSize);
dataByte.CopyTo(rcvdData, i);//Copy recieved bytes,from buffer, to another byte array
i += iRcvdBytes;
if (iRcvdBytes == 0) break;
}
//Get the File name length, BitConvertor occupies the first 4 bytes
int iFileNameLength = BitConverter.ToInt32(rcvdData, 0);
//Get the file name using length as the size and 4 as the offset
string sFileName = Encoding.ASCII.GetString(rcvdData, 4, iFileNameLength);
Stream fileStream = File.Open("C:\\TCPFileServer\\" + sFileName, FileMode.Create);
//Populate raw File on local machine
fileStream.Write(rcvdData, 4 + iFileNameLength, i - 4 - iFileNameLength);
fileStream.Close();
//Update BRS Net Files Server Log
this.Invoke((Action)(() => lstConnections.Items.Add(sFileName + ": Transfered.")));
//Close Connection
networkStream.Close();
handlerSocket = null; //Clear socket
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now I have debugged and as far I can determine, the first time I can see a problem is when we are trying to determine the file name length where the code reads int iFileNameLength = BitConverter.ToInt32(rcvdData, 0); - When debugging this variable is always determined as '0', which I assume is not correct? I am not sure. The required results that needs to be achieved is that the Server should receive the file and after the successful transfer, the file name should be displayed in the ListBox.
Here is screen shots showing the problem i'm experiencing:
We can see that bytes are definitely received:
Note that the file name can not be retrieved from using the file name length and offset:
And thus, the following error occurs:
My experience and expertise lie elsewhere and this is the first time I am coding within this paradigm(File transfer over network). I do however have theoretical knowledge about/studied the OSI model and TCP/IP Protocol stack, but never coded anything like this. One note given to me was that the server was coded with the assumption that it will be executed on a specific PC and I am allowed to change code of the Server application if it is absolutely necessary.
Try this. You have to do thinks in reverse on the client and using a List makes things easier.
private void TransferFile(string _sFileName, string _sIPAdress)
{
List<Byte> bFileBuffer = File.ReadAllBytes(_sFileName).ToList();
byte[] bFileName = Encoding.ASCII.GetBytes(_sFileName);
bFileBuffer.InsertRange(0, bFileName);
//Get the File name length, BitConvertor occupies the first 4 bytes
byte[] brcvdDataCount = BitConverter.GetBytes((UInt32)_sFileName.Count());
bFileBuffer.InsertRange(0, brcvdDataCount);
//Open TCP/IP Connection
TcpClient tcpClientSocket = new TcpClient(_sIPAdress, 8080);
NetworkStream nsNetworkStream = tcpClientSocket.GetStream();
nsNetworkStream.Write(bFileBuffer.ToArray(), 0, bFileBuffer.Count);
nsNetworkStream.Close();
}
There are two things with the server code you should be aware of
/Get the File name length, BitConvertor occupies the first 4 bytes
int iFileNameLength = BitConverter.ToInt32(rcvdData, 0);
//Get the file name using length as the size and 4 as the offset
string sFileName = Encoding.ASCII.GetString(rcvdData, 4, iFileNameLength);
1) You need to add a byte count to beginning of upload.
2) The code is using Ascii Encoding which means you can not upload binary data.
public void read(byte[] bytess)
{
int davar = this.clientSocket.Receive(bytess);
MemoryStream m = new MemoryStream(bytess);
BinaryFormatter b = new BinaryFormatter();
m.Position = 0;
SPacket information = b.Deserialize(m) as SPacket;
Image imageScreenShot = information.ScreenShot;
if (information.Premissionize)
Premitted = true;
if (information.Text != "")
{
cE.GetMessageFromServer(information.Text);
}
if (imageScreenShot == null)
return;
Bitmap screenShot = new Bitmap(imageScreenShot);
cE.UpdatePhoto(screenShot);
//screenShot.Dispose();
//Form1.t.Text = forText;
}
I have this read function in the client and when I run it online between 2 lan computers deserialization exception is thrown.
I guess that something delaying all the packet and only part of it has arrived. It said that the binary header is not valid.
How can I make sure in C# that I got the whole packet?
By the way this is TCP
The Receive function reads at least one byte and at most as many bytes as have been sent. Right now you assume that a single read will read everything which is not the case.
Deserialize from a new NetworkStream(socket). This allows BinaryFormatter to draw bytes from the socket.
What you wrote there about packets being delayed and such is not accurate. TCP shields you from that.
I'm trying to receive large strings over TCP, I tried various methods, but none of them worked as good as this one (which is quite simple actually).
public partial class MyClass : Form
{
Int64 counter;
StreamWriter writer;
StreamReader reader;
public MyClass(object streamIn, object StreamOut)
{
InitializeComponent();
richTextBox1.BackColor = Color.Black;
richTextBox1.ForeColor = Color.Gray;
writer = (StreamWriter)streamIn;
reader = (StreamReader)StreamOut;
}
private void button1_Click(object sender, EventArgs e)
{
JObject o = new JObject();
char[] buffer = new char[1024];
int count = buffer.Length;
o.Add("comando", 15);
o.Add("filename", textBox2.Text);
o.Add("param", textBox3.Text);
writer.Write(o.ToString());
writer.Flush();
richTextBox1.Text = reader.ReadToEnd();
}
}
The problem using this is that I have to close the stream on the other end, in order for this to read. There is any way I can use reader.ReadToEnd() without having to close the stream on the other end after sending and therefore closing the connection between client-server?
Checkout the Basic Example for the network library networkcomms.net which is covered in the Getting Started article. Although this is a console example it allows you to send arbitrary length strings.
Your example looks like it might be a winform application. There is also a WPF chat application example if that is of interest.
Here is my code to Serialize, Deserialize and Save an image to the file system. I have looked at many examples of serialization/deserialization and I just want to get some feedback as I am sure my code could be improved. Any feedback would be greatly appreciated. I know this is a common problem so hopefully this question will be a good resource for others in the future.
This is the revised code using recommendations:
private void Form1_Load(object sender, EventArgs e)
{
RunTest();
}
private void RunTest()
{
byte[] jpgba = ConvertFileToByteArray("D:\\Images\\Image01.jpg");
using (Image jpgimg = ConvertByteArrayToImage(jpgba))
{
SaveImageToFileSystem(jpgimg, "D:\\Images\\Image01_Copy.jpg");
}
byte[] pngba = ConvertFileToByteArray("D:\\Images\\Image02.png");
using (Image pngimg = ConvertByteArrayToImage(pngba))
{
SaveImageToFileSystem(pngimg, "D:\\Images\\Image02_Copy.png");
}
byte[] gifba = ConvertFileToByteArray("D:\\Images\\Image03.gif");
using (Image gifimg = ConvertByteArrayToImage(gifba))
{
SaveImageToFileSystem(gifimg, "D:\\Images\\Image03_Copy.gif");
}
MessageBox.Show("Test Complete");
this.Close();
}
private static byte[] ConvertFileToByteArray(String FilePath)
{
return File.ReadAllBytes(FilePath);
}
private static Image ConvertByteArrayToImage(byte[] ImageByteArray)
{
using (MemoryStream ms = new MemoryStream(ImageByteArray))
{
return Image.FromStream(ms);
}
}
private static void SaveImageToFileSystem(Image ImageObject, string FilePath)
{
// ImageObject.Save(FilePath, ImageObject.RawFormat);
// This method only works with .png files.
// This method works with .jpg, .png and .gif
// Need to copy image before saving.
using (Image img = new Bitmap(ImageObject.Width, ImageObject.Height))
{
using (Graphics tg = Graphics.FromImage(img))
{
tg.DrawImage(ImageObject, 0, 0);
}
img.Save(FilePath, img.RawFormat);
}
return;
}
What I have see from quick look:
Streams should be wrapped in using(...) pattern, in your case if exception occurs during processing, then Dispose() won't be called.
using (FileStream fs = new FileStream(FilePath, FileMode.Open))
{
// Another small optimization, removed unnecessary variable
byte[] iba = new byte[(int)fs.Length];
fs.Read(iba, 0, iba.Length);
}
You should catch only exceptions you expect. For example in SerializeImage this will be IOException. Catching all exceptions is very bad practice.
}
catch (IOException ex)
{
Image.FromStream method depends on stream, so if you close underlying stream and return Image you can receive unpredictable behavior (well, in most cases this will work, but sometimes error occurs). So you need to create image copy and return it.
using (MemoryStream ms = new MemoryStream(ImageByteArray))
{
using (Image img = Image.FromStream(ms))
{
return new Bitmap(img);
}
}
You are not disposed tg graphics object and img object in SaveImage method (but disposed ImageObject, see next paragraph). And in general I do not see necessity in such logic, simply call ImageObject.Save(..., ImageFormat.Png) if you want to save image preserving quality.
In the same method (SaveImage) you are disposed ImageObject parameter. This is also bad practice in most cases, consider disposing this image outside worker method by using using(...) pattern.
Here's a bit more:
private void RunTest()
{
// byte array that can be stored in DB
byte[] iba;
// image object to display in picturebox or used to save to file system.
iba = ReadImage("D:\\Images\\Image01.jpg");
using (Image img = DeserializeImage(iba))
{
SaveImage(img, "D:\\Images\\Image01_Copy.jpg");
}
iba = ReadImage("D:\\Images\\Image02.png");
using (Image img1 = DeserializeImage(iba))
{
SaveImage(img1, "D:\\Images\\Image02_Copy.png");
}
iba = ReadImage("D:\\Images\\Image03.gif");
using (var img2 = DeserializeImage(iba))
{
SaveImage(img2, "D:\\Images\\Image03_Copy.gif");
}
MessageBox.Show("Test Complete");
}
private static byte[] ReadImage(String filePath)
{
// This seems to be the easiest way to serialize an image file
// however it would be good to take a image object as an argument
// in this method.
using (var fs = new FileStream(filePath, FileMode.Open))
{
Int32 fslength = Convert.ToInt32(fs.Length);
var iba = new byte[fslength];
fs.Read(iba, 0, fslength);
return iba;
}
}
private static Image DeserializeImage(byte[] imageByteArray)
{
using (var ms = new MemoryStream(imageByteArray))
{
return Image.FromStream(ms);
}
}
private static void SaveImage(Image imageObject, string filePath)
{
// I could only get this method to work for .png files.
// imageObject.Save(filePath, imageObject.RawFormat);
// This method works with .jpg, .png and .gif
// Need to copy image before saving.
using (Image img = new Bitmap(imageObject.Width, imageObject.Height))
{
using (Graphics tg = Graphics.FromImage(img))
{
tg.DrawImage(imageObject, 0, 0);
}
img.Save(filePath, img.RawFormat);
}
return;
}
Note what you called Serializing is just reading the bytes in. Serializing is more what you're doing when you Save.
I got rid of all the try/catch blocks. The best they were doing for you is telling you whether the problem happened in Reading, Saving or Deserializing. You can determine that from the stack trace, which you were destroying by only displaying ex.Message.
You were also returning null on a serious exception, propagating failure.
Beside that I agree with everything arbiter said.
As John Saunder says, serializing and deserializing are more than just reading the raw data from a file. See Wiki on Serialization
For images in .net, you don't need to use anything more than the provided framework methods (most of the time)
So Loading an Image (De-Serialization) in .net is.
using System.Drawing.Image;
Image test;
test = Image.FromFile(#"C:\myfile.jpg")
test = Image.FromStream(myStream); // or you can load from an existing stream
Likewise, Saving the image (Serialization) is:
test.Save(#"C:\anotherFile.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
These are the basics of loading and saving an image in .net. If you have a more specific scenario, ask another question.