I have a TCP/IP connection between a server and a client. Once the connection is established, the server will start sending a lot of data to the client. The problem that I'm having is if the connection is slow, at some point the server will become unresponsive. I guess this is because it's buffering data waiting for the data that has already been sent to be acknowledged.
Is there are way before trying to send data to check the size of the current buffer, so I know I should wait before continuing sending data?
Thanks.
This is what I'm doing to send data:
System.Net.Sockets.Socket ClientSocket;
...
public void Send(byte[] data, int size)
{
try
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.SetBuffer(data, 0, size);
bool pending = ClientSocket.SendAsync(e);
}
catch (Exception ex)
{
}
}
The documentation for SendAsync says that it will raise the SocketAsyncEventArgs.Completed event on the e parameter when the operation is complete. So if you write an event handler that responds to that event, you can always know how many operations you have outstanding.
There's a reasonably good example of using the event here: http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
I found the solution. Basically I should only send data when
ClientSocket.Poll(0, SelectMode.SelectWrite) is true.
Easy and effective!
Related
I am working on client-server appliction in C#. The comunication between them is with TCP sockets. The server listen on specific port for income clients connection. After a new client arrived, his socket being saved in a socket list. I define every new client socket with receive timeout of 1 ms. To receive from the client sockets without blocking my server I use the threadpool like this:
private void CheckForData(object clientSocket)
{
Socket client = (Socket)clientSocket;
byte[] data = new byte[client.ReceiveBufferSize];
try
{
int dataLength = client.Receive(data);
if (dataLength == 0)// means client disconnected
{
throw (new SocketException(10054));
}
else if (DataReceivedEvent != null)
{
string RemoteIP = ((IPEndPoint)client.RemoteEndPoint).Address.ToString();
int RemotePort = ((IPEndPoint)client.RemoteEndPoint).Port;
Console.WriteLine("SERVER GOT NEW MSG!");
DataReceivedEvent(data, new IPEndPoint(IPAddress.Parse(RemoteIP), RemotePort));
}
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
catch (SocketException e)
{
if (e.ErrorCode == 10060)//recieve timeout
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
else if(e.ErrorCode==10054)//client disconnected
{
if (ConnectionLostEvent != null)
{
ConnectionLostEvent(((IPEndPoint)client.RemoteEndPoint).Address.ToString());
DisconnectClient(((IPEndPoint)client.RemoteEndPoint).Address.ToString());
Console.WriteLine("client forcibly disconected");
}
}
}
}
My problem is when sometimes the client send 2 messages one after another, the server doesn't receive the second message. I checked with wireshark and it shows that both of the messages were received and also got ACK.
I can force this problem to occur when I am putting break point here:
if (e.ErrorCode == 10060)//recieve timeout
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
Then send the two messages from the client, then releasing the breakpoint.
Does anyone met this problem before?
my problem is when sometimes the client send 2 messages one after another, the server doesn't receive the second message
I think it's much more likely that it does receive the second message, but in a single Receive call.
Don't forget that TCP is a stream protocol - just because the data is broken into packets at a lower level doesn't mean that one "send" corresponds to one "receive". (Multiple packets may be sent due to a single Send call, or multiple Send calls may be coalesced into a single packet, etc.)
It's generally easier to use something like TcpClient and treat its NetworkStream as a stream. If you want to layer "messages" on top of TCP, you need to do so yourself - for example, prefixing each message with its size in bytes, so that you know when you've finished receiving one message and can start on the next. If you want to handle this asynchronously, I'd suggest sing C# 5 and async/await if you possibly can. It'll be simpler than dealing with the thread pool explicitly.
Message framing is what you need to do. Here: http://blog.stephencleary.com/2009/04/message-framing.html
if you are new to socket programming, I recommend reading these FAQs http://blog.stephencleary.com/2009/04/tcpip-net-sockets-faq.html
I was looking how to detect a 'client disconnect' when using a TcpListener.
All the answers seem to be similar to this one:
TcpListener: How can I detect a client disconnect?
Basically, read from the stream and if Read() returns 0 the client had disconnected.
But that's assuming that a client disconnects after every single stream of data it sent.
We operate in environments where the TCP connect/disconnect overhead is both slow and expensive.
We establish a connection and then we send a number of requests.
Pseudocode:
client.Connect();
client.GetStatus();
client.DoSomething();
client.DoSomethingElse();
client.AndSoOn();
client.Disconnect();
Each call between Connect and Disconnect() sends a stream of data to the server.
The server knows how to analyze and process the streams.
If let the TcpListener read in a loop without ever disconnecting it reads and handles all the messages, but after the client disconnects, the server has no way of knowing that and
it will never release the client and accept new ones.
var read = client.GetStream().Read(buffer, 0, buffer.Length);
if (read > 0)
{
//Process
}
If I let the TcpListener drop the client when read == 0 it only accepts
the first stream of data only to drop the client immediately after.
Of course this means new clients can connect.
There is no artificial delay between the calls,
but in terms of computer time the time between two calls is 'huge' of course,
so there will always be a time when read == 0 even though that does not mean
the client has or should be disconnected.
var read = client.GetStream().Read(buffer, 0, buffer.Length);
if (read > 0)
{
//Process
}
else
{
break; //Always executed as soon as the first stream of data has been received
}
So I'm wondering... is there a better way to detect if the client has disconnected?
You could get the underlying socket using the NetworkStream.Socket property and use it's Receive method for reading.
Unlike NetworkStream.Read, the linked overload of Socket.Receive will block until the specified number of bytes have been read, and will only return zero if the remote host shuts down the TCP connection.
UPDATE: #jrh's comment is correct that NetworkStream.Socket is a protected property and cannot be accessed in this context. In order to get the client Socket, you could use the TcpListener.AcceptSocket method which returns the Socket object corresponding to the newly established connection.
Eren's answer solved the problem for me. In case anybody else is facing the same issue
here's some 'sample' code using the Socket.Receive method:
private void AcceptClientAndProcess()
{
try
{
client = server.Accept();
client.ReceiveTimeout = 20000;
}
catch
{
return;
}
while (true)
{
byte[] buffer = new byte[client.ReceiveBufferSize];
int read = 0;
try
{
read = client.Receive(buffer);
}
catch
{
break;
}
if (read > 0)
{
//Handle data
}
else
{
break;
}
}
if (client != null)
client.Close(5000);
}
You call AcceptClientAndProcess() in a loop somewhere.
The following line:
read = client.Receive(buffer);
will block until either
Data is received, (read > 0) in which case you can handle it
The connection has been closed properly (read = 0)
The connection has been closed abruptly (An exception is thrown)
Either of the last two situations indicate the client is no longer connected.
The try catch around the Socket.Accept() method is also required
as it may fail if the client connection is closed abruptly during the connect phase.
Note that did specify a 20 second timeout for the read operation.
The documentation for NetworkStream.Read does not reflect this, but in my experience, 'NetworkStream.Read' blocks if the port is still open and no data is available, but returns 0 if the port has been closed.
I ran into this problem from the other side, in that NetworkStream.Read does not immediately return 0 if no data is currently available. You have to use NetworkStream.DataAvailable to find out if NetworkStream.Read can read data right now.
Basically, I've written a windows phone 7 client, which is supposed to receive a very long string from the server. However, there seem to be some limitations on the phone networking code, and am having trouble figuring out how to do this. The following is the code I am using:
public string Receive()
{
string response = "Operation Timeout";
StringBuilder content = new StringBuilder();
// We are receiving over an established socket connection
if (_socket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = _socket.RemoteEndPoint;
// Setup the buffer to receive the data
socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);
// Inline event handler for the Completed event.
// Note: This even handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
}
else
{
response = e.SocketError.ToString();
}
_clientDone.Set();
});
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
// Make an asynchronous Receive request over the socket
_socket.ReceiveAsync(socketEventArg);
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
response = "Socket is not initialized";
}
return response;
}
As it stands, this will only accept the first N bytes of the message and return that... any help on how to do this would be greatly appreciated! Thanks!
Firstly, I would try to redesign your code to be appropriately async - working round the fact that WP7 doesn't offer synchronous IO by building your own isn't nearly as clean as embracing the asynchrony.
Secondly, you're assuming that you only need a single ReceiveAsync call to get the whole data. Assuming your using TCP, that's a stream-based protocol. You'll need to either add some sort of delimiter or length prefix, or keep reading until the other side closes the connection if that's the way your protocol works.
(Any reason you're not doing this with HTTP and WebClient or WebRequest, by the way?)
Firstly, I would try to redesign your code to be appropriately async - working round the fact that WP7 doesn't offer synchronous IO by building your own isn't nearly as clean as embracing the asynchrony.
Secondly, you're assuming that you only need a single ReceiveAsync call to get the whole data. Assuming your using TCP, that's a stream-based protocol. You'll need to either add some sort of delimiter or length prefix, or keep reading until the other side closes the connection if that's the way your protocol works.
(Any reason you're not doing this with HTTP and WebClient or WebRequest, by the way?)
i am trying to disconnect a client from a server but the server still sees it as being connected. I cant find a solution to this and Shutdown, Disconnect and Close all dont work.
Some code for my disconnect from the client and checking on the server:
Client:
private void btnDisconnect_Click(object sender, EventArgs e)
{
connTemp.Client.Shutdown(SocketShutdown.Both);
connTemp.Client.Disconnect(false);
connTemp.GetStream().Close();
connTemp.Close();
}
Server:
while (client != null && client.Connected)
{
NetworkStream stream = client.GetStream();
data = null;
try
{
if (stream.DataAvailable)
{
data = ReadStringFromClient(client, stream);
WriteToConsole("Received Command: " + data);
}
} // So on and so on...
There are more writes and reads further down in the code.
Hope you all can help.
UPDATE: I even tried passing the TCP client by ref, assuming there was a scope issue and client.Connected remains true even after a read. What is going wrong?
Second Update!!:
Here is the solution. Do a peek and based on that, determine if you are connected or not.
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] checkConn = new byte[1];
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
Here is the solution!!
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] checkConn = new byte[1];
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
From the MSDN Documentation:
The Connected property gets the
connection state of the Client socket
as of the last I/O operation.
When it
returns false, the Client socket was
either never connected, or is no
longer connected. Because the
Connected property only reflects the
state of the connection as of the most
recent operation, you should attempt
to send or receive a message to
determine the current state. After the
message send fails, this property no
longer returns true. Note that this
behavior is by design. You cannot
reliably test the state of the
connection because, in the time
between the test and a send/receive,
the connection could have been lost.
Your code should assume the socket is
connected, and gracefully handle
failed transmissions.
I am not sure about the NetworkStream class but I would think that it would behave similar to the Socket class as it is primarily a wrapper class. In general the server would be unaware that the client disconnected from the socket unless it performs an I/O operation on the socket (a read or a write). However, when you call BeginRead on the socket the callback is not called until there is data to be read from the socket, so calling EndRead and getting a bytes read return result of 0 (zero) means the socket was disconnected. If you use Read and get a zero bytes read result I suspect that you can check the Connected property on the underlying Socket class and it will be false if the client disconnected since an I/O operation was performed on the socket.
It's a general TCP problem, see:
How do I check if a SSLSocket connection is sane on Java?
Java socket not throwing exceptions on a dead socket?
The workaround for this tend to rely on sending the amount of data to expect as part of the protocol. That's what HTTP 1.1 does using the Content-Length header (for a entire entity) or with chunked transfer encoding (with various chunk sizes).
Another way is to send "NOOP" or similar commands (essentially messages that do nothing but make sure the communication is still open) as part of your protocol regularly.
(You can also add to your protocol a command that the client can send to the server to close the connection cleanly, but not getting it won't mean the client hasn't disconnected.)
I am using a serial port to communicate with a remote diagnostics device.
The length of the response from the remote device varies depending upon the command but is known ahead of time. So, currently I send the command and wait for the required number of response bytes to be received.
I subscribe to the 'SerialPort.DataReceived' event whenever I'm not actively soliciting data. The handler for this event simply dumps any 'unsolicited' received data to a log (unsolicited data is typically only received if the remote device restarts unexpectedly, etc).
In some cases I want to send commands at a rate of about 60Hz.
My question is whether it's best to unsubscribe/subscribe to the 'SerialPort.DataReceived' event every time I call my 'SendCommand' method to actively solicit data, or should I leave the event subscription alone and just toggle a boolean 'TransferInProgress' flag that the DataReceived handler can use to ignore incoming data when I'm actively soliciting it?
Here's the current implementation:
public virtual bool SendCommand(byte[] command, ref byte[] response) {
try {
TransferInProgress = true;
OnTransferStarted();
// temporarily unsubscribe since we're actively soliciting data
_port.DataReceived -=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
_port.DiscardInBuffer();
_port.Write(command, 0, command.Length);
OnCommandSent(command);
// read the requested number of response bytes
int responseBytesRead = 0;
while (responseBytesRead < response.Length) {
responseBytesRead +=
_port.Read(response, responseBytesRead, (response.Length - responseBytesRead));
}
OnCommandResponseReceived(response);
return true;
}
catch (Exception ex) {
OnCommandSendFailed(ex.Message);
return false;
}
finally {
_port.DataReceived +=
new SerialDataReceivedEventHandler(SerialPort_DataReceived);
OnTransferComplete();
TransferInProgress = false;
}
}
-Trevor
Have you thought about handling all of your data reception in one place? You could treat the commands you send as fire and forget, parsing the data received for the responses. If the responses do not have an identifying header and the ONLY way you know how to parse them is by knowing which command you sent and the length of the response, then you could keep track of the commands sent in a queue. The way that would work, is that in your Data Received handler you would check the queue of commands you're waiting on a response for, and then parse the data received like you do now.
Long story short, I would recommend handling all incoming data in one place.
My opinion if I'm understanding correctly would be to simply handle all your receiving data in the DataReceived handler or you have one other options.
If the data received between actual request isn't much you could just read the buffer and log it before transmitting your request. The serial driver receive buffer may be enough to store a small amount of data. Then send the request and read in just the response. This will probable be the easier method and simpler code.
I normally toggle a boolean. With subscribing/unsubscribing you run the risk of subscribing to the same event more than once. For instance in your code if OnTransferStarted() throws an exception you will subscribe twice to the DataReceived event.