Messages aren't properly displayed on the client side - c#

I have written a simple server and client code in C#. As soon as the client is connected, the server will send a welcome message, a file size and a string to the client one by one and the client will display it. The client will convert the string into a string array and display it. After that the client will send an id to server and the server will display it. But the problem is, the client is not properly displaying. When I run the client program after running the server, it's displaying the following thing whereas it's supposed to display each message in a single line.
welcome1.cpp,.jpg,.png
Moreover, on the client side, The line which I have written to display the converted string array, is not working at all neither the lines after this are executing. Seems like,the code hangs. I have marked it in my code. My sample code is given below:
Server:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace server
{
class Program
{
static void Main(string[] args)
{
// Listen on port 1234.
try
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
byte[] data = new byte[1024];
// Infinite loop to connect to new clients.
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream ns = tcpClient.GetStream();
//sending welcome message
string welcome = "welcome";
ns.Write(Encoding.ASCII.GetBytes(welcome), 0, welcome.Length);
ns.Flush();
//sending file size
string fsize = "1";
ns.Write(Encoding.ASCII.GetBytes(fsize), 0, fsize.Length);
ns.Flush();
//sending extensions
string[] extensions = { ".cpp", ".jpg", ".png" };
string str = string.Join(",", extensions);
Console.WriteLine(str);
ns.Write(Encoding.ASCII.GetBytes(str), 0, str.Length);
ns.Flush();
//receiving id
int recv = ns.Read(data, 0, data.Length);
string id = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(id);
}
}
catch (Exception e)
{
Console.Write(e.Message);
}
Console.Read();
}
}
}
Client:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace client
{
class Program
{
static void Main(string[] args)
{
try
{
TcpClient tcpClient = new TcpClient("127.0.0.1", 1234);
NetworkStream ns = tcpClient.GetStream();
byte[] data = new byte[1024];
StreamWriter sWriter = new StreamWriter(tcpClient.GetStream());
//receiving welcome message
int recv = ns.Read(data, 0, data.Length);
string message = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(message);
//receive filesize
int recv2 = ns.Read(data, 0, data.Length);
string message2 = Encoding.ASCII.GetString(data, 0, recv2);
Console.WriteLine(message2);
//receiving extensions
int recv1 = ns.Read(data, 0, data.Length);
string message1 = Encoding.ASCII.GetString(data, 0, recv1);
Console.WriteLine(message1);
var array2 = message1.Split(',');
foreach (string s in array2) //from this line the program isn't working
{
Console.WriteLine(s);
}
string input = Console.ReadLine();
ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length);
ns.Flush();
}
catch (Exception e)
{
Console.Write(e.Message);
}
Console.Read();
}
}
}
What's wrong in the code?

After looking at your post again, I am sure that my comment is the actual answer:
The code already hangs at int recv2 because all data will be received by the 1st ns.read and the 2nd call in the mentioned line is blocking as no more data is present.
You need to add a 'high level protocol' that lets you identify the data of each packet.
Sample:
000007welcome
6 bytes (4 could be enough though) at the Start of each message specify the length of the user data. So you can easily separate your data which will look like
000007welcome0000011000014.cpp,.jpg,.png
You have to create the functions for creating/adding and separating/interpreting your 6 bytes header (which actually is just a length info, but could be enhanced to contain multiple info) by yourself of course, but that's quite easy.
In general you should always consider timing issues on EVERY operation that is not just adding 2 integers: file access, data transfer over network or other media, ... really any type of hardware access. Therefore sending data over network means that the packets could
arrive in correct order, as desired, with some delay so that you could read them separately. This is quite unlikely though.
arrive in correct order, but at the same time or with major delay (which is a couple of 100ms, so not really large for a human, but indeed in terms of networking). This is very common, see your case.
arrive partially. Packet fragmentation usually occurs only on large packets (greater than MTU), but you should have a routine that stores and concats incoming data, while another routine checks the stored data for complete messages, removes complete messages from the stored data and processes them. For this, use a loop (in a separate thread) and call ns.read when ns.DataAvailable is true.
arrive in different order. This may happen on long transmission paths (internet; rarely on LAN). You would have to enhance your protocol by an incrementing sequence number.
get lost! On UDP, the packets will NOT be resent. On TCP they will, but actually I cannot tell what happens on the sender side if the resend fails, too...
This means, depending on the importance of your data you may need to take safety measures for these cases.
And finally you might want to handle (unexpected) connection losses...
I also suggest you take a look on my answers on other networking questions for advices on debugging, testing, common issues, ...

Analysis
The code:
// Receive the welcome message.
int recv = ns.Read(data, 0, data.Length);
string message = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(message);
// Receive the file size.
int recv2 = ns.Read(data, 0, data.Length);
string message2 = Encoding.ASCII.GetString(data, 0, recv2);
Console.WriteLine(message2);
// Receive the extensions.
int recv1 = ns.Read(data, 0, data.Length);
string message1 = Encoding.ASCII.GetString(data, 0, recv1);
Console.WriteLine(message1);
does not work properly because the Stream.Read(Byte[], Int32, Int32) method has the following return value:
Return Value
Type: System.Int32
The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
— Stream.Read Method (Byte[], Int32, Int32), MSDN.
The Stream class does not guarantee the "data correspondence" between the Stream.Write() and the Stream.Read() method calls. For example, if the Stream.Write() method call writes 6 bytes, the first Stream.Read() method call could return 1 byte (first byte).
Conceptual solution
Because of "streaming" it is necessary to define the logical messages to "extract" ("detect") them from the stream.
It is required to "join" the messages using the "separator" when sending and "split" the messages using the "separator" when receiving. One of the following alternatives could be considered to implement the "separator" concept:
Introduce the "end-of-the-message" marker.
Introduce the message header which contains the length of the message.
The small article, TCP/IP client-server application: exchange with string messages, may be useful to understand the mentioned alternatives.

Related

C# Client/Server returning answer from server to client

I've created a simple client/server program which takes an input from the client and returns a answer to the input by looking in the text file to see if there is an answer associated with the input.
The issue I'm having is that I get the response on the server side but I don't know how to send it back to the client (it just returns the input on the client side).
The second issue is that it will execute once, as in, it will only take in one input. I tried adding a loop but couldn't get it to work.
Server.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace server
{
class server
{
const string SERVER_IP = "127.0.0.1";
static void Main(string[] args)
{
//Create Dictionary
Dictionary<string, string> dict = new Dictionary<string, string>();
//---Read Text File containing commands ---
StreamReader sr = new StreamReader(#"C:\Users\Desktop\potato.txt");
string line;
//Splits the text into commands:responses
while ((line = sr.ReadLine()) != null)
{
string[] arr = line.Split(';');
dict.Add(arr[0], arr[1]);
}
//Print dictionary TESTING FUNCTION
foreach (KeyValuePair<string, string> kvp in dict)
{
Console.WriteLine("Command = {0} Response = {1}", kvp.Key, kvp.Value);
}
//---Input the port number for clients to conect---
Console.Write("Input port" + System.Environment.NewLine);
int PORT_NO = int.Parse(Console.ReadLine());
//---listen at the specified IP and port no.---
IPAddress localAdd = IPAddress.Parse(SERVER_IP);
TcpListener listener = new TcpListener(localAdd, PORT_NO);
Console.WriteLine("Listening for Commands");
listener.Start();
//---incoming client connected---
TcpClient client = listener.AcceptTcpClient();
//---get the incoming data through a network stream---
NetworkStream nwStream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
//---read incoming stream---
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
//---convert the command data received into a string---
string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received Command : " + dataReceived);
//---Search Command and send a response
string Response;
if (dict.TryGetValue(dataReceived, out Response))
{
Console.WriteLine(Response);
}
//---write back the response to the client---
Console.WriteLine("Sending Response : " + Response);
nwStream.Write(buffer, 0, bytesRead);
Console.ReadLine();
}
}
}
You need to convert Response to a byte[] just as you do in the client sending your request (i.e. bytesToSend). E.g.:
Console.WriteLine("Sending Response : " + Response);
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(Response);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
Console.ReadLine();
That said, you have made the classic mistake every person does who tries to write TCP code without first reading references about TCP and sockets: you mistakenly believe that when you read from a socket, you will always receive in that single operation every byte that was read. So even with the above fix (which does address the issue on the server side), it is possible you will see partial responses on the client side (not as likely when testing locally, but much more likely if you move to running on the Internet or across LANs, and especially as the message size increases).
For low-volume network interaction, you may want to consider wrapping your NetworkStream with StreamReader and StreamWriter objects, and use ReadLine() and WriteLine() to receive and send data (i.e. use line-breaks as the delimiter for the data).
As for dealing with multiple requests, given the code you have presented here, the simplest approach is to add a loop around the server code after the listener.Start() method. I.e. containing all the code after that statement, starting with the call to listener.AcceptTcpClient() and going to the last statement in the method. However, again this is only appropriate for low-volume network code. If you anticipate clients will need your server to handle multiple requests and especially if in quick succession, what you really want is for the client to maintain the connection, i.e. only connect once and then have it send multiple requests on that same connection.
Similarly, if you want to be able to handle multiple clients at once, you cannot run the server in a single thread. Instead, at the very least you'll need to use the thread-blocking logic you're using now, where you have a new thread created for each client. Better, would be to use the non-blocking API (e.g. NetworkStream.BeginRead(), StreamReader.ReadLineAsync(), etc. … there are many asynchronous options to choose from), and let .NET deal with the threading for you.
Doing it that way will require significant changes to the server code. You really should look carefully at various samples on MSDN and Stack Overflow to see how this sort of thing is done. I also strongly recommend you read through the Winsock Programmer's FAQ. It is not specifically about .NET at all, but does cover all of the key details you'll need to know in order to effectively and correctly use the .NET API.

How to get the Socket Buffer size in C#

I have a Socket code which is communicating through TCP/IP.The machine to which i am communicating has buffer data in its buffer.At present i am trying to get the buffer data using this code.
byte data = new byte[1024];
int recv = sock.Receive(data);
stringData = Encoding.ASCII.GetString(data, 0, recv);
But this code retrieves only 11 lines of data whereas more data is there in the machines buffer.Is this because i have used int recv = sock.Receive(data); and data is 1024 ?
If yes ,How to get the total buffer size and retrieve it into string.
If you think you are missing some data, then you need to check recv and almost certainly: loop. Fortunately, ASCII is always single byte - in most other encodings you would also have to worry about receiving partial characters.
A common approach is basically:
int recv;
while((recv = sock.Receive(data)) > 0)
{
// process recv-many bytes
// ... stringData = Encoding.ASCII.GetString(data, 0, recv);
}
Keep in mind that there is no guarantee that stringData will be any particular entire unit of work; what you send is not always what you receive, and that could be a single character, 14 lines, or the second half of one word and the first half of another. You generally need to maintain your own back-buffer of received data until you have a complete logical frame to process.
Note, however, Receive always tries to return something (at least one byte), unless the inbound stream has closed - and will block to do so. If this is a problem, you may need to check the available buffer (sock.Available) to decide whether to do synchronous versus asynchronous receive (i.e. read synchronously while data is available, otherwise request an asynchronous read).
Try something along these lines:
StringBuilder sbContent=new StringBuilder();
byte data = new byte[1024];
int numBytes;
while ((numBytes = sock.Receive(data))>0)
{
sbContent.Append(Encoding.UTF8.GetString(data));
}
// use sbContent.ToString()
Socket tcpSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine(" ReceiveBufferSize {0}", tcpSocket.ReceiveBufferSize);
For actual data you can put below condition:-
int receiveBytes;
while((receiveBytes = tcpSocket.Receive.Data(receiveBytes)) > 0)
{
}

TcpClient missing data

I am trying to write network part for my game in C# using System.Net.Sockets and TcpClient class.
Each update server is sending information to client.
All information is built into 2kb packets, so in 1 update 1-2-3-5-10 packets can be sent.
Client is checking information and if information has right format - then reading it.
Everything is working fine, until server starts trying to send too many packets.
When it happens client time to time is receiving packets with wrong data 1 of 20-50 packets usually.
For example, 1-2 packets for 1 update usually are received fine, 3-10 packets for update giving wrong data streams.
If I am starting several clients in 1 time, that should get same data streams from server - they get different numbers of success and fail data streams.
What am I doing wrong, and how can I evade this wrong data streams?
Am I just sending too much data in 1 ms and it is needed to send it over time?
This is the sending information:
TcpClient client;
public void SendData(byte[] b)
{
//Try to send the data. If an exception is thrown, disconnect the client
try
{
lock (client.GetStream())
{
client.GetStream().BeginWrite(b, 0, b.Length, null, null);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This is the receiving information:
byte[] readBuffer;
int byfferSize = 2048;
private void StartListening()
{
client.GetStream().BeginRead(readBuffer, 0, bufferSize, StreamReceived, null);
}
private void StreamReceived(IAsyncResult ar)
{
int bytesRead = 0;
try
{
lock (client.GetStream())
{
bytesRead = client.GetStream().EndRead(ar); // просмотр длины сообщения
}
}
catch (Exception ex)
{ MessageBox.Show(ex.Message); }
//An error happened that created bad data
if (bytesRead == 0)
{
Disconnect();
return;
}
//Create the byte array with the number of bytes read
byte[] data = new byte[bytesRead];
//Populate the array
for (int i = 0; i < bytesRead; i++)
data[i] = readBuffer[i];
//Listen for new data
StartListening();
//Call all delegates
if (DataReceived != null)
DataReceived(this, data);
}
It is main network code.
I don't know what you do with the data after you've received it, but it's quite possible that you're not reading all of the data from the connection. You have:
bytesRead = client.GetStream().EndRead(ar);
There's no guarantee that the number of bytes you've read are all of the bytes that the server sent. For example, the server could have sent 2,048 bytes, but when you called Read, there were only 1,024 bytes available. The rest of them are still "in transit." As the documentation for NetworkStream.Read says:
The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter
You could be getting partial packets. If your DataReceived handlers assume that the data buffer contains a complete packet, then you're going to have problems.
To reliably read from a network stream, you need to know how much data you're supposed to read, or you need a record separator. Something has to make sure that if you're expecting a complete packet that you get a complete packet before you try to process it. Your code just checks to see if bytesRead is not 0. If it's anything else, you just pass it on. This is going to be a problem unless your DataReceived handlers know how to buffer partial packets.
On another note, you really don't need to lock the network stream. Unless you can have several threads reading from the same stream. And that would be disastrous. Ditch the lock. You don't need it.

C# TCP Client Cant get more than 1 message in row

I have build a app that can connect to my server. Everything is running smoothly, but I have a problem when the server send message to client simultaneously. Such as when the server sends 2 messages in row. The client just receives the first one. Is there possible to get more than one message in row?
Here is my part of code for client:
TcpClient clientSocket;
public string IPS= "###.###.###.###";
public int SocketS = ####;
public void ConnectingToServer()
{
clientSocket= new TcpClient();
clientSocket.Connect(IPS, SocketS);
if (clientSocket.Connected)
{
serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes();
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
}
}
// Function for send data to server.
public void SendDataToServer(string StrSend)
{
if (clientSocket.Connected)
{
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(StrSend);
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
}
}
// Function for receive data from server (I put this in looping).
public void getMessage()
{
if (clientSocket != null)
{
if (clientSocket.Connected)
{
if (serverStream.DataAvailable)
{
int buffSize = 0;
buffSize = clientSocket.ReceiveBufferSize;
byte[] inStream = new byte[buffSize];
serverStream.Read(inStream, 0, buffSize);
string StrReceive= System.Text.Encoding.ASCII.GetString(inStream);
}
}
}
}
The send/receive functions of socket do not guarantee that all the data you provided will be sent/received at one call. The functions return actual number of sent/received bytes. In your case, you must not ignore the result of the serverStream.Read(...) method call.
That is why application-level protocol should be designed to exchange the things (you call it "messages").
There are many approaches to design the protocol, but let's consider the following "message" protocol as an example:
----------------------------------------------------
| Number of string bytes | String bytes in UTF-8 |
----------------------------------------------------
| 1 byte | 2 - ... bytes |
----------------------------------------------------
Sending the "message": the string should be converted to UTF-8 (for example) representation and sent it with the byte length of the byte representation (as described above).
Receiving the message: receive the data to memory buffer. The process of extracting the "message" is opposite to the sending ones. Of course, you can receive more than one "message" at once, so process the buffer thoroughly.
Example
I have just written a small article with code example.

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