OnMaximumConnections() event. If you are allowed to initiate a new connection then the first event that occurs on an outbound connection is
OnSocketCreated() this gives you a chance to access the raw
SOCKET that is associated with the connection. You should use this
SOCKET with care. The purpose of this callback is to allow you to customise the connection in ways that the framework doesn't explicitly support. You may wish to set certain socket options, for example. Next
OnPreOutgoingConnect() is called. This provides you with the socket object's user data, the address that you're connecting to and a pointer to any "user data" that was passed to the call to
Connect() or its equivalents. This is the point where you may wish to store any per-connection user data that was supplied by the code that is calling connect, in the socket. You would do this by adding opaque user data to the socket and then storing your per-connection data in the user data "slot" that you allocated. You can then access the user data from other callbacks. The attempt to connect to the remote server is now made. This can either be synchronous or asynchronous and it whichever it is it doesn't affect the callbacks that you will receive. If the connection is successfully established then you will get an
OnConnectionEstablished() event and if the connection fails you will get an
OnOutgoingConnectionFailed() event. Depending on whether the connection attempt was successful you will now either become active or simply move to the connection destruction callback.
OnConnectionEstablished() is called. The callback is the same as for outbound connections and for servers this would be where you might allocate and store any per-connection data that you have. You can see an example of this in the Thread Pool Large Packet Echo Server example. The connection is now active.
Read() or get a response of
TryRead() then you will receive, at any time thereafter, either a successful read completion,
OnReadCompleted(), or a read completion error,
OnReadCompletionError(). Likewise when you call
Write() or get a response of
TryWrite() then you will receive, at any time thereafter, either a successful write completion,
OnWriteCompleted(), or a write completion error,
OnWriteCompletionError(). Completion errors usually occur when there are insufficient system resources to complete the requested operation. You can't usually do much in these situations and the best way of dealing with them is to make sure that you never have to deal with them. To that end you should always install a connection limiter and also be aware of how many outstanding read and write requests you have on a connection at any one time, possibly even installing a
CFlowControlStreamSocketConnectionFilter if appropriate. Once you have issued a successful read or write you may also receive any of the connection termination or error callbacks.
OnConnectionClientClose() event. This occurs when a read returns successfully with zero bytes. You will only ever receive this callback once and once you have received it you wont get any more read completions or read completion errors no matter how many reads you have outstanding. Note that you can continue to send data to the remote end of the connection, the connection is now in the TCP Half Close state and you can continue to send, but not receive, until you either shutdown your send side of the connection or the connection is fully closed by the remote end of the connection. If the connection is reset for any reason (so not just a RST, any error that means the connection is no longer in a usable state) then you will receive the
OnConnectionReset() event. This means that the connection is no longer usable, any pending reads or writes will complete in error and attempts to initiate any new reads or writes will fail. You can cause a connection to be reset by calling
SOCKET. The connection exists until both ends of it are shutdown. The socket exists until it is closed. Closing a socket causes the connection to be shutdown. There is no explicit way for a user of the framework to close a socket; when the number of pending reads and the number of pending writes and the number of externally held references to a socket all reach zero the socket is automatically closed. If you wish to cleanly shut down the TCP connection you can use
Shutdown() and if you wish to reset the connection you can use
AbortConnection(). When a socket is closed you receive notification via the
OnConnectionClosed() event. This event is useful to help track active connections, perhaps for use in performance monitoring counters. This event is guaranteed to occur once for each connection for which there was a corresponding
OnSocketReleased() is called. This event is guaranteed to be the last event that you receive on a given connection. This is the ONLY place where you should clean up any per-connection user data that you may have stored in the socket as this is guaranteed to be the last event that will occur and when this event does occur you are guaranteed that no other events are still being processed and that no other code holds a reference to the connection. Note that you only get passed the user data and not the socket. Also note that you should not assume that you can use the address of the user data object for anything. Due to how the socket object's inheritance works this particular interface is different to the instance of
IIndexedOpaqueUserData that you can cast to from the
IStreamSocket interface that you're given in all of the other callbacks and the address of this user data will be different to the address of the user data passed in
OnPreOutgoingConnect() for the same socket. Note that you can, of course, access the socket's user data store via the interface, you just can't use the address of the interface for anything useful.
OnSocketReleased() event last and it will only occur when all other events have completed. You will also never receive a read or write completion or read or write error completion unless you have issued a read or a write call. For write calls you will ALWAYS receive either 1 write completion OR 1 error completion for each write that you issue. For read calls you will receive 1 read completion OR 1 error completion OR an
OnConnectionClientClose() event. Once you have received an
OnConnectionClientClose() event you will NOT receive any more completions for any outstanding reads that you might have, this is by design. For outbound connections you will recieve one call to
OnSocketCreated() followed by one call to
OnPreOutgoingConnect(). Note that as soon as you have issued a successful read or write call then it is possible that you could receive error, client disconnect, connection reset or connection closed events at any time. It is therefore advisable to allocate and attach any per-connection user data before issuing the first read or write call and only delete it in
AddRef() and then call into the socket at a later time, perhaps because another connection has done something that your held socket needs to know about or perhaps because of an external event. Either way if you have your own lock, A, and the socket has its own lock, B, then when you call into the framework because of an external event you may lock A and then B and when the framework calls into you it may cause you to lock B and then A. From such lock inversions are deadlocks born.
OnWriteCompletionError() - only for sequenced sockets
OnReadCompletionError() - only for sequenced sockets