I’m updating one of the pieces of sample code that we put on CodeProject a couple of years ago. A client wants a version of the COM object socket server that has been built with the latest version of our server framework and which supports outbound connections as well as inbound. The work has been going reasonably well until this afternoon when I found I had a problem shutting down the socket connections that I’d opened.
The latest version of our framework supports ConnectEx for asynchronous outbound socket connection requests. Rather than have your code wait around for a connect to complete you can have it complete asynchronously and have the framework tell you when it has completed, or failed. This works well and was a good fit for the COM object. You call
Connect() on the object and when the connection completes it calls you back.
Late this afternoon the code had got to the point where I could create a noddy little proxy server in VB. This created outbound connections in response to incoming connections and routed the data from one to another. To test the code I took our echo server sample and its test harness and stuck the VB proxy in-between. The test harness connects, pumps loads of data to the echo server, reads it all back and checks that what was sent is the same as what was received. The test ran well but there was a problem with the proxy not being able to pass a half-close on to the echo server. When the test harness has finished sending all of its test data it shuts down the send side of its socket connection and I wanted the proxy to pass this half-close on to the echo server and it didn’t.
It took me a while to work out what was going on. First I tinkered with the way the proxy handled the half-close. I assumed that I was doing something wrong and simply not calling
shutdown() correctly, or calling it on the wrong socket, or something. Eventually I traced down from VB into the C++ and watched the socket shutdown code do its stuff. Later I actually paid attention to what it was doing and realised that the
shutdown() call was actually failing, the low-level socket server framework error callback hadn’t yet been routed up to VB so I wasn’t seeing the message. When I finally did see the message it didn’t make sense. The error I was getting from `shutdown() was WSANOTCONN (10057) which was obviously wrong because the socket was clearly connected as data was flowing through it.
I spent some time assuming that somehow I’d screwed up somewhere and the socket I was passing to
shutdown() had been trashed or changed or something. Eventually I was happy that it everything was as it should be and that for some reason
shutdown() seemed to think that the socket was already closed.
I looked at the ConnectEx docs again and actually read them this time.
Halfway down the docs there’s this paragraph:
When the ConnectEx function successfully completes, socket handle s can be passed to the following functions only:
ReadFile WriteFile send or WSASend recv or WSARecv TransmitFile closesocket
shutdown() isn’t present in the above list. I’ve had issues with the documentation for these Winsock “Ex” functions before. When I was originally writing about AcceptEx over on CodeProject the docs on MSDN didn’t include WSASend and WSARecv in the AcceptEx version of that list, they do now… However, since shutdown` isn’t on the list and doesn’t appear to work I don’t think that I can use the ‘documentation bug’ excuse for its lack of appearance in this instance.
The docs go on to say that:
When the ConnectEx function returns, the socket s is in the default state for a connected socket. The socket s does not enable previously set properties or options until SO_UPDATE_CONNECT_CONTEXT is set on the socket. Use the setsockopt function to set the SO_UPDATE_CONNECT_CONTEXT option.
err = setsockopt( s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0 );
Which is similar to how
AcceptEx() works… It doesn’t say anywhere that you must do this and most things seem to work if you don’t… I decided to try adding some code to make this call once the socket connection completed. Once I did
shutdown started to work as expected again…
I think it would be useful if the MSDN docs on the Microsoft Winsock extension functions was updated so that it was a little more precise about exactly what you need to do and which bits are optional and what the consequences of the optionality are.