Socket plain text response cut off - c#

I'm having trouble getting a socket connection between Android(client) and a c# app(server) to read a response correctly.
I've successfully got a message sending from android to c#, and I'm reading it fine on that end. But when I try to send an acknowledgement back to android, I don't know the correct way to handle this, and I've had to make some assumptions where tutorials have been unclear. I am getting a response back in android, but it's not 100% correct. I've verified via Wireshark that c# is sending what I want it to send, and the text looks fine until it gets to android.
C#:
public void SendClientMessage()
{
NetworkStream clientStream = _Client.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Client!"); //Static test message
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
...
Android:
private void listenResponse()
{
Log.i(TAG, "listenRespose() Listening...");
try
{
InputStream is = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int countBytesRead = bis.read(buffer, 0, 8);
String response = new String(buffer);
Log.i(TAG, "listenResponse() Heard: " + response);
}
catch (IOException e)
{
Log.e(TAG, "listenResponse() IOException", e);
e.printStackTrace();
}
Log.i(TAG, "listenResponse() Done Listening.");
}
...
WireShark shows:
Hello Client!
...
Android LogCat shows:
listenRespose() Listening...
listenResponse() Heard: Hello Cl??????????????????????????? [... ?s continue for a long time]
listenResponse() Done Listening.
If instead I initialize my String like this:
String response = new String(buffer, 0, countBytesRead);
I don't get all the question marks at least, but I still don't get the full string I should be getting. Am I initializing my byte[] wrong, or is there a different way to do this better suited for plain text?

You only read 8 bytes here
int countBytesRead = bis.read(buffer, 0, 8);
// max bytes to read ^
instead, read all you can:
int countBytesRead = bis.read(buffer, 0, Buffer.length);
The question marks you see are because your buffer is not initialized.

Related

TCPListener File Transfer

I have a little complication i encounter.
I may not be expert in TCP Connections but i hope someone here would help me.
This is my Client Code:
void Connect(String server, String message)
{
try
{
Int32 port = 8968;
TcpClient client = new TcpClient(server, port);
Byte[] data = File.ReadAllBytes(curSelectedFile);
NetworkStream stream = client.GetStream();
Byte[] fileData = File.ReadAllBytes(curSelectedFile);
Byte[] msgData = Encoding.ASCII.GetBytes("SendFile");
Byte[] sendData = new byte[fileData.Length + msgData.Length];
// Copy data to send package.
msgData.CopyTo(sendData, 0);
fileData.CopyTo(sendData, 4);
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", message);
// Receive the TcpServer.response.
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
// Close everything.
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
This is my server one:
// Listen loop.
while(true)
{
using (TcpClient tcpClient = myListener.AcceptTcpClient())
{
Console.WriteLine("[Server] Acceptam client.");
using (NetworkStream networkStream = tcpClient.GetStream())
{
// 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);
}
// Incercam sa vedem ce doreste clientul.
string msg = Encoding.ASCII.GetString(data[0], 0, length);
if(msg.StartsWith("SendFile"))
{
using (Stream stream = new FileStream(#"C:\test.mp3", FileMode.Create, FileAccess.ReadWrite))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
networkStream.Position = 4;
binaryFormatter.Serialize(networkStream, data.ToArray());
}
}
}
}
}
What i'm trying to do here:
- I want the client to send a Message.. like "SaveFile" & after this string to be the filedata.
- The server should read the client message, and to process stuff according to the Client sentstring, before doing something with the file.
I believe that i don't know how to do it.
May i have an example on how to send/receive and read certain strings from the beggining of the file? How i can put them in the byte array and how to read it... It's quite overwhelming..
PS: The current Server Code is reading the data and CAN write as i coded it, without losing any packages. But also he's writing the aditional packets i sent before i converted the bytes of the file.
networkStream.Position = 4; isn't legal, because NetworkStream is not seekable.
I would discourage you from mixing text and binary data, just because of the complication it makes in the application protocol. But if you really want to do that, you should use BinaryWriter and BinaryReader, because it can write strings to a stream which can then be read later without consuming the bytes after the string.
Then you can do something like this...
In the client:
BinaryWriter writer = new BinaryWriter(networkStream);
writer.Write("SendFile");
writer.Write(fileData.Length);
writer.Write(fileData);
In the server:
BinaryReader reader = new BinaryReader(networkStream);
switch (reader.ReadString())
{
case "SendFile":
{
int length = reader.ReadInt32();
byte[] fileData = reader.ReadBytes(length);
// ... then do whatever with fileData, like write to a file
break;
}
}
The BinaryWriter/Reader implementation of the length-counted string is non-standard, so if you wanted to interact with any other non-.NET code using this technique, it would be more complicated because you have to replicate/reimplement the non-standard length-counting logic yourself.
IMHO a better approach is to encode commands as fixed-length data, e.g. an 8-, 16-, or 32-bit value, which is just some integer that specifies the command. Then you can list your commands in the code as an enum type, casting to/from the network stream for the I/O. This would be more portable, easier to implement on non-.NET platforms.

C# Sending request with SSL via TCP doesn't work

here is my code to send TCP request coded in SSL and get the answer from server, but it doesn't work. Debugging and trying to catch exceptions didn't work.
public void SendRequest(string request)
{
byte[] buffer = new byte[2048];
int bytes = -1;
sslStream.Write(Encoding.ASCII.GetBytes(request));
bytes = sslStream.Read(buffer, 0, buffer.Length);
Console.WriteLine(Encoding.ASCII.GetString(buffer, 0, bytes));
}
I took that from stackoverflow answer so I'm suprised it doesn't work. Here is my code that receives greeting from server (it works properly):
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = 0;
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
Console.Write(messageData.ToString());
If the sslStream.Read(buffer, 0, buffer.Length); is hanging, it's because the server hasn't sent a response.
Taking a look at your code on github (linked from your other question), it looks like you are using Console.ReadLine() to read commands and then write them to your network stream.
What is happening is that ReadLine() strips off the "\r\n" in the string that it returns, so what you'll need to do when sending it to the server is to add back the "\r\n" so that the server knows that the command is finished (it waits until it gets an EOL sequence before it will respond).
What you could do is in SendRequest(), you could do:
sslStream.Write(Encoding.ASCII.GetBytes(request + "\r\n"));

TcpClient not receiving full server reply

The following method is supposed to send a couple of commands to the server. It is also supposed to return full reply from the server as a string. The problem I have is with the reply section of the code; specifically, I am not getting the FULL reply back. If I add the following line Console.WriteLine(bytesRead); before the memoryStream.Write(buffer,0,bytesRead); I receive the full reply. Seems like this silly line of code helps "pause" something so that all data is returned. I really do not know what I am doing wrong and I need your help. Thanks
public string Send(List<string> commands)
{
try
{
// String that will contain full reply from server
string fullServerReply = string.Empty;
TcpClient tcpClient = new TcpClient(host, port);
NetworkStream networkStream = tcpClient.GetStream();
foreach (string command in commands)
{
// Check to see if this NetworkStream is writable
if (networkStream.CanWrite)
{
// Translate the passed message into UTF8 and store it as a byte array.
byte[] sendBytes = Encoding.UTF8.GetBytes(command);
// Send the message to the connected TcpServer.
networkStream.Write(sendBytes, 0, sendBytes.Length);
}
else
{
// Close everything.
networkStream.Close();
tcpClient.Close();
return "";
}
// Check to see if this NetworkStream is readable
if (networkStream.CanRead)
{
using (MemoryStream memoryStream = new MemoryStream())
{
// Buffer to store the response bytes.
byte[] buffer = new byte[1024];
do
{
int bytesRead = networkStream.Read(buffer, 0, buffer.Length);
if (bytesRead <= 0)
{
break;
}
//Console.WriteLine(bytesRead); <- BY ADDING THIS CODE I GET THE FULL REPLY
memoryStream.Write(buffer, 0, bytesRead);
} while (networkStream.DataAvailable);
memoryStream.Position = 0;
fullServerReply += Encoding.UTF8.GetString(memoryStream.ToArray()); // THIS STRING DOES NOT CONTAIN FULL REPLY
}
}
else
{
// Close everything.
networkStream.Close();
tcpClient.Close();
return "";
}
}
// Close everything.
networkStream.Close();
tcpClient.Close();
return fullServerReply.Trim();
}
catch (ArgumentNullException ex)
{
return "";
}
catch (SocketException ex)
{
return "";
}
return "";
}
Yup! DataAvailable is NOT indicator that a complete stream has been received. It only indicates if some data that hasn't yet been ready available in receive buffer.
When you add console.readline, you give network packets a chance to catch up and get more data in buffer.
Instead you should keep looping until either the network stream is closed, or since it is a tcp stream, whatever the protocol you're using that tells you how big your application packet is to keep reading for.
That's kind of how networking works - you cannot assume that you get everything back instantly, or in the same number of reads as there were writes. In your case, adding the write to the console was just enough to have the client spool everything.
Try this to flush all the writes to the stream after the loop:
memoryStream.Flush();
memoryStream.Position = 0;
fullServerReply += Encoding.UTF8.GetString(memoryStream.ToArray()); // THIS STRING DOES NOT CONTAIN FULL REPLY

Sending/receiving .txt file from Android to PC, not whole file is sent

I'm trying to make an Android app that sends a .txt file to a Windows Forms application on my computer. The problem is that not the whole file gets sent (I haven't been able to find out whether the problem is on the sending or receiving side). I only get a random part from somewhere in the middle of the .txt file to the receiving side. What am I doing wrong? The strange thing is that it has worked perfectly a few times, but now I'm never getting the beginning or the end of the file.
The Android app is written in Java, and the Windows Forms app is written in C#. filepath is the name of my file. What is the problem here?
Code for Android app (sending file)
//create new byte array with the same length as the file that is to be sent
byte[] array = new byte[(int) filepath.length()];
FileInputStream fileInputStream = new FileInputStream(filepath);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
//use bufferedInputStream to read to end of file
bufferedInputStream.read(array, 0, array.length);
//create objects for InputStream and OutputStream
//and send the data in array to the server via socket
OutputStream outputStream = socket.getOutputStream();
outputStream.write(array, 0, array.length);
Code for Windows Forms app (receiving file)
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[65535];
int bytesRead;
clientStream.Read(message, 0, message.Length);
System.IO.FileStream fs = System.IO.File.Create(path + dt);
//message has been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
fs.Write(message, 0, bytesRead);
fs.Close();
Instead of reading the complete array to your memory and sending it to an outputstream afterwards, you could do read/write at the same time, and just use a "small" buffer byte array. Something like this:
public boolean copyStream(InputStream inputStream, OutputStream outputStream){
BufferedInputStream bis = new BufferedInputStream(inputStream);
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
byte[] buffer = new byte[4*1024]; //Whatever buffersize you want to use.
try {
int read;
while ((read = bis.read(buffer)) != -1){
bos.write(buffer, 0, read);
}
bos.flush();
bis.close();
bos.close();
} catch (IOException e) {
//Log, retry, cancel, whatever
return false;
}
return true;
}
on the receiving side you should do the same: Write a portion of bytes as you receive them and not store them completly into the memory befor using.
This might not fix your issue, but is something you should improve anyway.

create a "listener" for an incoming socket stream that will contain a byte[] array

I'm creating a socket server that needs to continuously listen for incoming messages from the connected clients. Those messages will be sent in a byte[] array. I had the server working great with a StreamReader but StreamReader only works with textual represenations of the data being sent...not byte[] arrays.
Here's what I had:
StreamReader reader = new StreamReader(Client.GetStream());
string line = "";
while (true)
{
line = reader.ReadLine();
if (!string.IsNullOrEmpty(line))
{
parentForm.ApplyText(line + "\r\n");
SocketServer.SendBroadcast(line);
}
}
I need to now convert that into a raw stream somehow that will convert the stream contents into a byte[] array but I can't seem to get a handle on it.
I tried this:
while (true)
{
var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
var buffer = new byte[512];
var bytesRead = default(int);
while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
memstream.Write(buffer, 0, bytesRead);
bytes = memstream.ToArray();
}
//parentForm.ApplyText(bytes.Length + "\r\n");
}
but as you might guess, the while(true) loop doesn't quite work how I need it to. Can anyone help me with some code adjustment to make this work as I need it to. It needs to continuously listen for incoming messages, then when a message is received, it needs to do something with that message (the byte[] array) then go back to listening again.
TIA
I guess "listening continuously" is not task of reader its a task of listener. I ran into same problem when i was writing server using TcpListener. I am not sure what you want to do but i am posting solution for your "listening continuous" and reading into byte[] problem. I guess this code might help you:
TcpListener t = new TcpListener(IPAddress.Loopback, _port);
t.Start();
Console.WriteLine("Server is started and waiting for client\n\n");
byte[] buff = new byte[255];
NetworkStream stream;
TcpClient client;
while(true)
{
client = t.AcceptTcpClient();
if (!client.Connected)
return;
stream = client.GetStream();
while ((stream.Read(buff, 0, buff.Length)) != 0)
{
break;
}
if (0 != buff.Length)
break;
}
There's no need to convert anything. GetStream() returns a NetworkStream. See the sample Microsoft includes in the NetworkStream.Read Method. All you have to do is replace the myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); line with an appropriate storage mechanism.

Categories