How to read all requested data using NetworkStream.BeginRead? - c#

There is a code of async server. Client sends Header - size of Data Block + Data Block.
Server reads asynchronously first Header and then Data Block.
I need, after I read Data Block run the BeginRead for Header reading part, to make threads async.
PROBLEM:
When I got DataCallBack, in line:
int bytesRead = ns.EndRead(result);
I get not all buffer i asked to read in
mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);
If client send 1MB of Data I can get different number of "bytesRead".
QUESTION:
How to force "BeginRead" to read all data from connection. It should cause the new loop of Header - Data.
MyClient - simply wrapper over TcpClient;
CODE:
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
client.NoDelay = false;
// client.ReceiveBufferSize = 1024*1024;
listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), listener);
MyClient mc = new MyClient(client);
ContinueRead(0,mc);
}
public void ContinueRead(int size, MyClient mc)
{
if (size != 0)
{
mc.DataBuffer = new byte[size];
mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);
}
mc.Client.GetStream().BeginRead(mc.HeaderBuffer, 0, 4, new AsyncCallback(HeaderCallBack), mc);
}
private void HeaderCallBack(IAsyncResult result)
{
MyClient mc = (MyClient)result.AsyncState;
NetworkStream ns = mc.Stream;
int bytesRead = ns.EndRead(result);
if (bytesRead == 0)
throw new Exception();
mc.TotalLengs = BitConverter.ToInt32(mc.HeaderBuffer, 0);
ContinueRead(mc.TotalLengs, mc);
}
private void DataCallBack(IAsyncResult result)
{
MyClient mc = (MyClient)result.AsyncState;
NetworkStream ns = mc.Stream;
int bytesRead = ns.EndRead(result);
if (bytesRead == 0)
throw new Exception();
BAD CODE - MAKES ASYNC READING - SYNC
while (bytesRead < mc.TotalLengs)
{
bytesRead += ns.Read(mc.DataBuffer, bytesRead, mc.TotalLengs - bytesRead);
}
END BAD CODE
ContinueRead(0, mc);
ProcessPacket(mc.DataBuffer, mc.IP);
}

"If client send 1MB of Data I can get different number of "bytesRead"."
Yes...this is simply how TCP works under the hood. You can't change this. TCP guarantees the order of packets, not how they are grouped. The hardware and traffic conditions along the route the packets travel determine how that data is grouped (or un-grouped).
"How to force "BeginRead" to read all data from connection."
TCP has no idea how much data is being sent. As far as it is concerned, the connection is simply an endless stream of bytes; therefore it cannot read "all data" since there is no end to the data (from its perspective). TCP also has no notion of what a "complete message" is with respect to your application. It is up to you, the programmer, to develop a protocol that allows your application to know when all data has been sent.
If you are expecting a certain number of bytes, then keep a running sum of the values returned by EndRead() and stop when that magic number is hit.

Related

EndReceive in Async Socket without strings

I'm following this example about the creation of an async tcp listener in C#.
MSDN Example
I see that all data is encoded as string to check for message completeness. More precisely, every message sent is already a string, which we append the 'EOF' char to for string termination.
The server side part i'm talking about is in this snippet:
public static void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
Is there a way, as i usually do with TcpListener/TcpClient classes, to check if received bytes are available on the socket?
I mean something like this:
private void HandleClientConnection(TcpClient client)
{
NetworkStream clientStream = client.GetStream();
MemoryStream memoryStream = new MemoryStream();
while (true)
{
int read = clientStream.ReadByte();
if (read != -1)
{
memoryStream.WriteByte((byte)read);
}
else
{
break;
}
}
}
I'm aware that i probably misunderstood this example, or at least the Begin/End part and the "legacy" async pattern. But this is my goal, do you know some way to get it working without involving strings?
You said : "Is there a way to check if received bytes are available on the socket?"
In general 'EndReceive' will block the thread until data is available. So you don't need to do anything because 'EndReceive' is doing all the job for you.
'bytesRead' is an int that shows you how much data you have received.
a quote from docs.microsoft
The EndReceive method will block until data is available.1
But if you are using a SYNC socket (which you are not) then it's another topic.

When I send a command to server from client , the client receives the response only if the request is sent twice

I am trying to send commands to the server , like for example requesting the server to send back the list of files in it's directory. The problem is that when I send the "list" command to the server, I have to send it twice in order for the server to send back the list of files to the client. I am sure that the server receives the command in both times as on the server side I print the result that is supposed to be sent to the client on the console and it appears both times.
I am using C# and TCPListeners to listen for incoming responses or commands, and TCPClient to send responses or commands between the server and the client.
The client code
private TcpListener tcpListener = new TcpListener(9090);
private void button3_Click(object sender, EventArgs e)
{
Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes("list");
try
{
TcpClient clientSocket = new TcpClient(serverIPFinal, 8080);
if (clientSocket.Connected)
{
NetworkStream networkStream = clientSocket.GetStream();
networkStream.Write(bytesToSend, 0, bytesToSend.Length);
// networkStream.Close();
// clientSocket.Close();
thdListener = new Thread(new ThreadStart(listenerThreadList));
thdListener.Start();
}
}
catch
{
isConnectedLbl.Text = "Server not running";
}
}
//Listener Thread to receive list of files.
public void listenerThreadList()
{
tcpListener.Start();
while (true)
{
handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
lock (this)
{
if (handlerSocket != null)
{
nSockets.Add(handlerSocket);
}
}
ThreadStart thdstHandler = new
ThreadStart(handlerThreadList);
Thread thdHandler = new Thread(thdstHandler);
thdHandler.Start();
}
}
}
//Handler Thread to receive list of files.
public void handlerThreadList()
{
Socket handlerSocketList = (Socket)nSockets[nSockets.Count - 1];
NetworkStream networkStreams = new NetworkStream(handlerSocketList);
int requestRead = 0;
string dataReceived;
byte[] buffer = new byte[1024];
//int iRx = soc.Receive(buffer);
requestRead = networkStreams.Read(buffer, 0, 1024);
char[] chars = new char[requestRead];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(buffer, 0, requestRead, chars, 0);
dataReceived = new System.String(chars);
Console.WriteLine(dataReceived);
MessageBox.Show(dataReceived);
//tcpListener.Stop();
thdListener.Abort();
}
The Server code:
TcpListener tcpListener = new TcpListener(8080);
public void listenerThreadCommands()
{
tcpListener.Start();
while (true)
{
handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
connections.Items.Add(
handlerSocket.RemoteEndPoint.ToString() + " connected.");
// clientIP = handlerSocket.RemoteEndPoint.ToString();
lock (this)
{
nSockets.Add(handlerSocket);
}
ThreadStart thdstHandler = new
ThreadStart(handlerThreadCommands);
Thread thdHandler = new Thread(thdstHandler);
thdHandler.Start();
//tcpListener.Stop();
//handlerSocket.Close();
}
}
}
//Handler Thread to receive commands
public void handlerThreadCommands()
{
Socket handlerSocketCommands = (Socket)nSockets[nSockets.Count - 1];
NetworkStream networkStream = new NetworkStream(handlerSocketCommands);
int requestRead = 0;
string dataReceived;
byte[] buffer = new byte[1024];
requestRead = networkStream.Read(buffer, 0, 1024);
char[] chars = new char[requestRead];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(buffer, 0, requestRead, chars, 0);
dataReceived = new System.String(chars);
//connections.Items.Add(dataReceived);
if (dataReceived.Equals("list"))
{
localDate = DateTime.Now;
Files = Directory.GetFiles(System.IO.Directory.GetCurrentDirectory())
.Select(Path.GetFileName)
.ToArray();
String FilesString = "";
for (int i = 0; i < Files.Length; i++)
{
FilesString += Files[i] + "\n";
}
String clientIP = handlerSocketCommands.RemoteEndPoint.ToString();
int index = clientIP.IndexOf(":");
clientIP = clientIP.Substring(0, index);
WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list");
Console.WriteLine(clientIP);
Console.WriteLine(FilesString);
Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(FilesString);
try
{
WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list-response");
TcpClient clientSocket = new TcpClient(clientIP, 9090);
if (clientSocket.Connected)
{
NetworkStream networkStreamS = clientSocket.GetStream();
networkStreamS.Write(bytesToSend, 0, bytesToSend.Length);
networkStreamS.Close();
clientSocket.Close();
networkStream.Close();
//tcpListener.Stop();
// handlerSocketAuthenticate.Close();
}
}
catch
{
Console.WriteLine("Cant send");
}
}
else if (dataReceived.Equals("downloadfile"))
{
// handlerSocketAuthenticate.Close();
// tcpListener.Stop();
networkStream.Close();
thdListenerDownload = new Thread(new ThreadStart(listenerThreadDownloading));
thdListenerDownload.Start();
}
else
{
String clientIP1 = handlerSocketCommands.RemoteEndPoint.ToString();
int index = clientIP1.IndexOf(":");
clientIP1 = clientIP1.Substring(0, index);
// handlerSocketAuthenticate.Close();
CommandExecutor(dataReceived, clientIP1);
}
}
There are so many different things wrong with the code you posted, it's hard to know where to start, and it's impossible to have confidence that in the context of a Stack Overflow, one could sufficiently address all of the deficiencies. That said, in the interest of helping, it seems worth a try:
Sockets are bi-directional. There is no need for the client to use TcpListener at all. (By convention, the "server" is the endpoint that "listens" for new connections, and the "client" is the endpoint that initiates new connections, by connecting to a listening server.)You should just make a single connection from client to server, and then use that socket both for sending to and receiving from the server.
You are setting the CheckForIllegalCrossThreadCalls property to false. This is evil. The exceptions that occur are there to help you. Setting that property to false disables the exceptions, but does nothing to prevent the problems that the exceptions are designed to warn you about.You should use some mechanism to make sure that when you access UI objects, you do so only in the thread that owns those objects. The most primitive approach to this is to use Control.Invoke(). In modern C#, you are better off using async/await. With TcpClient, this is easy: you already are using GetStream() to get the NetworkStream object that represents the socket, so just use the asynchronous methods on that object, such as ReadAsync(), or if you wrap the stream in a StreamWriter and StreamReader, use the asynchronous methods on that object, such as ReadLineAsync().
You are checking the Connected property of the TcpClient object. This is pointless. When the Connect() method returns, you are connected. If you weren't, an exception would have been thrown.
You are not sufficiently synchronizing access to your nSockets object. In particular, you use its indexer in the handlerThreadList() method. This is safe when using the object concurrently only if you have guaranteed that no other thread is modifying the object, which is not the case in your code.
You are writing to the stream using ASCII encoding, but reading using UTF8 encoding. In practice, this is not really a problem, because ASCII includes only the code points 0-127, and those map exactly to the same character code points in UTF8. But it's really bad form. Pick one encoding, stick with it.
You are accepting using AcceptSocket(), but then just wrapping that in a NetworkStream anyway. Why not just use AcceptTcpClient() and call GetStream() on that? Both Socket and TcpClient are fine APIs, but it's a bit weird to mix and match in the same program, and will likely lead to some confusion later on, trying to keep straight which you're using where and why.
Your code assumes that the handlerThreadCommands() method will always be called in exactly the same order in which connections are accepted. That is, you retrieve the current socket with nSockets[nSockets.Count - 1]. But, due to the way Windows thread scheduling works, it is entirely possible that two or more connections could be accepted before any one of the threads meant to handle the connection is started, with the result that only the most recent connection is handled, and it is handled by those multiple threads.
You are assuming that command strings will be received as complete units. But this isn't how TCP works. TCP guarantees only that if you receive a byte, it will be in order relative to all the bytes sent before it. But you can receive any number of bytes. In particular, you can receive just a single byte, or you can receive multiple commands concatenated with each other, or you can receive half a command string, then the other half later, or the second half of one command and the first half of the next, etc. In practice, these problems don't show up in early testing because the server isn't operating under load, but later on they very well may be. And the code needs to be designed from the outset to work properly under these conditions; trying to patch bad code later is much more difficult.
I can't say that's the above are the only things wrong with the code, but they are most glaring, and in any case I think the above is sufficient food for thought for you at the moment.
Bottom line: you really should spend more time looking at good networking examples, and really getting to understand how they work and why they are written the way they do. You'll need to develop a good mental model for yourself of how the TCP protocol works, and make sure you are being very careful to follow the rules.
One resource I recommend highly is The Winsock Programmer's FAQ. It was written long ago, for a pre-.NET audience, but most of the information contained within is still very much relevant when using the higher-level networking APIs.
Alternatively, don't try to write low-level networking code yourself. There are a number of higher-level APIs that use various serialization techniques to encode whole objects and handle all of the lower-level network transport mechanics for you, allowing you to concentrate on the value-added features in your own program, instead of trying to reinvent the wheel.

C# Multithreading socket server receiving data

I have developed this small multi-threaded socket server, that creates one socket to not block the main program, and another thread for each new accepted connection.
The question comes when reading the data, I have a method called receiveData that reads all the data input, so right now the code only reads one time for each connection.
I solved this by adding inside the method receiveData a while loop with the flag socket.CanRead, what does this flag exactly do? What difference is there between CanRead and DataAvailable?
class Server
{
static void receiveData(TcpClient client, NetworkStream stream)
{
byte[] bytes = new byte[client.ReceiveBufferSize];
stream.Read (bytes, 0, (int) client.ReceiveBufferSize);
string returndata = Encoding.UTF8.GetString (bytes);
Console.WriteLine ("SERVER RECEIVED: " + returndata);
}
static void startServer()
{
new Thread(() =>
{
TcpListener listener = new TcpListener(IPAddress.Any, 5000);
listener.Start();
while (true)
{
if (listener.Pending()) {
new Thread(() =>
{
TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();
Server.receiveData(client, stream);
}).Start();
}
}
}).Start();
}
static void Main(string[] args)
{
Server.startServer ();
}
}
Refer to MSDN NetworkStream.CanRead Property,
CanRead:
Gets a value that indicates whether the NetworkStream supports reading.
while DataAvailable:
Gets a value that indicates whether data is available on the NetworkStream to be read.
It's like incoming data will be indicated as available only if the NetworkStream supports reading, thus can be read.
Take a look at sample code, I think it will answer all of your questions:
// Examples for CanRead, Read, and DataAvailable.
// Check to see if this NetworkStream is readable.
if(myNetworkStream.CanRead){
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while(myNetworkStream.DataAvailable);
// Print out the received message to the console.
Console.WriteLine("You received the following message : " +
myCompleteMessage);
}
else{
Console.WriteLine("Sorry. You cannot read from this NetworkStream.");
}
According to MSDN documentation NetworkStream.CanRead Can Read MSDN is used to determine if network stream is support reading. NetworkStream.DataAvailable Data Acalible MSDN shows you if there is data to read right now. So you should use NetworkStream.CanRead to determine if it is readable, and use NetworkStream.DataAvailable to determine if there is something to be read.

High performance asynchronous awaiting sockets

I am writing an app that will require to make hundreds of socket connections over tcp to read/write data.
I have come across this code snippet here and I'm wondering how I can make this more robust.
This is currently how I am calling the code:
foreach (var ip in listofIps)
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
client.Connect(remoteEP);
await ReadAsync(client);
}
Is there anything wrong with the above, and how can it be optimized such that it runs concurrently?
In the code snippet, the buffer size is set to 1000. Just as a simple illustration, if I were to attempt to print out only the bytes received, and not the remaining 0x00s, I have to do something like this:
while (true)
{
await s.ReceiveAsync(awaitable);
int bytesRead = args.BytesTransferred;
if (bytesRead <= 0) break;
var hex = new StringBuilder(bytesRead * 2);
var msg = new byte[bytesRead];
for (int i = 0; i < bytesRead; i++)
msg[i] = args.Buffer[i];
foreach (byte b in msg)
hex.AppendFormat("{0:x2} ", b);
AppendLog(string.Format("RX: {0}", hex));
}
Is there a more efficient way of doing this? Previously, I would iterate the whole buffer and print out the data, but that will give me a whole bunch of trailing 0x00s as my protocol is anywhere between 60 to 70 bytes long.
I am writing an app that will require to make hundreds of socket connections over tcp to read/write data.
You don't need "high-performance sockets" for that. The code is far simpler with regular-performance sockets.
For starters, don't use the custom awaitables from the link you posted. They are perfectly fine for some people (and completely "robust"), but you don't need them and your code will be simpler without them.
Is there anything wrong with the above, and can it be further optimized?
Yes. You shouldn't mix blocking (Connect) and asynchronous (ReadAsync) code. I would recommend something like this:
foreach (var ip in listofIps)
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ip), 4001);
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
await client.ConnectTaskAsync(remoteEP);
...
}
Where ConnectTaskAsync is a standard TAP-over-APM wrapper:
public static Task ConnectTaskAsync(this Socket socket, EndPoint endpoint)
{
return TaskFactory.FromAsync(socket.BeginConnect, socket.EndConnect, endpoint, null);
}
As Marc Gravell pointed out, this code (and your original code) is connecting the sockets one at a time. You could use Task.WhenAll to connect them all simultaneously.
2) Is there a more efficient way of doing this?
First, you should define a TAP-over-APM ReceiveTaskAsync wrapper similar to the above. When dealing with binary data, I also like to have an extension method on byte arrays for dumping:
public string DumpHex(this ArraySegment<byte> data)
{
return string.Join(" ", data.Select(b => b.ToString("X2")));
}
Then you can have code like this:
while (true)
{
int bytesRead = await socket.ReceiveTaskAsync(buffer);
if (bytesRead == 0) break;
var data = new ArraySegment<byte>(buffer, 0, bytesRead);
AppendLog("RX: " + data.HexDump());
...
}
If you do a lot of binary manipulation, you may find my ArraySegments library helpful.
3) Where and how should I include the logic to check if my whole data has arrived within a single read
Oh, it's more complex than that. :) Sockets are a stream abstraction, not a message abstraction. So if you want to define "messages" in your protocol, you need to include a length prefix or delimiter byte so you can detect the message boundaries. Then you need to write code that will parse out your messages, keeping in mind that blocks of data read from the socket may contain only a partial message (so you have to buffer it), a complete message, multiple complete messages, and may also end with a partial message (again, buffering). And you have to also consider your existing buffer when receiving the new block.
I have a TCP/IP .NET Sockets FAQ on my blog that addresses this specifically and has some example code using my personal default preference for message framing (4-byte little-endian length prefixing).
4) How should I include a writeasync method such that I can send data through the socket in the middle of reads.
That one's surprisingly tricky:
public static Task<int> SendTaskAsync(this Socket socket, byte[] buffer, int offset, int size, SocketFlags flags)
{
return Task<int>.Factory.FromAsync(socket.BeginSend, socket.EndSend, buffer, offset, size, flags, null);
}
public static Task WriteAsync(this Socket socket, byte[] buffer)
{
int bytesSent = 0;
while (bytesSent != buffer.Length)
{
bytesSent += await socket.SendTaskAsync(data, bytesSent, buffer.Length - bytesSent, SocketFlags.None);
}
}

TCP client\server - client doesn't always read

Client Code:
TcpClient client = new TcpClient();
NetworkStream ns;
private void Form1_Load(object sender, EventArgs e)
{
try
{
client.Connect("127.0.0.1", 560);
ns = client.GetStream();
byte[] buffer = ReadFully(ns, client.Available);
//working with the buffer...
}
catch
{
//displaying error...
}
}
public static byte[] ReadFully(NetworkStream stream , int initialLength)
{
// If we've been passed an unhelpful initial length, just
// use 32K.
if (initialLength < 1)
{
initialLength = 32768;
}
byte[] buffer = new byte[initialLength];
long read = 0;
int chunk;
while ((chunk = stream.Read(buffer, (int)read, buffer.Length - (int)read)) > 0)
{
read += chunk;
// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();
// End of stream? If so, we're done
if (nextByte == -1)
{
return buffer;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
Server Code:
private static TcpListener tcpListener;
private static Thread listenThread;
private static int clients;
static void Main(string[] args)
{
tcpListener = new TcpListener(IPAddress.Any, 560);
listenThread = new Thread(new ThreadStart(ListenForClients));
listenThread.Start();
}
private static void ListenForClients()
{
tcpListener.Start();
Console.WriteLine("Server started.");
while (true)
{
//blocks until a client has connected to the server
TcpClient client = tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private static void HandleClientComm(object client)
{
clients++;
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
Console.WriteLine("Client connected. ({0} connected)", clients.ToString());
#region sendingHandler
byte[] buffer = encoder.GetBytes(AddressBookServer.Properties.Settings.Default.contacts);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
#endregion
}
As you can see from the code, i'm trying to send AddressBookServer.Properties.Settings.Default.contacts (a string, not empty) to the connected client.
The problam is that sometimes(that's the wierd part) the client recieves the string and sometimes its keep being blocked on the ns.Read line waiting to recieve something.
I tryed debuging by putting a breakpoint on the line after ns.Read and i saw that when it doesn't work it never gets to that line, so it doesn't recieve the message that was sent by the server.
My question: How can I fix it?
My assumption: The server is sending the message before the client can recieve it therefor it's never get recieved by the client.
As Mark Gravell pointed out, this is a framing problem. Here is a simple client and server to show you how to frame your messages with a length prefix on the message. Keep in mind that this is just a sample to get you started. I would not consider it production ready code:
Client Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
namespace SimpleClient
{
internal class Client
{
private static void Main(string[] args)
{
try
{
TcpClient client = new TcpClient();
NetworkStream ns;
client.Connect("127.0.0.1", 560);
ns = client.GetStream();
byte[] buffer = ReadNBytes(ns, 4);
// read out the length field we know is there, because the server always sends it.
int msgLenth = BitConverter.ToInt32(buffer, 0);
buffer = ReadNBytes(ns, msgLenth);
//working with the buffer...
ASCIIEncoding encoder = new ASCIIEncoding();
string msg = encoder.GetString(buffer);
Console.WriteLine(msg);
client.Close();
}
catch
{
//displaying error...
}
}
public static byte[] ReadNBytes(NetworkStream stream, int n)
{
byte[] buffer = new byte[n];
int bytesRead = 0;
int chunk;
while (bytesRead < n)
{
chunk = stream.Read(buffer, (int) bytesRead, buffer.Length - (int) bytesRead);
if (chunk == 0)
{
// error out
throw new Exception("Unexpected disconnect");
}
bytesRead += chunk;
}
return buffer;
}
}
}
Server Code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SimpleServer
{
class Server
{
private static TcpListener tcpListener;
private static int clients;
static void Main(string[] args)
{
tcpListener = new TcpListener(IPAddress.Any, 560);
tcpListener.Start();
Console.WriteLine("Server started.");
while (true)
{
//blocks until a client has connected to the server
TcpClient client = tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private static void HandleClientComm(object client)
{
int clientCount = Interlocked.Increment(ref clients);
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
Console.WriteLine("Client connected. ({0} connected)", clientCount);
#region sendingHandler
byte[] buffer = encoder.GetBytes("Some Contacts as a string!");
byte[] lengthBuffer = BitConverter.GetBytes(buffer.Length);
clientStream.Write(lengthBuffer, 0, lengthBuffer.Length);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
tcpClient.Close();
#endregion
}
}
}
The reason your code sometimes worked, and sometimes failed is that client.Available can return 0. When it did you were setting the bytes to read to 32k, so the read call was waiting for those bytes to come in. They never did, and since the server never closed the socket, read would not error out either.
Hope this gets you moving in the right direction.
Edit:
I forgot to mention endianess in my original post. You can see the documentation here about endianess and using BitConverter: http://msdn.microsoft.com/en-us/library/system.bitconverter(v=vs.100).aspx
Basically you need to make sure both server and client are running on architectures with the same endianess, or handle the conversion from one endianess to another as needed.
Edit 2 (to answer question in the comments):
1) Why can client.available return 0?
This is a timing issue. The client is connecting to the server, then immediately asking which bytes are available. Depending on what other processes are running, time slices for the available processor etc, the client may be asking what is available before the server has had a chance to send anything at all. In this case it will return 0.
2) Why did I use Interlocked to increment the clients?
The code as you had originally written it was incrementing the clients in the newly created thread running HandleClientComm(...). If two or more clients connected simultaneously it is possible a race condition could occur as multiple threads were trying to increment clients. The end result would be clients would be less than it should be.
3) Why did I change ReadFully method?
You version of ReadFully, which I changed to ReadNBytes, was close to be correct, but had a few flaws:
Setting initialLenth to 32768 if the original initialLength was zero or less. You should never guess how many bytes you need to read from a socket. As Mark Gravell mentioned you need to frame your messages with either a length prefix or some sort of delimiter.
NetworkStream.Read blocks until some bytes are read or returns a 0 if the socket is closed out from underneath it. There was no need to call stream.ReadByte, since chunk would already be 0 if the socket was disconnected. Making that change simplified the method, especially since we know exactly how many bytes we need to read based on our simple header.
Now that we know how much we are going to read, we can allocate exactly what we need up front. This removes the necessity of re-sizing the buffer upon return. We can just return what we allocated.
4) How do I know the length buffer size is 4?
The length I serialized was 32 bits. You can see that in the documentation for BitConverter.GetBytes(int value). We know a byte is 8 bits, so simply divide 32 by 8 giving us 4.
5) Why is this not production ready/How can I improve the code?
Basically there is no real error handling. NetworkStream.Read() can throw several exceptions, none of which I am handling. Adding the proper error handling would go a long way to making it production ready.
I have not really tested the code beyond a cursory run. It would need to be tested under a variety of conditions.
There is no provision for the client to reconnect or retry, though you may not need this for your purposes.
This was written as a simple example, and may not actually meet the requirements you are trying to fulfill. Not knowing those requirements I cannot claim this is ready for your production environment (whatever that is).
Conceptually ReadNBytes is fine, but if someone sends you malicious message which claim the length of the message is 2 gigabytes or something, you are going to try and allocate 2 gigabytes blindly. Most byte level protocols (the description of what goes over the wire) specify the maximum size of messages. That would need to be checked, and if the messages can actually be large you would need to handle it differently than just allocating a buffer, maybe writing to a file or other output stream as you read it in. Again, not knowing your full requirements, I cannot be sure what is needed there.
Hope this helps.

Categories