[C#] TcpClient problems

Soldato
Joined
12 Apr 2004
Posts
11,788
Location
Somewhere
I have the following code, which simply reads data from a TCP connection with a server:
Code:
private byte[] GetTcpData()
{
	if (m_IsQueryInProgress)
	{
		return m_CachedTcpData;
	}

	m_IsQueryInProgress = true;
	try
	{
		TcpClient client = new TcpClient();
		client.Connect(m_ServerAddress, m_TcpPort);
		client.ReceiveTimeout = Properties.Settings.Default.TcpTimeout;
		client.SendTimeout = Properties.Settings.Default.TcpTimeout;
		[B]int availableData = client.Available;[/B]
		using (Stream stream = client.GetStream())
		{
			BinaryReader reader = new BinaryReader(stream);
			m_CachedTcpData = reader.ReadBytes(availableData);
		}
		return m_CachedTcpData;
	}
	finally
	{
		m_IsQueryInProgress = false;
	}
}

This all works perfectly, and returns 41 bytes of data, if I step through the line typed in bold (a break point must be placed on that line or before, then stepped trough). If I don't, client.Available remains at 41, and availableData at 0.

I'm stumped. Any ideas? :)
 
Associate
Joined
27 Oct 2002
Posts
897
If it works when you step through I'd guess it's one of two things:

1. Timing - the pause when you break is enough to read the data in whereas the miliseconds it takes when running isn't. Are you sure the client.Available is still 41 and not 0 because no data has been received yet?

2. Watches - if you have a watch on client.Available, the debugger will be calling client.Available in order to get the value - the act of doing this runs whatever code the Available property contains. I had something like this where I had a watch on some unassociated property and didn't realise that it was doing something to the state of my object that was changing the outcome of the thing I actually cared about.

It might be worth outputting the contents of client.Available to the console or output (Diagnostics.Debug.WriteLine) at some strategic possitions to check the result without slowing the execution down too much.

One other thing, I'd personally reccomend using TcpSocket rather than the TcpClient wrapper - the lower level socket communication isn't much more complex and will afford you greater control over what goes on in your program. I had a mare with TcpServer and TcpClient trying to get a virtual whiteboard to work - I finally fixed it by using TcpSockets and writing the packet control myself.

Good luck
 
Soldato
OP
Joined
12 Apr 2004
Posts
11,788
Location
Somewhere
Well, I don't have any watches on those variables, and I haven't received any data via that TcpClient yet, so I suppose it must be the former. How would I get round this?

Also, I can't find any TcpSocket class in the object browser :confused:
 
Associate
Joined
27 Oct 2002
Posts
897
Oops, I got the name wrong - it's not TcpSocket, it's just Socket (System.Net.Sockets.Socket) and the constructor takes a ProtocolType which in this case will be Tcp.

When using a Socket to read whatever comes in you should probably use an Async call and an internal buffer to get a reasonable sized chunck before trying to do something with it (since you could get your data 1 byte at a time).

Async was a bit daunting when I did it first but once you do a few functions you'll realise it's pretty simple:

1. Connect your socket using Socket.Connect (or BeginConnect if you want to do EVERYTHING Async but tbh if you are only connecting to one socket waiting for it to connect should be fine.

2. Set up your buffer (just a byte array of a fixed size)

3. Call Socket.BeginReceive to start looking for incoming data. The call will look something like this:
Code:
_connection.Socket.BeginReceive(_connection.Buffer, 0, _connection.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), _connection);
"ReceiveCallback" is the name of the method that is called by the Async opperation

4. Do your read from the socket in "ReceiveCallback":
Code:
private void ReceiveCallback(IAsyncResult result)
        {
            lock (_connection)
            {
                try
                {
                    _connection.BytesRead += _connection.Socket.EndReceive(result);

...
_connection is my global object that holds the socket, buffer and some other application specific info.
_connection.Buffer now contains _connection.BytesRead worth of data. At this point you should check if the buffer is full (or has enough in for you to use in whatever way). If it is full you should do something with the data, and reset the BytesRead to 0 (don't need to empty the buffer, it will just be overwritten when you start writing at 0 next time).

5. At the end of the "ReceiveCallback" method once you have done something with your data (if the buffer is full) you need to do another async call to get the next blob of data:
Code:
_connection.Socket.BeginReceive(_connection.Buffer, _connection.BytesRead, _connection.Buffer.Length - _connection.BytesRead, SocketFlags.None, new AsyncCallback(ReceiveCallback), _connection);
This says: start receiving from Socket putting the data into _connection.Buffer, starting at byte _connection.BytesRead (i.e. if BytesRead is 50, Buffer[0] to Buffer[49] have data in them so start at Buffer[50]), up to a maximum of Buffer.Length - BytesRead (to make sure you don't read more than your buffer can take) and then async call "ReceiveCallback"


That sequence should read as much or as little data as is sent to you. You will get an exception on Socket.EndReceive if the connection is closed/broken.

I haven't done any Socket stuff in work and I've been too fed up of programming by the time I get home to do any "for fun" - I'd forgotten how nifty this stuff was :)


If you want to decipher the stuff you receive you will have to buffer the data until you have received a whole packet. The way to do this is to have two reading stages - reading the header and reading the message. The header will be a fixed length message (mine is 54 bytes - the length of a binary-serialized Int32) which tells you how long the following message is going to be. You read the header (BeginReceive(buffer, 0, 54...)), then set up your buffer to be that length and then just read until your buffer is exactly full at which point you can Deserialize the message and so something with it (and start reading the next header obv).

I think I'll write a stupid online game this weekend...
 
Soldato
Joined
18 Oct 2002
Posts
5,600
Location
Surrey
I'm using TCPClients in a current prototype with no real problems:

Code:
TcpClient tcpc = new TcpClient();
logManager.writeLog("Read config file successfully");
String sSourceServer = configTable["generatorhost"].ToString();

int connectPort = int.Parse(configTable["generatorport"].ToString());
	
// Verify that the server exists 
if (Dns.GetHostByName(sSourceServer) == null) 
{
	logManager.writeLog("Cannot find source server");
	return;
}
// Try to connect to the source server
try
{
	tcpc.Connect(sSourceServer, connectPort);
}
catch (System.Net.Sockets.SocketException)
{
        logManager.writeLog("Failed to connect to source server");		
	MessageBox.Show("Failed to connect to source server: {0}", sSourceServer);
	return;
}
			

// Get the stream
Stream s;
try 
{
	s = tcpc.GetStream();
} 
catch (InvalidOperationException) 
{
	MessageBox.Show("Cannot get stream from  source server: {0}", sSourceServer);
	return;
}


StreamReader reader = new StreamReader(s);
Console.WriteLine("After Stream reader created");

string sXMLNode = "";
logManager.writeLog("Begin Processing XML chunks");
while((sXMLNode = reader.ReadLine()) != null)
{
    ... AND SO ON

This is working fine for me at a high rate of output on the stream.
HT
 
Soldato
OP
Joined
12 Apr 2004
Posts
11,788
Location
Somewhere
Just read through your post Reezer, very helpful! :)

Are there any inherent advantages in doing this specific operation asynchronously? As it happens, the method I posted would be called on a threadpool thread anyway, so response time isn't too much of an issue.
 
Associate
Joined
27 Oct 2002
Posts
897
Inquisitor said:
Just read through your post Reezer, very helpful! :)

Are there any inherent advantages in doing this specific operation asynchronously? As it happens, the method I posted would be called on a threadpool thread anyway, so response time isn't too much of an issue.

Well if you are only reading from one socket and not doing anything else in the background (updating the UI, accepting user input etc.) then no, the synchronous methods will do exactly the same job and you'll basically be calling the "ReceiveCallback" directly over and over again but with Socket.Receive instead of EndReceive.

Personally I write almost everything async because it's hardly any more code and you don't have to worry about updating your UI and you can very simply add connections later. The above example works in a single connection mode on my client program as well as a multiple (up to 10,000's in *theory*) with no changes to the callback at all.

The one thing you do have to mess about with if you go the async route is making sure you update your UI on the main thread - if you try to access a Control directly from within your callback you will get an "IllegalCrossThread<somethingsomething>Exception". To get around this you need a method inside your top level control that you can Invoke on the main thread. It sounds complecated but you'll soon realise you only need a Do-Something-With-This-Message(MyMessage) function on the UI in 99% of cases.

Apparently there are also reliability, scalability, maintainability, etc. benefits of doing everything async but that could just be microsoft trying to make their async stuff look cool.

If you are doing this for a specific one off project I'd probably just do it the simplest way, but if your just experimenting or you might want to improve/upgrade it later I'd go the async, thread-safe, interface based tcp library route - you should learn more and it will be more satisfying to finish (it that's your bag ;))
 
Soldato
OP
Joined
12 Apr 2004
Posts
11,788
Location
Somewhere
UI updating isn't a problem really. The object that the method is a member of is completely independant and self-contained. The main UI thread simply refreshes the data contained in the object with said method (well, another method which calls that method in the process), and then takes the data from the object's properties.

Basically, I have a class called StatusMonitor, with a RefreshInfo() method. This method returns void and simply updates the data contained within the object's fields. It does this by calling two methods (which is where the TCP data is needed) from another class, StatusChecker, on ThreadPool threads (QueueUserWorkItem) which themselves are synchronous, and return the info directly. The callback for QueueUserWorkItem then updates the StatusMonitor object's fields once the methods have returned.

It just wouldn't make a lot of sense to get the TCP data in async as well, as it's already being done async.
 
Last edited:
Back
Top Bottom