So I'm writing an application in where I need to steam data from a client to my server and I've noticed some very weird behavior with how the NetworkStream.Read function behaves. I'm sending packets of about 18 bytes in length and if I make the while loop in the server Read and then Write then I get all of the packets properly. However, if I only Read and never Write then the Read does not return after a single packet. Instead, it gathers several packets before returning, saying that it received some 300+ bytes at each iteration. Does anybody know why I'm getting this behavior? I could simply write back 'a' every time but this seems a bit ghetto.
Here is the section of code:
private static void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
int count = 0;
while (true)
{
bytesRead = 0;
try
{
// Block until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
// A socket error has occured
break;
}
if (bytesRead == 0)
{
// The client has disconnected from the server
break;
}
Debug.print("Number of bytes: {0:D}", bytesRead);
count++;
// Message has successfully been received. Echo back.
clientStream.Write(new byte[2], 0, 1);
}
Debug.print("Count: {0:F}", count);
tcpClient.Close();
}
Related
I wrote this method using UDP socket with ReceiveTimeout = 1000. And sometimes it loses second packet:
private static byte[] ReceivePlainData(Socket socket) {
var recievedData = new List<byte>();
var buffer = new byte[1024];
do {
int recievedAmount;
while (true) {
try {
recievedAmount = socket.Receive(buffer);
}
catch (SocketException) {
continue;
}
break;
}
recievedData.AddRange(buffer.Take(recievedAmount));
} while (socket.Available > 0);
return recievedData.ToArray();
}
Socket initialization:
using (var socket = new Socket(SocketType.Dgram, ProtocolType.Udp) {
ReceiveTimeout = 1000
})
What do i need to change to make this method work correctly?
Socket.Available only tells you if data has been received and available for immediate reading. It doesn't tell you if the stream is complete or not. The sender may still be in the process of generating or transmitting more data. If you intend to consume the entire response and are relying on the socket to be closed by the sender when there is no more data, continue reading from the socket until the number of bytes returned is 0 (indicating the socket was closed normally) or an exception occurs (indicating abnormal termination).
MemoryStream stream = new MemoryStream();
int received;
var buffer = new byte[8096];
do {
received = socket.Receive(buffer);
stream.Write(buffer, 0, received);
} while (received > 0);
// Use the stream
// byte[] bytes = stream.ToArray();
Set your socket's ReceiveTimeout to the maximum amount of time you're willing to wait between bytes before aborting.
I'm working with an Access Control Device, that allows or denies access to rooms, verifying rights through biometric data. I need to listen indefinitely for data on a connected TcpClient(Socket). But how to do this without the following approach:
byte[] bb = new byte[1024]
while(true)
{
if (tcpClient.Client.Available > 0)
{
tcpClient.Client.Receive(bb, bb.Length, SocketFlags.None);
int k = tcpClient.Client.Receive(bb);
string result = Encoding.Default.GetString(bb.Take(k).ToArray());
// do sth here, rise an event, etc...
}
Thread.Sleep(500);
}
I'm following this example, did some adaptation (see below), but only the first chunk of data is read (if the device sends something again, the sent data are not read). I think that the EndReceive method is closing the socket or else, but I want to read the socket continuously, to raise an event every time socket reads data.
...
TcpClient tcpClient // Connected with BeginConnect
byte[] BufferData = new byte[1024]
public IAsyncResult StartReceivingData()
{
//some code here
return tcpClient.Client.BeginReceive(DataBuffer, 0,
DataBuffer.Length, SocketFlags.None,
new AsyncCallback(ReceivedData), tcpClient);
}
public void ReceivedData(IAsyncResult callerResult)
{
TcpClient remote = (TcpClient)callerResult.AsyncState;
int recv = remote.Client.EndReceive(callerResult);
string stringData = Encoding.ASCII.GetString(BufferDados, 0, recv);
//rise an event here containing stringData
}
EDIT
I'm working with .NET 3.5 and I can't upgrade to .NET 4/4.5, because this is part of a legacy system.
TcpClient has a very useful method GetStream. I would use it.
var buf = new byte[0x10000];
var stream = tcpClient.GetStream();
int len = await stream.ReadAsync(buf, 0, buf.Length);
while (len > 0)
{
//your work ....
len = await stream.ReadAsync(buf, 0, buf.Length);
}
If you are getting strings separated by newline chars, then you can also use this
var stream = new StreamReader(tcpClient.GetStream());
string line = await stream.ReadLineAsync();
while (line!=null)
{
//...
line = await stream.ReadLineAsync();
}
EDIT
but only the first chunk of data is read It is because BeginReceive doesn't mean you'll get a callback for every data you receive. You should call it everytime you receive your data (for ex, in ReceivedData method)
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
In my client/server application my client wiil communicate with the server for 2 functions: the client will either request data from the server or it will send data so the server will save it. I'm using one socket for both methods, and the method to be used is defined by the first byte sent. If the first byte is "1" it is requesting data. If it is "2", it will send data (data bytes are sent after the "2" byte). It works perfectly for sending data. But when I'm requesting data it works, as long as I don't read the socket stream in the client. It's like if I make the client read data after sending data, the server will have no data to read, and it just crashes when trying to read the data.
Here is my server code:
private const int BufferSize = 1024;
NetworkStream netstream = null;
byte[] RecData = new byte[BufferSize];
int RecBytes;
try {
netstream = clientSocket.GetStream();
int totalrecbytes = 0;
using (MemoryStream ms = new MemoryStream()) {
//When I get here, there is no data to read
while ((RecBytes = netstream.Read(RecData, 0, RecData.Length)) > 0) {
ms.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
byte[] bytes = ms.ToArray();
byte b = bytes[0];
switch (b) {
case 1:
//Here I gather data and put it in "stream" variable
byte[] SendingBuffer = null;
int NoOfPackets = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(stream.Length) / Convert.ToDouble(BufferSize)));
int TotalLength = (int)stream.Length, CurrentPacketLength, counter = 0;
for (int i = 0; i < NoOfPackets; i++) {
if (TotalLength > BufferSize) {
CurrentPacketLength = BufferSize;
TotalLength = TotalLength - CurrentPacketLength;
}
else
CurrentPacketLength = TotalLength;
SendingBuffer = new byte[CurrentPacketLength];
stream.Read(SendingBuffer, 0, CurrentPacketLength);
netstream.Write(SendingBuffer, 0, (int)SendingBuffer.Length);
}
netstream.Flush();
}
catch (Exception e) {
Console.WriteLine("EXCEPTION:\n" + e.ToString());
}
break;
case 2:
//Code to read data
break;
}
}
netstream.Close()
clientSocket.Close();
And here is my client code:
using (System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient()) {
string returnData = "";
IAsyncResult ar = clientSocket.BeginConnect("127.0.0.1", 8080, null, null);
System.Threading.WaitHandle wh = ar.AsyncWaitHandle;
try {
if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5), false)) {
clientSocket.Close();
Console.WriteLine("Timeout");
return;
}
System.Net.Sockets.NetworkStream serverStream = clientSocket.GetStream();
byte b = 1;
byte[] outStream = { b };
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
//If I comment following lines, the server can read sent data, but server can't otherwise
byte[] RecData = new byte[1024];
int RecBytes;
int totalrecbytes = 0;
MemoryStream MS = new MemoryStream();
while ((RecBytes = serverStream.Read(RecData, 0, RecData.Length)) > 0) {
MS.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
serverStream.Close();
clientSocket.Close();
clientSocket.EndConnect(ar);
}
catch (Exception ex) {
Console.WriteLine("Exceção: " + ex.ToString());
}
finally {
wh.Close();
}
}
So, how can I send data to server and read the response? (I tried even putting the thread to sleep after sending data, with no luck.)
Thanks in advance.
EDIT:
With some debug messages I discovered that the server do read the "1" byte that was sent, but somehow it gets stuck inside the while loop, like, the server just stops there, no more loops and it does not leave the while loop. I saw that after writing "loop" in console inside the while loop, and writing read bytes also in console. It wrote "loop" once, and the read byte.
This code worries me:
//When I get here, there is no data to read
while ((RecBytes = netstream.Read(RecData, 0, RecData.Length)) > 0) {
ms.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
You are reading until the client closes the connection (or shuts down sending, which you don't do). But the client only closes when the server has replied. The server reply will never come. It is a deadlock.
Solution: Read a single byte to determine the requests command (b).
Unrelated to the question, your "packetised" sending (NoOfPackets, ...) does not seem to serve any purpose. Just use Stream.Copy to write. TCP does not have packets.
An even better solution would be to abandon your custom TCP protocol and use an HTTP library. All these concerns just go away. There are various smaller problems with your code that are very typical to see in TCP code.
This is the the function for receiving the values from the client. But the problem is I'm receiving it only once. No matter how many time I send some data, the label on the MainWindows is being changed only once.
What am I doing wrong here?
private void HandleClientComm(object client)
{
tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
if (String.IsNullOrWhiteSpace(data))
{
ASCIIEncoding encoder = new ASCIIEncoding();
data = encoder.GetString(message, 0, bytesRead);
MainWindow.Change(data);
tcpClient.Close();
}
tcpClient.Close();
}
On the client side I have this following example:
try
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("127.0.0.1", 8001);
Console.WriteLine("Connected");
while (true)
{
Console.Write("Enter the string to be transmitted : ");
String str = Console.ReadLine();
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
Console.WriteLine("Transmitting.....");
stm.Write(ba, 0, ba.Length);
stm.Flush();
Console.WriteLine("Sent.....");
}
tcpclnt.Close();
Console.Read();
}
catch (Exception e)
{
Console.WriteLine("Error..... " + e.StackTrace);
}
Just a fast example, so when I enter the string name, the value changes the first time but when I enter a second time or if i exit the client program and reenter nothing changes, the value of the label content is equal to the first sent value.
You must use asynchronous server socket... see this sample from MSDN
Link
It looks like after the first successful read you are calling:
tcpClient.Close();
This will prevent any future reads from being successful.
If you would like avoid these sort of socket problems have you considered using the network library instead? I'm a developer for the library networkComms.net and you can find a short example or what I mean here.
If you use clientStream.Read() after tcpClient.Close(), it will throw an exception, which will run your catch block and break you out of the loop. (And you are calling tcpClient.Close() in two places within the loop so just getting rid of just one will not help. Both of those are wrong.)
tcpClient.Close() should not be called unless you're completely done with the connection (moreover it should only be called once.)
Also,
"You must close the NetworkStream when you are through sending and receiving data. Closing TcpClient does not release the NetworkStream."
So make sure you close the TcpClient and the NetworkStream, but only when you're completely finished processing in the loop.