I have a function to request an update from the server. I provide the query and also indicate the expected length of the response.
public Byte[] GetUpdate(Byte[] query, int expLength)
{
var response = new Byte[expLength];
lock(Client)
{
Stream s = Client.GetStream();
s.Write(query, 0, query.Length);
var totalBytesRead = 0;
var numAttempts = 0;
while(totalBytesRead < expLength && numAttempts < MAX_RETRIES)
{
numAttempts++;
int bytes;
try
{
bytes = s.Read(response, totalBytesRead, expLength);
}
catch (Exception ex)
{
throw new IOException();
}
totalBytesRead += bytes;
}
if(totalBytesRead < expLength)
{
// should probably throw something here
throw new IOException();
}
}
return response;
}
The function that calls the above function is given below. Both of them belong to class Connection.
public Byte[] GetData(string ip, int port ,Byte [] query, int responseLen)
{
Connection connection = GetConnection(ip,port);
Byte[] data = null;
try
{
data = connection.GetUpdate(query, responseLen);
}
catch(Exception e)
{
connection?.Disconnect();
return new Byte[0];
}
return data;
}
My question is as follows.
With the above code I am trying to read some values of a remote network endpoint. Sometimes the connections go down on the link. I test the scenario by manually unplugging the Ethernet cable. Once I unplug and plug the cable I sometimes find that s.Read() in function GetUpdate throws StreamReadFailed exception.It happens occasionally. What could be the possible reason for this and what is the best way to recover from this.?
Related
I have a serious issue with my .NET server application. In production, the application will reach 100% CPU usage and get stuck there until I restart the server. It seems completely random. Sometimes it will happen 10 minutes after I start the server. Sometimes a week after I start the server. There are no logs that indicate what causes it either. But I am guessing I wrote the TCP client/server wrong and there is some edge case that can cause this. I believe this issue didn't start happening until I added this TCP client/server. I say client and server because this class does both and I actually have two different server applications that use it to communicate to each other and they both experience this issue randomly (not at the same time). Also side note user clients from all over the world use this same TCP client/server class: Bridge to connect to the server as well.
Is there anything I can do to try and figure out what's causing this? It's a .NET console app running on a Linux VM on Google Cloud Platform.
If you are knowledgable with .NET TCP classes then perhaps you can find an issue with this code?
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SBCommon
{
public class Bridge<T> where T : struct, IConvertible
{
public Dictionary<int, BridgeHandler> bridgeHandlers;
public ClientHandler clientHandler;
TcpListener server = null;
public Bridge(int port, Dictionary<int, BridgeHandler> bridgeHandlers)
{
server = new TcpListener(IPAddress.Any, port);
server.Start();
this.bridgeHandlers = bridgeHandlers;
Logging.Info($"bridge listener on address {IPAddress.Any} port {port}");
}
public void StartListener()
{
try
{
Logging.Info($"Starting to listen for TCP connections");
while (true)
{
Logging.Debug("TCP client ready");
TcpClient client = server.AcceptTcpClient();
Logging.Debug("TCP client connected");
Thread t = new Thread(new ParameterizedThreadStart(HandleConnection));
t.Start(client);
}
}
catch (SocketException e)
{
Logging.Error($"SocketException: {e}");
server.Stop();
}
}
public void HandleConnection(object obj)
{
TcpClient client = (TcpClient)obj;
client.ReceiveTimeout = 10000; // adding this to see if it fixes the server crashes
var stream = client.GetStream();
try
{
if (stream.CanRead)
{
byte[] messageLengthBytes = new byte[4];
int v = stream.Read(messageLengthBytes, 0, 4);
if (v != 4)
{
// this is happening and then the server runs at over 100% so adding lots of logging to figure out what's happening
StringBuilder sb = new StringBuilder($"could not read incoming message. Read {v} bytes");
try
{
sb.Append($"\nfrom {(IPEndPoint)client.Client.RemoteEndPoint}");
}
catch (Exception e)
{
sb.Append($"\ncould not get client's IP address because {e}");
}
sb.Append($"\nclient.Available: {client.Available}");
sb.Append($"\nclient.SendBufferSize: {client.SendBufferSize}");
Logging.Error(sb.ToString());
stream.Close();
client.Close();
stream.Dispose();
client.Dispose();
return;
}
int messageLength = BitConverter.ToInt32(messageLengthBytes, 0);
int readPos = 0;
byte[] recievedData = new byte[messageLength];
while (readPos < messageLength)
{
int size = Math.Min(messageLength - readPos, client.ReceiveBufferSize);
v = stream.Read(recievedData, readPos, size);
readPos += v;
}
Bits incoming = new Bits(recievedData);
incoming.InitReadableBuffer();
int packetType = incoming.ReadInt();
Bits outgoing;
if (packetType == int.MaxValue)
{
Logging.Info($"recieved client handler message");
outgoing = clientHandler(incoming, client);
}
else
{
if (bridgeHandlers.ContainsKey(packetType))
{
Logging.Info($"recieved {(T)(object)packetType}");
outgoing = bridgeHandlers[packetType](incoming);
}
else
{
Logging.Error($"recieved unhandled packetType {packetType}!!!!!");
return;
}
}
if (outgoing != null)
{
#region send response
byte[] sendData = new byte[outgoing.Length() + 4];
// first write the length of the message
BitConverter.GetBytes(outgoing.Length()).CopyTo(sendData, 0);
// then write the message
outgoing.ToArray().CopyTo(sendData, 4);
stream.Write(sendData, 0, sendData.Length);
outgoing.Dispose();
#endregion
}
else
{
byte[] sendData = new byte[4];
BitConverter.GetBytes(0).CopyTo(sendData, 0);
stream.Write(sendData, 0, sendData.Length);
}
}
else
{
Logging.Info("Sorry. You cannot read from this NetworkStream.");
}
}
catch (Exception e)
{
Logging.Error($"Exception: {e}");
stream.Close();
client.Close();
stream.Dispose();
client.Dispose();
}
}
public static void SendTCPmessageFireAndForget(IPEndPoint iPEndPoint, Bits bits)
{
Task.Run(() =>
{
SendTCPmessage(iPEndPoint, bits, out _);
bits.Dispose();
});
}
public static async Task<Bits> SendTCPmessageAsync(IPEndPoint iPEndPoint, Bits bits)
{
TcpClient client = new TcpClient();
client.Connect(iPEndPoint);
NetworkStream stream = client.GetStream();
stream.WriteTimeout = 5000;
stream.ReadTimeout = 5000;
// Send the message
byte[] bytes = new byte[bits.Length() + 4];
BitConverter.GetBytes(bits.Length()).CopyTo(bytes, 0); // write length of message
bits.ToArray().CopyTo(bytes, 4);
await stream.WriteAsync(bytes, 0, bytes.Length);
// Read the response
byte[] messageLengthBytes = new byte[4];
int v = await stream.ReadAsync(messageLengthBytes, 0, 4);
if (v != 4) throw new Exception("could not read incoming message");
int messageLength = BitConverter.ToInt32(messageLengthBytes, 0);
if (messageLength > 0)
{
int readPos = 0;
byte[] recievedData = new byte[messageLength];
while (readPos < messageLength)
{
int size = Math.Min(messageLength - readPos, client.ReceiveBufferSize);
v = await stream.ReadAsync(recievedData, readPos, size);
readPos += v;
}
stream.Close();
client.Close();
bits = new Bits(recievedData);
}
else bits = null;
return bits;
}
public static void SendTCPmessage(IPEndPoint iPEndPoint, Bits bits, out Bits responseBits)
{
try
{
TcpClient client = new TcpClient();
client.Connect(iPEndPoint);
NetworkStream stream = client.GetStream();
stream.WriteTimeout = 50000;
stream.ReadTimeout = 50000;
// Send the message
byte[] bytes = new byte[bits.Length() + 4];
BitConverter.GetBytes(bits.Length()).CopyTo(bytes, 0); // write length of message
bits.ToArray().CopyTo(bytes, 4);
stream.Write(bytes, 0, bytes.Length);
// Read the response
byte[] messageLengthBytes = new byte[4];
if (stream.Read(messageLengthBytes, 0, 4) != 4) throw new Exception("could not read incoming message");
int messageLength = BitConverter.ToInt32(messageLengthBytes, 0);
if (messageLength > 0)
{
int readPos = 0;
byte[] recievedData = new byte[messageLength];
while (readPos < messageLength)
{
int size = Math.Min(messageLength - readPos, client.ReceiveBufferSize);
int v = stream.Read(recievedData, readPos, size);
readPos += v;
}
stream.Close();
client.Close();
responseBits = new Bits(recievedData);
}
else responseBits = null;
}
catch (Exception e)
{
Logging.Error($"Exception: {e}");
responseBits = null;
}
}
}
public delegate Bits BridgeHandler(Bits incoming);
public delegate Bits ClientHandler(Bits incoming, TcpClient client);
}
Notes:
Bits is a class I use for serialization.
You can see in StartListener that I start a thread for every incoming connection
I also use while (true) and AcceptTcpClient to accept tcp connections. But maybe I shouldn't be doing it that way?
I read the first 4 bytes as an int from every packet to determine what kind of packet it is.
then I continue to to read the rest of the bytes until I have read all of it.
There is a lot wrong with your existing code, so it's hard to know what exactly is causing the issue.
Mix of async and non-async calls. Convert the whole thing to async and only use those, do not do sync-over-async.
Assuming stream.Read is actually returning the whole value in one call.
Lack of using in many places.
Repetitive code which should be refactored into functions.
Unsure what the use of bits.ToArray is and how efficient it is.
You may want to add CancellationToken to be able to cancel the operations.
Your code should look something like this:
public class Bridge<T> : IDisposable where T : struct, IConvertible
{
public Dictionary<int, BridgeHandler> bridgeHandlers;
public ClientHandler clientHandler;
TcpListener server;
public Bridge(int port, Dictionary<int, BridgeHandler> bridgeHandlers)
{
server = new TcpListener(IPAddress.Any, port);
this.bridgeHandlers = bridgeHandlers;
Logging.Info($"bridge listener on address {IPAddress.Any} port {port}");
}
public async Task StartListener()
{
try
{
Logging.Info($"Starting to listen for TCP connections");
server.Start();
while (true)
{
Logging.Debug("TCP client ready");
TcpClient client = await server.AcceptTcpClientAsync();
Logging.Debug("TCP client connected");
Task.Run(async () => await HandleConnection(client));
}
}
catch (SocketException e)
{
Logging.Error($"SocketException: {e}");
}
finally
{
if (listener.Active)
server.Stop();
}
}
public async Task HandleConnection(TcpClient client)
{
using client;
client.ReceiveTimeout = 10000; // adding this to see if it fixes the server crashes
using var stream = client.GetStream();
try
{
var incoming = await ReadMessageAsync(stream);
incoming.InitReadableBuffer();
int packetType = incoming.ReadInt();
Bits outgoing;
if (packetType == int.MaxValue)
{
Logging.Info($"recieved client handler message");
outgoing = clientHandler(incoming, client);
}
else
{
if (bridgeHandlers.TryGetValue(packetType, handler))
{
Logging.Info($"recieved {(T)(object)packetType}");
outgoing = handler(incoming);
}
else
{
Logging.Error($"recieved unhandled packetType {packetType}!!!!!");
return;
}
}
using (outgoing);
await SendMessageAsync(stream, outgoing);
}
catch (Exception e)
{
Logging.Error($"Exception: {e}");
}
}
public static void SendTCPmessageFireAndForget(IPEndPoint iPEndPoint, Bits bits)
{
Task.Run(async () =>
{
using (bits)
await SendTCPmessageAsync(iPEndPoint, bits);
});
}
public static async Task<Bits> SendTCPmessageAsync(IPEndPoint iPEndPoint, Bits bits)
{
using TcpClient client = new TcpClient();
await client.ConnectAsync(iPEndPoint);
using NetworkStream stream = client.GetStream();
stream.WriteTimeout = 5000;
stream.ReadTimeout = 5000;
await SendMessageAsync(stream, bits);
return await ReadMessageAsync(stream);
}
}
private async Task SendMessageAsync(Stream stream, Bits message)
{
var lengthArray = message == null ? new byte[4] : BitConverter.GetBytes(bits.Length());
await stream.WriteAsync(lengthArray, 0, lengthArray.Length); // write length of message
if (message == null)
return;
var bytes = bits.ToArray();
await stream.WriteAsync(bytes, 0, bytes.Length);
}
private async Task<Bits> ReadMessageAsync(Stream stream)
{
var lengthArray = new byte[4];
await FillBuffer(stream, lengthArray);
int messageLength = BitConverter.ToInt32(lengthArray, 0);
if (messageLength == 0)
return null;
byte[] receivedData = new byte[messageLength];
await FillBuffer(stream, receivedData);
bits = new Bits(receivedData);
return bits;
}
private async Task FillBuffer(Stream stream, byte[] buffer)
{
int totalRead = 0;
int bytesRead = 0;
while (totalRead < buffer.Length)
{
var bytesRead = await stream.ReadAsync(lengthArray, totalRead, buffer.Length - totalRead);
totalRead += bytesRead;
if(bytesRead <= 0)
throw new Exception("Unexpected end of stream");
}
}
I am a complete beginner in the TCP section, I try to program a Master Server which should handle more then 500 clients with small delay.
This is my first attempt, do you have any suggestions how to improve the code, or is my code complete garbage! :D
I send a Uint16 first as indicator of the message size
// receive Code!
private byte[] indexBuffer = new byte[sizeof(UInt16)];
private int indexSize = sizeof(UInt16);
public async void receiveData(TcpClient client) {
var result = await Task.Run(() => {
try {
int checkSum = client.Client.Receive(indexBuffer, 0, indexBuffer.Length, SocketFlags.None);
if (checkSum != indexSize) return null;
int packageSize = BitConverter.ToUInt16(indexBuffer, 0);
Console.WriteLine(packageSize);
var recData = new Byte[packageSize];
checkSum = client.Client.Receive(recData, 0, packageSize, SocketFlags.None);
if (checkSum != packageSize) return null;
return Encoding.ASCII.GetString(recData);
} catch(Exception ex) {
Console.WriteLine(ex.Message);
return "-1";
}
});
// blabla do something
Console.WriteLine(result);
//---------------------
if (client.Connected)
receiveData(client);
}
IOCP server is good choice in this case.
There are many samples for this.
May be this one is good.
https://www.codeproject.com/Articles/832818/EpServerEngine-cs-A-lightweight-asynchronous-IOCP
Good luck.
I am trying to implement a system that can send and receive "Nessages" (a custom type) using async Sockets. Each Message consists of three elements:
First I send the Message-ID (4 bytes), next is the length of the data (4 bytes) and then the actual data.
The way TCP/IP works, I can not know if the full Message is in the buffer of data that I just received. I need to look for the InfoHeader (ID and length) first and use the remaining data to fill the Message's DataBuffer.
public void ReadCallback(IAsyncResult ar)
{
try
{
// How many bytes were received? They are now stored in the PacketBuffer
int bytesReceived = Socket.EndReceive(ar);
if (bytesReceived != 0)
{
// We did receive some data
if (MessageInfoHeader.Length < NetProtocol.NetInfoHeaderSize)
{
// The InfoHeader we have is not complete yet. How many bytes are missing from it?
int infoHeaderDataInCurrentBuffer = NetProtocol.NetInfoHeaderSize - (int) MessageInfoHeader.Length;
// Check if we have read enough data to fill the entire missing InfoHeader
if (infoHeaderDataInCurrentBuffer > bytesReceived)
{
// Nope, we dont. Fill as much as we can
infoHeaderDataInCurrentBuffer = bytesReceived;
Log.Info("We do not have enough data to complete the InfoHeader yet. Fill as much as we can");
}
else
{
Log.Info("We do have enough data to complete the InfoHeader");
}
// Now fill it from the chunk of data we just received
MessageInfoHeader.Write(ReceivedData, 0, infoHeaderDataInCurrentBuffer);
// See if there is any data remaining in the buffer that we can store in the MessageDataBuffer
int dataRemainingInPacket = bytesReceived - infoHeaderDataInCurrentBuffer;
Log.Info("DataRemainingInPacket: " + dataRemainingInPacket);
// Check again if we have the full header
if (MessageInfoHeader.Length == NetProtocol.NetInfoHeaderSize)
{
Log.Info("We now have assembled the full InfoHeader");
// We do have the full InfoHeader. Use the rest of the data to fill the DataBuffer
using (MemoryStream ms = new MemoryStream())
{
byte[] b = MessageInfoHeader.GetBuffer();
ms.Write(b, 0, b.Length);
ms.Seek(0, SeekOrigin.Begin);
try
{
using (BinaryReader br = new BinaryReader(ms))
{
CurrentMessageID = br.ReadInt32();
CurrentMessageDataLength = br.ReadInt32();
Log.Info("InfoHeader: MessageID=" + CurrentMessageID + " / DataLength=" + CurrentMessageDataLength);
}
}
catch (Exception ex)
{
throw ex;
}
}
if (dataRemainingInPacket > 0)
{
Log.Info("There are " + dataRemainingInPacket + " bytes remaining in the packet. Writing them to the PacketDataBuffer");
MessageDataBuffer.Write(ReceivedData, CurrentPacketBytesRead, ReceivedData.Length - CurrentPacketBytesRead);
}
}
}
else
{
MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if(ReceivedData.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
}
// Start receiving the new TCP-Packet
Socket.BeginReceive(ReceivedData, 0, ReceivedData.Length, SocketFlags.None, new AsyncCallback(ReadCallback), null);
}
else
{
Log.Info("No data has been received");
}
}
catch (Exception ex)
{
throw ex;
}
}
private void AcceptCallback(IAsyncResult ar)
{
// Retrieve the listening Socket
Socket listener = (Socket)ar.AsyncState;
// Retrieve the Socket that is actually connected to the client
Socket csocket = listener.EndAccept(ar);
Log.Info("Accepted connection from: " + csocket.RemoteEndPoint.ToString());
// Create a new client with the CSocket and add it to the list
ClientConnection client = ClientManager.AddClient(csocket);
// Start reading data from the Client's Socket and then enter the "read-loop" inside the client
client.Socket.BeginReceive(client.ReceivedData, 0, (int) client.ReceivedData.Length, SocketFlags.None, new AsyncCallback(client.ReadCallback), null);
// Turn the listener back into listening mode
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
}
public const int NetInfoHeaderSize = 8; // 2x int
public static byte[] FrameMessage(NetMessage message)
{
byte[] result;
using(MemoryStream ms = new MemoryStream())
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write(message.ID);
bw.Write(message.Data.Length);
bw.Write(message.Data);
}
result = ms.ToArray();
}
return result;
}
This piece of code obviously does not work correctly. I get the "RECEIVED ENTIRE MESSAGE" line multiple times.
Question: What exactly am I doing wrong? I'm failing to wrap my head around what is the best way to handle something like this? Thanks in advance for any input!
There could be quite a few problems. But the first one I can see for the loop on "RECEIVED ENTIRE MESSAGE" is that you are assuming the ReceivedData buffer has been filled entirely by Socket.EndReceive(ar); - which isn't the case, so a first troubleshooting start could be to rewrite this:
MessageDataBuffer.Write(ReceivedData, 0, ReceivedData.Length);
if (ReceivedData.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
to:
MessageDataBuffer.Write(ReceivedData, 0, bytesReceived);
if (MessageDataBuffer.Length <= NetDataBufferSize)
{
Log.Info("WE HAVE RECEIVED THE ENTIRE MESSAGE");
}
Assuming that MessageDataBuffer is intended to contain the entire message
This is for local communication between a MoSync application and an external DLL, MoSync does not allow me to use 3rd part DLLs and that is the reason why I have to implement this bridge software instead of using a simple call to a DLL, I have to convert from xml to the DLL Message format, and again to XML. I know this is a dumb thing, unfortunately there is no flexibility to change the architecture. Initially i thought that there was only one request so I had Sync coms but now I find out there can be more than one request, so I need to implement Async back again.
I have an exception that is thrown from time to time, since I am new to C# I am unable to find the memory leak... perhaps a pair of more well trained eyes can find the issue
SOURCE CODE:
I have written the following code, I am quite new to C# and Sockets, so perhaps I have made some big mistakes that only more experienced eyes can detect. This is to be used in a Windows Mobile 6.1 device, so I am trying to avoid using many threads.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Diagnostics;
namespace SmartDevice_Server
{
//ClientConnection saves connection information is used to keep context in Async and Event calls
public class ClientConnection : EventArgs
{
public NetworkStream NetworkStream { get; private set; }
public byte[] Data { get; private set; }
public int byteReadCount { get; set; }
public ClientConnection(NetworkStream networkStream, byte[] data)
{
NetworkStream = networkStream;
Data = data;
}
}
//MySocket - Is a server that listens for events and triggers Events upon Request Completion
public class MySocketTCP
{
#region Class Members
TcpListener myTcpListener;
TcpClient myTcpClient;
NetworkStream myNetworkStream;
const string localHost = "127.0.0.1";
IPAddress myAddress = IPAddress.Parse(localHost);
int myPortNumber = 58889;
byte[] myData;
int bytesReadCount;
const int MIN_REQUEST_STRING_SIZE = 10;
int TimeStart;
//Event
public event socketReadCompleteHandler socketReadCompleteEvent;
public EventArgs eventArguments = null;
public delegate void socketReadCompleteHandler(MySocketTCP myTcpSocket, ClientConnection eventArguments);
#endregion
//Constructor
public MySocketTCP()
{
Init();
}
//Constructor overloaded to receive IPAdress Host, and Port number
public MySocketTCP(IPAddress hostAddress, int portNumber)
{
myAddress = hostAddress;
myPortNumber = portNumber;
Init();
}
//Initializes the TCPListner
public void Init()
{
try
{
myTcpListener = new TcpListener(myAddress, myPortNumber);
//myNetworkStream = myTcpClient.GetStream();
}
catch (Exception ex)
{
throw ex;
}
}
/*TODO_Listener_Timer: After you accept a connection you wait for data to be Read indefinitely
*Possible solution: Use a timeout to close the socket connection.
*Check WIKI, TODOS
* */
//Listens Asynchronously to Clients, class a recieveMessageHandler to process the read
public void ListenAsync()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
myTcpClient = myTcpListener.AcceptTcpClient();
var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
}
}
//Callback is used to Process the request Asynchronously, triggers socketReadCompleteEvent
public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection clientInstance)
{
bytesReadCount = 0;
lock (clientInstance.NetworkStream)
{
try
{
bytesReadCount = clientInstance.NetworkStream.EndRead(asyncResult);
clientInstance.byteReadCount = bytesReadCount;
}
catch (Exception exc)
{
throw exc;
}
}
if (bytesReadCount < MIN_REQUEST_STRING_SIZE)
{
//Could not read form client.
Debug.WriteLine("NO DATA READ");
}
else
{
if (socketReadCompleteEvent != null)
{
socketReadCompleteEvent(this, clientInstance);
}
}
}
//Reads the request, uses the ClientConnection for context
public string ReadAsync(ClientConnection connObj)
{
int bytesReadCount = connObj.byteReadCount;
byte[] myData = connObj.Data;
string xmlMessage;
try
{
xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
}
catch (Exception ex)
{
throw ex;
}
return xmlMessage;
}
//Deprecated
public string Read()
{
string xmlMessage;
try
{
xmlMessage = Encoding.ASCII.GetString(myData, 0, bytesReadCount);
}
catch (Exception ex)
{
throw ex;
}
return xmlMessage;
}
//Deprecated
public void Write(byte[] outBytes)
{
try
{
myNetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
}
//Deprecated
public void Write(string outMessage)
{
byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);
try
{
myNetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
int TimeEnd = Environment.TickCount;
int TimeResult = TimeEnd - TimeStart;
}
//Is used to send the message to the correct socket
public void WriteAsync(ClientConnection connObj, string outMessage)
{
byte[] outBytes = Encoding.ASCII.GetBytes(outMessage);
try
{
connObj.NetworkStream.Write(outBytes, 0, outBytes.Length);
}
catch (Exception ex)
{
throw ex;
}
int TimeEnd = Environment.TickCount;
int TimeResult = TimeEnd - TimeStart;
}
//Closes the client
public void Close()
{
//myNetworkStream.Close();
try
{
myTcpClient.Close();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
The most likely problem is that you are expecting to do exactly three "reads" for the three "writes" that client did.
This is a wrong assumption since TCP socket is a byte stream and does not preserve your application message boundaries. The server might consume those three "messages" sent by the client in one, or two, or seventeen reads.
You need to tell the server somehow where the message ends in the byte stream. Usual choices are fixed length messages, delimiters, message headers that tell length of the payload, self-describing formals like XML, etc.
So you continue reading from the stream until you have a complete message for processing, but at the same time you might have a part of the next message already read into your buffer.
I think the problem here is that you're only holding a single NetworkStream (myNetworkStream) as such, if a second client connects before the first has sent data, your accept loop will overwrite myNetworkStream with the stream for the 2nd connection. When the first client then sends some data your receiveMessageHandler will call EndRead on the 2nd connection's NetworkStream (which was stored in myNetworkStream when the 2nd client connected), but passing in the asyncResult from the 1st client's read. This causes the exception you indicate. Specifically when I tested it, I got the following message:
Unable to read data from the transport connection: The IAsyncResult object was not returned from the corresponding asynchronous method on this class.
Parameter name: asyncResult.
Try making the following changes:
// Create a class to hold details related to a client connection
public class ClientConnection
{
public ClientConnection(NetworkStream networkStream, byte[] data)
{
NetworkStream = networkStream;
Data = data;
}
public NetworkStream NetworkStream { get; private set; }
public byte[] Data { get; private set; }
}
public void Listen()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
myTcpClient = myTcpListener.AcceptTcpClient();
var client = new ClientConnection(myTcpClient.GetStream(), new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, client.Data.Length, r => receiveMessageHandler(r, client), null);
}
}
public void receiveMessageHandler(IAsyncResult asyncResult, ClientConnection client)
{
var byteReadCount = client.NetworkStream.EndRead(asyncResult);
if (byteReadCount < MIN_REQUEST_STRING_SIZE)
{
//Could not read form client.
//Erro - Como tratar? Close()
}
else
{
if (socketReadCompleteEvent != null)
{
socketReadCompleteEvent(this, eventArguments);
}
}
}
As others have mentioned, there are additional issues related to your expectations of matched reads/writes between sender and receiver, but this seems to be the cause of the actual issue you're seeing.
Edit:
Here's a server that will receive data, and call a callback method when a full message is received. The callback returns a string, which is then sent back to the client, which calls its own replyCallback with the response data. Only a single request-response is sent per connection (which is rather inefficient, but should serve as a good starting point).
public static class Server
{
public static void Run(int port, Action<string> callback)
{
var listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
while (true)
{
using (var client = listener.AcceptTcpClient())
{
try
{
var buffer = new byte[2048];
using (var memoryStream = new MemoryStream())
{
using (var stream = client.GetStream())
{
stream.ReadTimeout = 1000; // 1 second timeout
int bytesRead;
// Loop until Read returns 0, signalling the socket has been closed
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
}
}
// Pass the client's message to the callback and use the response as the reply message to the client.
var reply = Encoding.UTF8.GetBytes(callback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length)));
stream.Write(reply, 0, reply.Length);
}
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
}
}
}
Here's a small client program that will connect, send its data and wait for a response. Once the response is received, it will pass call replyCallback with the server's response:
public static class Client
{
public static void Run(string hostname, int port, string dataToSend, Action<string> replyCallback)
{
using (var client = new TcpClient(hostname, port))
{
using (var stream = client.GetStream())
{
var buffer = Encoding.UTF8.GetBytes(dataToSend);
stream.Write(buffer, 0, buffer.Length);
// Shutdown the send side of the socket, indicating to the server we're done sending our message
client.Client.Shutdown(SocketShutdown.Send);
using (var memoryStream = new MemoryStream())
{
stream.ReadTimeout = 1000; // 1 second timeout
int bytesRead;
// Loop until Read returns 0, signalling the socket has been closed
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
}
replyCallback(Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Length));
}
}
}
}
}
And a small test-harness to tie it all together:
static class Program
{
static void Main(string[] args)
{
var port = 12345;
ThreadPool.QueueUserWorkItem(o => Server.Run(port, ProcessClientMessage));
while (true)
{
Console.WriteLine("Enter a message to send and hit enter (or a blank line to exit)");
var data = Console.ReadLine();
if (string.IsNullOrEmpty(data)) break;
Client.Run("localhost", port, data, m => Console.WriteLine("Client received server reply: {0}", m));
}
}
private static string ProcessClientMessage(string clientMessage)
{
Console.WriteLine("Server received client message: {0}", clientMessage);
// This callback would ordinarily process the client message, then return a string that will be sent back to the client in response.
// For now, we'll just return a fixed string value...
return "This is the server reply...";
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NullReferenceException on instanciated object?
What is a NullReferenceException in .NET?
I have built a TCP client in C#, when the client cannot connect Im getting the below.
I keep getting a NullReference Exception with the following code, I cannot figure how to catch it or stop it.. any help will be greatly appreciated.
Its happening at "int a = sReponseData.Length"
{
string sMesg = "LogOn Ext:" + txtdevice.Text;
SendMessage(sMesg);
int a = sResponseData.Length;
//Geting the Script out of the message.
if (a > 10)
{
string stemp = sResponseData;
string[] sSplit = stemp.Split(new Char[] { ';'});
foreach (string s in sSplit)
{
if ((s.Trim() != "Tag") & (s.Trim() != "StopStart"))
sScript = s;
}
}
}
}
and here is the sMesg
{
InitializeComponent();
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
//Int32 port = 13000;
TcpClient client = new TcpClient("127.0.0.1", 32111);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(sMsg);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
//MessageBox.Show("Sent: {0}" + sMsg);
// Receive the TcpServer.response.
// String to store the response ASCII representation.
sResponseData = String.Empty;
if (stream.CanRead)
{
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while (stream.DataAvailable);
// Print out the received message to the console.
sResponseData = myCompleteMessage.ToString();
//MessageBox.Show(sResponseData);
}
else
{
sResponseData = ("3");
MessageBox.Show("TagIt Server is unavalible at this moment.");
}
// Close everything.
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
WriteLog("ArgumentNullException: {0}" + e);
MessageBox.Show("TagIt Server is unavalible at this moment.");
}
catch (SocketException e)
{
WriteLog("SocketException: {0}" + e);
MessageBox.Show("TagIt Server is unavalible at this moment.");
}
}
You can check if Response data has value or not:
Something like:
int a;
try
{
if(sResponseData==null || sResponseData=="" )
{
MessageBox.Show("ResponseData is NULL or Empty"); //Shows Error
}
else
{
//SresponseData has value
string sMesg = "LogOn Ext:" + txtdevice.Text;
SendMessage(sMesg);
a= Convert.ToInt32(sResponseData).Length;
//Geting the Script out of the message.
if (a > 10)
{
string stemp = sResponseData;
string[] sSplit = stemp.Split(new Char[] { ';'});
foreach (string s in sSplit)
{
if ((s.Trim() != "Tag") & (s.Trim() != "StopStart"))
sScript = s;
}
}
}
}
catch (Execption ex)
{
MessageBox.Show(ex.Message); //Shows Error
}
Before checking length of sResponseData make sure you check whether sResponseData is null or not.