Why is using StreamWriter.WriteLine, followed by StreamWriter.BaseStream.WriteByte unreliable? - c#

I am developing a networking application.
For sending and receiving data, I am using NetworkStream that I get from TcpClient.
For sending text, I wrapped NetworkStream in a StreamWriter, and I simply call StreamWriter.WriteLine(text), followed by StreamWriter.Flush().
For sending 1 byte flags from server to client (that are required for in my own communication protocol), I am using StreamWriter.BaseStream.WriteByte(byte). So, it is all the same underlying stream and it worked great until I got into this situation. The following code is where it breaks:
// This is server sending data to client.
writer.WriteLine(text);
writer.Flush();
writer.BaseStream.WriteByte(flag);
// This is client trying to read incoming data from server.
string text = reader.ReadLine(); // This will read text successfully.
int flag = reader.BaseStream.ReadByte(); // Problem is here: It will block here as if there is no data.
However, if I put some delay between flushing data and sending the byte, everything works fine...
// This is server sending data to client.
writer.WriteLine(text);
writer.Flush();
Thread.Sleep(1000); <-------------------------------- delay
writer.BaseStream.WriteByte(flag);
// This is client trying to read incoming data from server.
string text = reader.ReadLine(); // This will read text successfully.
int flag = reader.BaseStream.ReadByte(); // Now the byte is read successfully as well.
Can someone please explain why this is happening and how I can fix it?

Related

Reading data from a StreamSocket

From looking at the example application on the Microsoft Website How to connect with a stream socket (XAML)
, I have learned how to connect to a server, and send string to the server. However, the example doesn't quite extend on reading data from the socket.
The server is a c# windows console application and what it does is send data to the mobile client by using a network stream.
//send user response
//message is a frame packet containing information
message = new Message();
//type 1 just means it's successfull
message.type = 1;
//using Newton JSON i convert the Message Object into a string object
string sendData = JsonConvert.SerializeObject(message);
//conver the string into a bytearray and store in the variable data (type byte array)
byte[] data = GetBytes(sendData);
//netStream is my NetworkStream i want to write the byte array called data, starting to from 0, and ending at the last point in array
netStream.Write(data, 0, data.Length);
//flushing the stream, not sure why, flushing means to push data
netStream.Flush();
//debugging
Console.WriteLine("sent");
In order to read data from a stream, the DataReader class is used. I am quite new to c# Mobile, and the documentation on the DataReader class doesn't provide any implementations of examples, so how can I read data from the Stream Socket?
Using the example code from microsoft;
DataReader reader = new DataReader(clientSocket.InputStream);
// Set inputstream options so that we don't have to know the data size
reader.InputStreamOptions = InputStreamOptions.Partial;
await reader.LoadAsync(reader.UnconsumedBufferLength);
But now I am not sure how to read the byte array sent from the server.

Sending some constraints to client from server in C#

I have created a simple server using socket programming in C# which will receive a file from the client side. My sample code segment is given below.
I want to add some restrictions. I want to make a limit on the file size (such as 4 KB or 2 KB) and allowable file formats (such as .doc, .txt, .cpp, etc.) which will be sent to the client as soon as the client connects to the server so that the client can send files accordingly. How will I do that?
Sample code segment:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace FileTransfer
{
class Program
{
static void Main(string[] args)
{
// Listen on port 1234
TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
Console.WriteLine("Server started");
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected to client");
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
//Will add some lines to add restrictions...
}
}
}
}
Which additional lines will I have to add to the code to send the restrictions to client?
Basically I think mainly you need two things:
define application protocol as suggested in other answer
and handle partial read/writes
For handling partial reads (not sure how much such function is needed for write) you may use function like below:
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.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;
}
}
Thing is traditional Stream.Read() doesn't guarantee to read as many bytes as you told it, this method on the other hand, will ensure to have read as many bytes as specified in data.Length parameter. So you can use such function to implement the desired application protocol instead.
Some relevant information about such application protocols you will find here too
Ok this is for example how the server could send file length limit and the file extension:
// Send string
string ext = ".txt";
byte [] textBytes = Encoding.ASCII.GetBytes(ext);
ns.Write(textBytes, 0, textBytes.Length);
// Now, send integer - the file length limit parameter
int limit = 333;
byte[] intBytes = BitConverter.GetBytes(limit);
ns.Write(intBytes, 0, intBytes.Length); // send integer - mind the endianness
But you will still need some kind of protocol otherwise you should let client read the "full" stream and parse these data later somehow, which isn't trivial if the data doesn't have fixed length etc - otherwise how will the client distinguish which part of the message is text, which integer?
You seem to be making the classical socket mistake. The given code and explanation seem to assume sockets handle in messages. They don't. When used this way, you're using streaming internet sockets, which provide a stream, not messages.
You don't show any code that does the actual sending, so I'm guessing that you just pump a file's data to the other side and close the connection. How else will you know you've successfully transferred an entire file?
This set of rules that client and server have to follow in order to usefully exchange data through sockets is called an application protocol. You will have to have one, otherwise you'll just be sending data to $deity knows where, and you'll have no control over it at all. This means server nor client will know what's going on, they'll just be sending and receiving data and hoping all goes well. So there's not "a few lines" you have to add to your code, you'll have to restructure it entirely.
There are many ways to define an application protocol and many options to choose from, so I'm going to show you an arbitrary one: a textual explanation of messages that are prefixed with an ID and a payload length (if applicable), both in unspecified numeric variables. You could choose little-endian four-byte unsigned integers, for example.
Messages in this format are known as "Type/Length/Value" or TLV.
So we define these messages:
ID Name Direction Description Payload
1 ServerHello Server -> Client The server sends this message None.
to every connecting client. Or maybe server or
protocol version.
2 MaxUpload Server -> Client Sent after the ServerHello. Maximum upload size
in bytes.
3 AllowedExts Server -> Client Allowed upload extensions, The allowed extensions.
comma-separated. Sent after
MaxUpload message.
10 IncomingFile Client -> Server There's a file coming. The file name.
11 FileUpload Client -> Server The file to upload. The file data.
Sent after IncomingFile.
Now all that's required is to implement this application protocol in server and client and you're done.
You also have to decide what to do if a client or server doesn't adhere to the prototol. It can for example send a message that you can't parse, an unknown message ID, a message length that you don't want to support, an out-of-order message (FileUpload before IncomingFile) or a message that isn't conform the messages sent earlier, like a client uploading a larger file than the server said it would accept or an invalid extension. You also have to think about "acknowledgement" or response messages, like the server telling the client "OK, go ahead, send the next message".
All in all, this is a very broad question and not answered easily. I tried to address that in my comment to your question, which got removed. So here you have your answer.
You can learn more about this on the web, for example Beej's Guide to Network Programming as linked to by Giorgi (be sure to read the entire guide) and Stephen Cleary's blog.

C# Sending Hex string to tcp socket

I'm trying to send a hex string to a tcp socket. I have some problems with the format or conversion of this string, cause I'm not very sure what the format its using.
I've written a WindowsPhone app which is working fine based on Socket Class.
This app emulates request, that are normaly send from a desktop program to a device which hosts a webservice.
Via wireshark, I found out, that the webservice will accept an input stream (think its in hex) and returns a 2nd. hex stream which contains the data I need.
So the desktop app is sending a stream
and Wireshark shows when :
Data (8 bytes)
Data: 62ff03fff00574600
Length: 8
Now I've tried a lot to reproduce this stream. I thougt, it used to be a UTF8 string and converted this stream to this format. But every time I send it, is see in Wireshark the following output: 62c3bf03c3bf00574600
As far as i've investigated 62 = b but ff send always c3bf.
Does somebody know how to send this stream in the right format?
Cheers,
Jo
The socket transport shouldn't care, the content of a TCP packet is binary representing "whatever".
From the code you pointed to in the comments:
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
...
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
At the end of the socket send/receive (data == response). If that isn't occurring you need to figure how where the problem is. The first step is to write some very simple code like so:
string source = "your problem text string";
byte[] encode = Encoding.UTF8.GetBytes(source);
target = Encoding.UTF8.GetString(encode, 0, encode.Length);
Debug.Assert(source == target);
If that works, then output the 'encode' array can check to make sure that is contained in the packet data where it is being send, then verify that that is what is being received. If you are sending the right data but receiving it corrupted you have serious problems ... I doubt you find that but if so write a very simple test program that sends and receives on the same machine (localhost) to see if it is repeatable.
If I had to guess I would say that the characters being encoded are not Unicode or that Win phone doesn't properly support it (Proper unicode support).
As long as you don't know the protocol / the encoding the server expects you can only replay the known messages, like the bytes you provided in your question.
Therefore you just define directly the byte array like this:
byte[] payload = new byte[] {0x62, 0xff, 0x03, 0xff, 0xf0, 0x05, 0x74, 0x60};
and send it over the socket like you did with the encoded string before. The server should now accept the message like it was sent by the client you sniffed.

C# Network Stream still contains old data after read

New to TCP socket communications in C#, having a problem where I send some data across the network stream from my client app, then read it from my server app. Data comes through fine the first time, but the next time I try to send data, the old data is still in the network stream and just gets overridden up to whatever the length of the new data is. This is causing issues when trying to parse the data in the stream. I have tried adding a null termination but it doesn't seem to have any effect.
How can I empty the network stream before sending more data across?
We send a string such as this:
1|0|bob|cornell|9/14/2012 12:49:34 AM
Then another one like this:
1|0|jim|horne|9/14/2012 12:49:34 AM
But the second string goes through as :
1|0|jim|horne|9/14/2012 12:49:34 AMAM
...followed by a bunch of \0.
The last chunk is a DateTime, and it is failing to convert the string to a DateTime because of the extra AM. Even when appending \0 to the end of the string we are sending across the stream, it won't work. For example:
1|0|jim|horne|9/14/2012 12:49:34 AM\0M
It seems to treat the null termination as just another character, rather than a signal to stop reading the string.
It seems that "Flush" does nothing on NetworkStream types. Must be a dumb problem here, but any help is appreciated.
The client does this:
private static void writeToServer(MessagePacket message, NetworkStream clientStream)
{
clientStream.Write(message.ToBytes(), 0, message.ToBytes().Length);
}
The server does this:
byte[] rawMessage = new byte[4096];
while (true)
{
try
{
clientStream.Read(rawMessage, 0, 4096);
clientStream.Flush();
}
catch
{
break;
}
newPacket = new MessagePacket(rawMessage);
}
Stream.Read() returns the number of received bytes, the bytes after after that are undefined.
prefix your message with the number of bytes/chars to read ...
like: < integer>< delimeter char>< normal message>
on the receiver side, try finding the first match for the delimeter char ... parse the int in the part before that delimeter ... try reading the number of bytes/chars after the delimeter

StreamWriter.Flush(); won't flush

i am trying to link a Unity game to a Java server using C#
when the Java server up the only way i can send data is by closing the StreamWriter (OUT.Close();) which actually closes the connection too. so i can only send data onces. or, every time i want to send a message, i have to reconnect to the server again.
when i just use Flush(), the data will not be send to the server.
Code:
NetworkStream STREAM = connection.GetStream();
StreamWriter OUT = new StreamWriter(STREAM);
OUT.Write(text);
OUT.Flush()
this is my reading code:
BufferedReader input = new BufferedReader(new InputStreamReader(client.getInputStream()));
String inputstring = input.readLine();
You write text without a line separator with Write and read with ReadLine, so this doesn't match up. In the absence of line separators, ReadLine reads to the end of the stream, which explains why you need to close the stream. Replace Write with WriteLine.

Categories