I'm building a WPF application which is using socket to transfer data to and from the server.
The problem: While sending and receiving data from the server, the screen is frozen, and I would like to add a simple loading animation so the end user will know that it's currently loading, but I don't know how
My C# socket code:
public static string SendRecOne(string dataToSvr)
{
TcpClient client = new TcpClient(SERVER_NAME, PORT);
#region SendRequest
int ByteCount = Encoding.ASCII.GetByteCount(dataToSvr); //How much bytes?
byte[] ByteBuffer = new byte[1024]; //initialize byte array
ByteBuffer = Encoding.ASCII.GetBytes(dataToSvr);
NetworkStream stream = client.GetStream();
stream.Write(ByteBuffer, 0, ByteBuffer.Length);
#endregion
#region Receive Response
//byte[] responseData = new byte[client.ReceiveBufferSize];
//int bytesRead = stream.Read(responseData, 0, client.ReceiveBufferSize);
int i;
string ToReturn = null;
ByteBuffer = new byte[ByteBuffer.Length];
MemoryStream ms = new MemoryStream();
while (true)
{
if (stream.DataAvailable)
{
while ((i = stream.Read(ByteBuffer, 0, ByteBuffer.Length)) != 0)
{
ms.Write(ByteBuffer, 0, ByteBuffer.Length);
if (stream.DataAvailable)
continue;
else
break;
}
ToReturn = Encoding.ASCII.GetString(ms.ToArray());
return ToReturn;
}
}
#endregion
and I'm calling it after pressing a button in window which it's name is "login.xaml", and after checking the data IS ok, it closes the current windows and initializes dashboard.xaml.
I just need to add an animation while communicating with server.
Thanks!
Call your SendRecOne method on a background thread or make it asynchronous by using the *Async overloads:
public static async Task<string> SendRecOne(string dataToSvr)
{
progressBar.Visibility = Visibility.Visible;
string ToReturn = null;
using (TcpClient client = new TcpClient(SERVER_NAME, PORT))
{
int ByteCount = Encoding.ASCII.GetByteCount(dataToSvr); //How much bytes?
byte[] ByteBuffer = new byte[1024]; //initialize byte array
ByteBuffer = Encoding.ASCII.GetBytes(dataToSvr);
NetworkStream stream = client.GetStream();
await stream.WriteAsync(ByteBuffer, 0, ByteBuffer.Length);
//byte[] responseData = new byte[client.ReceiveBufferSize];
//int bytesRead = await stream.ReadAsync(responseData, 0, client.ReceiveBufferSize);
int i;
ByteBuffer = new byte[ByteBuffer.Length];
MemoryStream ms = new MemoryStream();
if (stream.DataAvailable)
{
while ((i = await stream.ReadAsync(ByteBuffer, 0, ByteBuffer.Length)) != 0)
{
await ms.WriteAsync(ByteBuffer, 0, ByteBuffer.Length);
if (!stream.DataAvailable)
break;
}
ToReturn = Encoding.ASCII.GetString(ms.ToArray());
}
progressBar.Visibility = Visibility.Collapsed;
}
return ToReturn;
}
XAML:
<ProgressBar x:Name="progressBar" IsIndeterminate="True" />
The UI thread cannot both process messages and execute your code simultaneously.
Related
I have a "messenger" application that works like this:
Client Sends Message to Server -> Server forwards Message to rest of the clients -> Clients read the message.
The server always receives the messages, but the clients do not.
Code that forwards the message to all clients (Server/TcpListener):
public static void SendToAll(TcpClient sender, string message)
{
// Log the recieved message in the console
Console.WriteLine(message);
// Save the recieved message for new clients
messageLog += message + "\n";
byte[] msg = Encoding.ASCII.GetBytes(message);
foreach (TcpClient client in clients.Keys)
{
if (client == sender || clients[client] == "") continue;
if (!client.Connected)
{
clients.Remove(client);
continue;
}
NetworkStream stream = client.GetStream();
stream.Write(msg, 0, msg.Length);
}
}
Code that reads messages from the server (TcpClient):
byte[] buffer = new byte[1024];
while (true)
{
using (MemoryStream memoryStream = new MemoryStream())
{
Int32 bytes = 0;
do
{
bytes = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytes);
}
while (stream.DataAvailable);
Console.WriteLine(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
Server (image attached)
Client1 (image attached)
Client2 (image attached)
Found the answer.
The problem was probably caused because TCP is slow; not entirely finished with reading all the stream and already tasked with reading new data.
So I Switched
byte[] buffer = new byte[1024];
while (true)
{
using (MemoryStream memoryStream = new MemoryStream())
{
Int32 bytes = 0;
do
{
bytes = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytes);
}
while (stream.DataAvailable);
Console.WriteLine(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
to
// ... Main() {
while (true) StartRead();
// ... }
public static async void StartRead(NetworkStream stream)
{
byte[] buffer = new byte[1024];
Int32 bytesRead = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length));
string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine(message);
}
Now I know that this question has been asked a lot but I really just don't get how to do it. I tried this but the file don't get complete I just receive just a bit of the file and the rest is just NULL here is my code in client part I first send a message to the server that contain the file size like this :
// here I send the a upload request with the size of the file that I want to send
byte[] data = Encoding.Unicode.GetBytes("uploadreq~"+new FileInfo(ofg.FileName).Length);
// here is the socket client
target.Send(data);
Then on the server side :
if (cmd.Contains(update.update_request))
{
// here I set an int var to the file size
update.update_size = int.Parse(cmd.Split('~')[1]);
// here I setup the a new byte array with the given file size
update.update_received = new byte[update.update_size];
// then I send a upload confirm command
Connection.sendCommand(Commands.update_confirme);
update.isupdate = true;
}
Again on the client side when the confirmation has been received :
if (cmd.StartsWith("updateConfirm"))
{
// reading all the bytes of the file and sending them
byte[] datatosend = File.ReadAllBytes("the file path");
Connection.send_bytes(datatosend);
}
Finally on the client side :
private void receiveInfo()
{
byte[] buffer = new byte[999999];
int received = 0;
try
{
received = Connection.clientSocket.Receive(buffer);
}
catch (SocketException)
{
Connection.clientSocket.Close();
Connection.clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Connection.makeConnection();
}
if (received == 0)
return;
byte[] data = new byte[received];
Array.Copy(buffer, data, received);
if (update.isupdate == true)
{
// this calls a method that process the data received
update.process_update(data);
}
}
public static void process_update(byte[] data)
{
int writeSize = 0;
Buffer.BlockCopy(data, 0, update_received, writeSize, data.Length);
writeSize += data.Length;
if (update_received.Length == update_size)
{
using (FileStream fs = File.Create("the path to where the file shloud go"))
{
byte[] info = update_received;
fs.Write(info, 0, info.Length);
}
Array.Clear(update_received, 0, update_received.Length);
isupdate = false;
}
}
As I was writing this question I changed the buffer size in the receive info method and that seems to change stuff a bit but still, the file won't arrive fully..
Try this for the client:
private void SendFile(String FileName,String IPAddress,int Port )
{
System.Net.Sockets.TcpClient TcpClient = new System.Net.Sockets.TcpClient(IPAddress, Port);
System.Net.Sockets.NetworkStream NetworkStream = TcpClient.GetStream();
System.IO.Stream FileStream = System.IO.File.OpenRead(FileName);
byte[] FileBuffer = new byte[FileStream.Length];
FileStream.Read(FileBuffer, 0, (int)FileStream.Length);
NetworkStream.Write(FileBuffer, 0, FileBuffer.GetLength(0));
NetworkStream.Close();
}
and this is the code for the server:
private void ReceiveFile(String FilePath, int Port)
{
System.Threading.Thread WorkerThread = new System.Threading.Thread(() =>
{
System.Net.Sockets.TcpListener TcpListener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 60000);
TcpListener.Start();
System.Net.Sockets.Socket HandlerSocket = TcpListener.AcceptSocket();
System.Net.Sockets.NetworkStream NetworkStream = new System.Net.Sockets.NetworkStream(HandlerSocket);
int BlockSize = 1024;
int DataRead = 0;
Byte[] DataByte = new Byte[BlockSize];
lock (this)
{
System.IO.Stream FileStream = System.IO.File.OpenWrite(FilePath);
while (true)
{
DataRead = NetworkStream.Read(DataByte, 0, BlockSize);
FileStream.Write(DataByte, 0, DataRead);
if (DataRead == 0)
{
break;
}
}
FileStream.Close();
}
});
WorkerThread.Start();
}
This will only transfer one file.
I'm working on a simple TCP server application using C# v4.0 (.Net Framework v4):
I want to accomplish these two steps:
Client sends message1 to Server (client can be .net or java application)
Server sends back message2 to Client as a response to message1
I have a problem with my server, it is not able to read message1 correctly unless I use one of these inappropriate solutions:
1) Use a MemoryStream with a Buffer of only 1 byte (works but slow):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[1]; // works but slow in case of big messages
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
Example: if message1.Length == 12501 and I use a buffer of 1024 the NetworkStream.Read() loop reads only 2048 bytes of message1, I think NetworkStream.DataAvailable does not return the correct value!
2) Use a Thread.Sleep(1000) after reading from NetworkStream to Buffer (works but slow):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[8192];
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
Thread.Sleep(1000); // works but receiving gets slow
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
3) Use StreamReader.ReadToEnd() and close the client's socket after sending messages1 (works but server cannot response to client with message2):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream, true);
string message1 = streamReader.ReadToEnd(); // blocks until client close its socket
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
4) Loop with StreamReader.ReadLine() and close the client's socket
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream);
StringBuilder stringBuilder = new StringBuilder();
while (!streamReader.EndOfStream)
{
stringBuilder.AppendLine(streamReader.ReadLine()); // blocks until client close its socket
}
string message1 = stringBuilder.ToString();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
5) Prefix message1 with its length (works but requires the client to add extra bytes to the message and this will not work with existing java clients)
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
byte[] bufferMessageLength = new byte[4]; // sizeof(int)
networkStream.Read(bufferMessageLength, 0, bufferMessageLength.Length);
int messageLength = BitConverter.ToInt32(bufferMessageLength, 4);
byte[] bufferMessage = new byte[messageLength];
networkStream.Read(bufferMessage, 0, bufferMessage.Length);
memoryStream.Write(buffer, 0, bufferMessage.Length);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
Regarding to these issues, what is the best method to read all data from the client without using the above mentioned solutions?
instead of using networkStream.DataAvailable append the size of data at the start of your message. for example the length of your message is 12501 use first 4 bytes as message length.
First define a method to read data from buffer
public static void ReadStream(NetworkStream reader, byte[] data)
{
var offset = 0;
var remaining = data.Length;
while (remaining > 0)
{
var read = reader.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
and then read data from stream.
var bytesRead = 0;
var offset = 0;
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
var bufferMessageSize = new byte[4]; // int32
ReadStream(networkStream, bufferMessageSize);
var messageSize = BitConverter.ToInt32(bufferMessageSize, 4); // bytesToRead
var bufferMessage = new byte[messageSize];
ReadStream(networkStream, bufferMessage);
// Now Respond back Client here
// networkStream.Write();
If the communication is line oriented, then StreamReader.ReadLine() could be suitable.
ReadLine() Reads a line of characters from the current stream and returns the
data as a string.
To put my toe in the water of Network programming, I wrote a little Console App to send a png file to a server (another console app). The file being written by the server is slightly bigger than the source png file. And it will not open.
The code for the client app is:
private static void SendFile()
{
using (TcpClient tcpClient = new TcpClient("localhost", 6576))
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
//FileStream fileStream = File.Open(#"E:\carry on baggage.PNG", FileMode.Open);
byte[] dataToSend = File.ReadAllBytes(#"E:\carry on baggage.PNG");
networkStream.Write(dataToSend, 0, dataToSend.Length);
networkStream.Flush();
}
}
}
The code for the Server app is :
private static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(Listen));
thread.Start();
Console.WriteLine("Listening...");
Console.ReadLine();
}
private static void Listen()
{
IPAddress localAddress = IPAddress.Parse("127.0.0.1");
int port = 6576;
TcpListener tcpListener = new TcpListener(localAddress, port);
tcpListener.Start();
using (TcpClient tcpClient = tcpListener.AcceptTcpClient())
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
var data = new List<byte>();
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
var copy = new byte[length];
Array.Copy(bytes, 0, copy, 0, length);
data.AddRange(copy);
}
BinaryFormatter binaryFormatter = new BinaryFormatter();
stream.Position = 0;
binaryFormatter.Serialize(stream, data.ToArray());
}
}
}
tcpListener.Stop();
The size of the written file is 24,103Kb, whereas the source file is only 24,079Kb.
Is it apparent to anyone why this operation is failing?
Cheers
You are writing your output using a BinaryFormatter. I'm pretty sure that this will add some bytes at the start of the output to indicate the type that you're outputting (in this case System.Byte[]).
Just write the bytes out directly to the file without using the formatter:
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, length);
}
}
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));