I'm running into an interesting scenario when I'm trying to roll with .Net's SocketAsyncEventArgs. Namely, the fact that they can't seem to detect when a graceful remote socket shutdown has occurred.
Bit of background: I'm updating a legacy application from MFC to a .NET project, and my code needs to interface with all other legacy MFC code. In the legacy MFC code, the MFC backend automatically registers when a remote connection is gracefully closed with a FIN or RST signal. I've observed this behavior in action, and all the user can or needs to interact with is overloading the OnClose method that MFC provides.
I can't replicate that in C# or C++/CLI at the moment. My SocketAsyncEventArgs that I use to handle all receive operations looks like this:
static void AcceptHandler(System::IAsyncResult^ ar)
{
ServerSocket ^server = (ServerSocket ^)ar->AsyncState;
try
{
server->Socket = gcnew SocketMgr(server->listener->EndAcceptSocket(ar));
//pConnectionCb a function variable I use for updating the GUI when
//connection status changes. ReceiveDataHandler is another function
//variable for logging purposes.
if (server->pConnectionChangedCb)
{
server->pConnectionChangedCb(server->nID);
}
if (server->receiveDataHandler)
{
System::Net::Sockets::SocketAsyncEventArgs ^receiveArgs = gcnew System::Net::Sockets::SocketAsyncEventArgs();
receiveArgs->SetBuffer(server->readbuffer, server->nOffset, server->nBytesToGet - server->nOffset);
receiveArgs->Completed +=
gcnew System::EventHandler<System::Net::Sockets::SocketAsyncEventArgs ^>(server, &ServerSocket::IO_Completed);
server->Socket->ReceiveAsync(receiveArgs);
}
}
catch (System::Net::Sockets::SocketException ^e)
{
System::Windows::Forms::MessageBox::Show("OnAccept: Could not Accept, exception" + e->ErrorCode);
server->listener->EndAcceptSocket(ar);
}
}
void IO_Completed(System::Object ^sender, System::Net::Sockets::SocketAsyncEventArgs ^e)
{
if (!(e->SocketError == System::Net::Sockets::SocketError::Success))
{
kPrintf("Error.");
}
// determine which type of operation just completed and call the associated handler
switch (e->LastOperation)
{
case System::Net::Sockets::SocketAsyncOperation::Receive:
ProcessReceive(e);
break;
case System::Net::Sockets::SocketAsyncOperation::Send:
ProcessSend(e);
break;
default:
throw gcnew System::ArgumentException("The last operation completed on the socket was not a receive or send");
}
};
From what I've observed, when the remote socket ceases to exist, the SocketAsyncEventArgs object in the middle of the read exists in a state where it has not been completed, and will never be completed. As it fails to complete, IO_Completed will never be called, and I will be unable to use this to detect when a socket sends a graceful disconnect. So it can't be used.
...The only problem with this being, of course, that there's no OnRemoteClose (or equivalent) event for me to scribe to in Socket.Net.Sockets.Socket or in the SocketAsyncEventArgs, leaving me unable to detect a socket FIN or RST signal and keeping the socket open longer than expected. C# probably has a way around this, but I can't, for the life of me, find it. Anyone else wrestled with this before?
As it turns out, SocketAsyncEventArgs does record a graceful termination of any remote socket, regardless of client language. It does not expose the underlying TCP/IP events or anything similar, and instead just demonstrates the socket closure as an empty message sent.
My code, because of a PEBKAC error, was not receiving the empty 0-byte messages, and thus I could never 'see' the graceful shutdown.
(In the event anyone has this issue in the future, the problem is that the ProcessReceive method should have called ReceiveAsync to continue the loop after receiving its first signal, and it... wasn't, for reasons unrelated to the code.)
Related
According to this MSDN article, the socket.EndConnect method should be called in the AsyncCallback delegate provided in the original socket.BeginConnect call.
What is not clear (and the MSDN article is silent here) is whether EndConnect should be called after a timeout (and the socket is NOT connected). socket.EndConnect throws an exception in this case.
What is the proper procedure to follow after timeout? What are the consequences if EndConnect is not called (either after a successful connection or timeout without connection)? My code appears to work fine without calling EndConnect.
Here is some example code covering the main ideas in the question:
// Member variables
private static ManualResetEvent m_event;
private static Socket m_socket;
// Constructor of class
public static CMyTestConnection()
{
// Create an event that can be used to wake this thread when the connection completes
m_event = new ManualResetEvent(false);
}
private static void TestConnection(object sender, EventArgs e)
{
// Create connection endpoint
IPAddress ip = IPAddress.Parse("200.1.2.3"); // Deliberately incorrect
IPEndPoint ipep = new IPEndPoint(ip, 12345); // Also deliberately incorrect
EndPoint ep = (EndPoint)ipep;
// Attempt connection
m_event.Reset();
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
m_socket.BeginConnect(ep, ConnectCompletedCallback, m_socket);
}
private static void ConnectCompletedCallback(IAsyncResult ar)
{
// The asynchronous connection call has completed. Either we have connected (success) or
// timed out without being able to connect (failure).
m_event.Set();
Socket s = (Socket)ar.AsyncState;
if (s.Connected)
{
// Success...should EndConnect only be called here?
s.EndConnect(ar);
}
else
{
// Or should EndConnect also be called here (in a try/catch block)?
s.Close();
}
}
You invited me to this chat room. I am assuming this is the question to which you're referring, but it's hard for me to know for sure. Your message in the chat room doesn't have a real URL. I looked at your question links in your profile, and the only one I recognize is this one, which isn't closed at the moment. So there's no need to vote to re-open.
That said, the answer is still the same as already provided in the comments: you always call the EndXXX method when you've called BeginXXX (the few known exceptions don't apply here). There's nothing in your question, even after the recent edit, that would indicate what more you need.
You don't show how the timeout is implemented, so there's not even enough information to understand the code you posted. But if you are closing the socket, thus causing your callback to be invoked where EndConnect() will throw an exception, you should be calling EndConnect(). Failing to do so can potentially leave unmanaged resources dangling, which would then eventually be exhausted, or at the very least lead to performance problems.
The source code for .NET is readily available, so you can easily examine the implementation yourself. In the case of Socket.EndConnect(), we can see that for the current implementation, if the socket has already been disposed, all that happens is an exception is thrown. So, in theory, you could ignore sockets that have already been closed. I.e. this is an exception to the general concern about leaving resources dangling, in the specific "socket is already closed" scenario. But only if your timeout is implemented by closing the socket.
There are a couple of problems here though, related to race conditions:
Depending on how the timeout is implemented (you didn't share that part, so the question is still incomplete), you may have code that got as far as starting to call Socket.Close(), but which has not set the disposed flag. You'll be dealing with a connected socket that is about to become disconnected, and you need to have try/catch in place to handle that scenario.
Your callback assumes (it seems…again, there's not enough context in your question) that the Connected property is a reliable way to detect that there's been a timeout, but the Connected property could theoretically be reset to false after being connected, but before your callback gets to execute (e.g. some other type of error on the socket).
As far as the question of calling EndConnect() on a successful connection, that is much more clear: you must do so. If your code appears to work even though you haven't, that's just you getting lucky. We can see in the implementation that the EndConnect() method does useful work to configure the socket state when called after a successful connection, so if you fail to call the method, your socket will be in some indeterminate, incompletely configured state.
Naturally, if your timeout is implemented in some other way, where the socket is not closed before the callback is invoked, then you are in the same situation as if the connection had completed, and you must call EndConnect() to ensure that the appropriate cleanup and socket configuration occurs. I.e. that would be the same as the "successful connection" scenario.
The bottom line is, there is zero benefit to not calling EndConnect() in the event of a close/dispose-based timeout. The only hypothetical benefit might be that you can avoid try/catch, but you can't get away without that, because of the race conditions that exist. And if there's not such a timeout, not only is there not a benefit to not calling the method, there is real harm in failing to call it.
On a related note, there's not enough context in your question to make any real assessments of the rest of your code (since you didn't show how you're implementing the timeout, nor how the rest of your network I/O is handled). But I will say that in most cases, the "reuse address" option is unnecessary and should not be used. Most people wind up using it because they get into a situation where they can't start a new listening socket after they have somehow stopped a previous one, but that problem only comes up with the first listening socket and/or associated connected sockets have not been closed or shutdown correctly. The correct approach in that case is to handle the socket closure/shutdown correctly, not to add to the problem by setting "reuse address".
I'm trying to educate myself on the intricacies of reading from a NetworkStream, and understanding the various ways in which problems can occur. I have the following code:
public async Task ReceiveAll()
{
var ns = this.tcp.GetStream();
var readBuffer = new byte[1000];
while (true)
{
int bytesRead;
try
{
bytesRead = await ns.ReadAsync(readBuffer, 0, readBuffer.Length);
if (bytesRead == 0)
{
// Remote disconnection A?
break;
}
}
catch (IOException)
{
// Remote disconnection B?
break;
}
catch (ObjectDisposedException)
{
// Local disconnection?
break;
}
/*Do something with readBuffer */
}
}
I've marked three points in the code where the program says 'something has gone awry, there is no point continuing'.
The 'Local disconnection' isn't exactly something wrong, it will happen when I locally close the socket which is the only way to exit the loop under normal circumstances. I don't think anything else can cause this, so I think I'm safe to just swallow the exception.
The two 'Remote disconnection' points are what I'm not sure about. I know ReadAsync will return 0 if the connection is terminated remotely (A), but the IOException also seems to fire in some circumstances. If my remote client is a C# console, then closing the socket seems to make 'A' happen, and closing the console window seems to be make 'B' happen. I'm not sure I understand what the difference is between these scenarios?
Finally, a bit of a general question, but is there anything glaringly wrong with this bit of code or my above assumptions?
Thanks.
EDIT: In response to my use of ObjectDisposedException to abort out of the loop:
This is what my 'Stop' method looks like (from the same class as above):
public void Stop()
{
this.tcp.Close();
}
This causes the pending 'ReadAsync' to except with ObjectDisposedException. AFAIK there isn't any other way to abort this. Changing this to:
public void Stop()
{
this.tcp.Client.Shutdown(SocketShutdown.Both);
}
Doesn't appear to actually do anything to the pending call, it just continues waiting.
When NetworkStream returns 0, this means that the socket has received a disconnect packet from the remote party. This is how network connections are supposed to end.
The correct way to shut down the connection (especially if you are in a full-duplex conversation) is to call socket.Shutdown(SocketShutdown.Send) and give the remote party some time to close their send channel. This ensures that you receive any pending data instead of slamming the connection shut. ObjectDisposedException should never be part of the normal application flow.
Any exceptions thrown indicate that something went wrong, and I think it's safe to say you can no longer rely on the current connection.
TL;DR
I don't see anything wrong with your code, but (especially in full-duplex communication) I'd shut down the send channel and wait for a 0-byte packet to prevent receiving ObjectDisposedExceptions by default:
use tcp.Shutdown(SocketShutdown.Send) to tell the remote party you want to disconnect
your loop may still receive data that the remote party was sending
your loop will, if everything went right, then receive a 0-byte packet, indicating that the remote party is disconnecting
loop terminates the right way
you may want to decide to dispose the socket after a certain amount of time, if you haven't received the 0-byte packet
I was trying to develop a multicast receiver program and socket initialization was done as shown below:
public void initializeThread()
{
statuscheckthread = new Thread(SetSocketOptions);
statuscheckthread.IsBackground = true;
}
private void Form1_Load(object sender, EventArgs e)
{
rxsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
iep = new IPEndPoint(IPAddress.Any, 9191);
rxsock.Bind(iep);
ep = (EndPoint)iep;
initializeThread();
statuscheckthread.Start();
}
public void SetSocketOptions()
{
initializeThread(); //re-initializes thread thus making it not alive
while (true)
{
if (NetworkInterface.GetIsNetworkAvailable())
{
bool sockOptnSet = false;
while (!sockOptnSet)
{
try
{
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("224.50.50.50")));
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 64);
sockOptnSet = true;
}
catch
{
//Catch exception here
}
}
}
break; // Break out from loop once socket options are set
}
}
When my PC is not connected to a network, SetSocketOption method was throwing exception and even after network is connected,
I was unable to receive data because socket options are not set.
To avoid this I used a thread which runs in the background checking
for network availability and once network is available, it sets the socket options.
It works properly in some PC's but in some others, NetworkInterface.GetIsNetworkAvailable()
returned true before network got connected
(while network was being identified) .
So, to make sure Socket options are set, I used a bool variable sockOptnSet
which is set as
true if all the statements in the try block is executed as shown inside the method public void SetSocketOptions()
This program works fine in all PC's I tried, but I am doubtful about how much I can rely on this to work.
My questions are:
1) Is this good practice?
2) If not, what are the possible errors or problems it may cause? And how can I implement it in a better way?
Is this a good practice?
No, not a good practice. The vast majority of exceptions, including your first one, fall in the category of vexing exceptions. Software is supposed to work, worked well when you tested it, but doesn't on the user's machine. Something went wrong but you do not know what and there isn't anything meaningful that you can do about it. Trying to keep your program going is not useful, it cannot do the job it is supposed to do. In your case, there's no hope that the socket is ever going to receive data when there is no network. And, as you found out, trying to work around the problem just begets more problems. That's normal.
If this is bad practice, how can I implement it in a better way?
You need help from a human. The user is going to have to setup the machine to provide a working network connection. This requires a user interface, you must have a way to tell a human what he needs to do to solve your problem. You can make that as intricate or as simple as you desire. Just an error message, a verbatim copy of the Exception.Message can be enough. Writing an event handler for the AppDomain.CurrentDomain.UnhandledException event is a very good (and required) strategy. Microsoft spent an enormous amount of effort to make exception messages as clear and helpful as possible, even localizing them for you in the user's native language, you want to take advantage of that. Even if the exception message is mystifying, a quick Google query on the message text returns hundreds of hits. With this event handler in place, you don't have to do anything special. Your program automatically terminates and your user knows what to do about it.
You can certainly make it more intricate, you discovered that SetSocketOption() is liable to fail right after the network becomes available but works when you wait long enough. So this is actually an error condition that you can work around, just by waiting long enough. Whether you should write the code to handle this is something that you have to decide for yourself. It is something you write when you have enough experience with the way your program behaves, you never write it up front. Usually as a result from feedback from the users of your program.
Some good advice in the comments, lets' expand on it.
Firstly, I would put all this socket code in to its' own class, outside of the form. This makes it its' own entity and semantically easier to understand. This class could have a property Initialised, which is initially set to false. The first thing you do in your form is call an Initialise method on this class which attempts to set socket options and catches the relevant exceptions if the network is not available. If it is available, we set our Initialised property to true.
If not available, we set a single timeout (see System.Threading.Timer) that calls this same function (potentially with a retry count) after 'x' seconds. Once again we'll find ourselves back in this Initialise function, perhaps with a retry count mentioned at the beginning. Once again, if it is available, we're good - if not, set the timer again. Eventually, after 'x' retries if we're not initialised we can throw an exception or set some other failure property to indicate that we can't proceed.
Your Form class can periodically check (or hook in to an event) to determine whether the socket is now ready for communication. In case of failure you can gracefully quit out, or because our class is nice and abstracted, attempt to start the whole process again.
I have client-server application in C#.Net and for that I am using Tcp Socket. I have used following function to aggressive close of socket object.
void CloseSocket(Socket socket)
{
if(socket != null)
{
socket.ShutDown(ocketShutdown.Both);
socket.Close();
}
}
In Normal Condition this function works perfectly and my method returns with 0 bytes returned from Read function.
But whenever client process terminated by taskmanager server program blocks into read function of network stream.
How can I workaround this read blocking function ? I don't want to use AsyncRead function because whole project uses blocking strategy so write now I can't change it to Async pattern.
Thanks, in advance.
I'm assuming that what you are saying is that when the connection isn't closed cleanly by the client, the server can end up blocking at Read indefinitely, even if the client has actually terminated abruptly. If so: yes, that happens. So if you want to use the synchronous read methods, you should use timeouts, in particular ReceiveTimeout. If you have a multi-message protocol, it may be worthwhile adding some kind of heartbeat message periodically, to allow you to correctly identify true zombies from idle connections (for example: if you are sending a heartbeat every minute, and you haven't seen any activity on a connection for 3 minutes, hit it with a shovel).
**you can try this may help you**
public void close()
{
if(clientSocket != null )
{
sendCommand("QUIT");
}
cleanup();
}
private void cleanup()
{
if(clientSocket!=null)
{
clientSocket.Close();
clientSocket = null;
}
logined = false;
}
This is not a question about how to do this, but a question about whether it's wrong what I'm doing. I've read that it's not possible to detect if a socket is closed unexpectedly (like killing the server/client process, pulling the network cable) while waiting for data (BeginReceive), without use of timers or regular sent messages, etc. But for quite a while I've been using the following setup to do this, and so far it has always worked perfectly.
public void OnReceive(IAsyncResult result)
{
try
{
var bytesReceived = this.Socket.EndReceive(result);
if (bytesReceived <= 0)
{
// normal disconnect
return;
}
// ...
this.Socket.BeginReceive...;
}
catch // SocketException
{
// abnormal disconnect
}
}
Now, since I've read it's not easily possible, I'm wondering if there's something wrong with my method. Is there? Or is there a difference between killing processes and pulling cables and similar?
It's perfectly possible and OK to do this. The general idea is:
If EndReceive returns anything other than zero, you have incoming data to process.
If EndReceive returns zero, the remote host has closed its end of the connection. That means it can still receive data you send if it's programmed to do so, but cannot send any more of its own under any circumstances. Usually when this happens you will also close your end the connection thus completing an orderly shutdown, but that's not mandatory.
If EndReceive throws, there has been an abnormal termination of the connection (process killed, network cable cut, power lost, etc).
A couple of points you have to pay attention to:
EndReceive can never return less than zero (the test in your code is misleading).
If it throws it can throw other types of exception in addition to SocketException.
If it returns zero you must be careful to stop calling BeginReceive; otherwise you will begin an infinite and meaningless ping-pong game between BeginReceive and EndReceive (it will show in your CPU usage). Your code already does this, so no need to change anything.