I have a socket listening on a port. I do an accept and get a socket
which is dedicated to a client which made an inbound connection. This latter socket is the one I'm interested in. If the client closes this socket I expected isActive (sent at the server end) to return false, but it returns continues to return true. I have waited for some time to see if the socket eventually times out and then starts returning false to isActive, but this does not seem to happen. So, does anyone know when a socket created from an accept will start returning isActive as false if no action is taken in the server but the socket is closed by the client? Thanks, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
Bruce Badger wrote:
> I have a socket listening on a port. I do an accept and get a socket > which is dedicated to a client which made an inbound connection. This > latter socket is the one I'm interested in. > > If the client closes this socket I expected isActive (sent at the > server end) to return false, but it returns continues to return true. > I have waited for some time to see if the socket eventually times out > and then starts returning false to isActive, but this does not seem to > happen. > > So, does anyone know when a socket created from an accept will start > returning isActive as false if no action is taken in the server but > the socket is closed by the client? > #isActive doesn't seem to tell you much, at a quick glance I'd say it only indicates that an OS level handle has been allocated for the socket- whether connected or not. If the socket has been closed properly you cannot read any more data from it, IOW if you do a #next on its readStream you should get an EndOfStream exception if it is closed (however your process will block if the connection is still open but no data is available yet). If you need to use a polling paradigm you are probably best helped by sending #atEnd to the readStream (but who likes polling, I prefer to implement a process that does #next and block under normal conditions but breaks on an exception when the stream is closed). Note that at the OS level TCP will not recognize closed sockets if the network is faltering (net split) or if the client closed the socket 'improperly' (by using #shutdown: or due to an OS crash), the server side TCP layer will not (can not!) recognize these conditions *unless* it sends some data to the client. So to detect such situations if your connection has been idle for a while simply send some 'do nothing' data to the client, after a little while the TCP layer will recognize that packets fail to arrive at the client and mark the server side as closed (you will get an OsTransferFault exception or similar while trying to send this data). Note that the client need not send a response, TCP already does that at the lower layers. In our applications we send such 'keep alive' data in both directions every minute or so so both client and server can recognize net split at times where the connection is otherwise idle. HTH, Reinout ------- |
On 20/11/2007, Reinout Heeck <[hidden email]> wrote:
> If the socket has been closed properly you cannot read any more data > from it, IOW if you do a #next on its readStream you should get an > EndOfStream exception Well, yes, but this is also true in other situations like the other end does not want to send any data at that moment. I guess the only option is to wait a reasonably long time for more data to turn up and then assume that nothing more is going to happen. And not expect >>isActive to help. Thanks for the tips. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
The only way to recognize a brolen connection is to send data (at least
under Windows) - we had the same problem under .NET. This works - even if the receiver buffer is full and can not receive any further data. Marten Bruce Badger schrieb: > On 20/11/2007, Reinout Heeck <[hidden email]> wrote: > > >> If the socket has been closed properly you cannot read any more data >> from it, IOW if you do a #next on its readStream you should get an >> EndOfStream exception >> > > Well, yes, but this is also true in other situations like the other > end does not want to send any data at that moment. > > I guess the only option is to wait a reasonably long time for more > data to turn up and then assume that nothing more is going to happen. > And not expect >>isActive to help. > > Thanks for the tips. > > All the best, > Bruce > |
In reply to this post by Bruce Badger
On Nov 20, 2007, at 12:10 AM, Bruce Badger wrote: > On 20/11/2007, Reinout Heeck <[hidden email]> wrote: > >> If the socket has been closed properly you cannot read any more data >> from it, IOW if you do a #next on its readStream you should get an >> EndOfStream exception > > Well, yes, but this is also true in other situations like the other > end does not want to send any data at that moment. No, in that case calling #next: would just block until data is available. > > I guess the only option is to wait a reasonably long time for more > data to turn up and then assume that nothing more is going to happen. > And not expect >>isActive to help. In our applications we usually have a write process and a read process that act as a bridge between the read/write streams on a socket and SharedQueues that hold message objects. Since no application logic is implemented in these two processes it is straightforward to implement the strategy I outlined earlier. The read process repeatedly does #next until an exception is raised which signals that the connection is closed or broken. To ensure detection of net split the send process (on the same side of the connection) will send a no-op message every minute or so, so even if the read process is already blocking in #next it will receive an exception in the case of netsplit. No wait logic needs to be implemented. Cheers, Reinout ------- |
In reply to this post by marten
On Nov 20, 2007, at 7:37 AM, Marten Feldtmann wrote: > The only way to recognize a brolen connection is to send data (at > least under Windows) - we > had the same problem under .NET. This works - even if the receiver > buffer is full and can not > receive any further data. Correct, this is a property of TCP and hence this behavior should be the same on every OS. R - |
In reply to this post by Reinout Heeck
Reinout and Marten,
Thanks for your help with my socket question. I still can't figure out how I can detect when a client has closed their socket. I run the following simple test case in a workspace and use the halts to let me mess with the client. I just use telnet as the client. serverSocket := SocketAccessor newTCPserverAtPort: 12345. serverSocket listenFor: 10. connectionSocket := serverSocket accept. buffer := ByteArray new: 100. octetsTransferred := connectionSocket readInto: buffer startingAt: 1 for: 100. readOctets := buffer copyFrom: 1 to: octetsTransferred. 1 halt: 'Now close the client connection'. octetsTransferred := connectionSocket readInto: buffer startingAt: 1 for: 100. 1 halt: 'Any exception? Nope.'. actuallyRead := connectionSocket writeFrom: readOctets. 1 halt: 'Any exception? Nope.' Reinout suggested: "If the socket has been closed properly you cannot read any more data from it, IOW if you do a #next on its readStream you should get an EndOfStream exception" ... but this is not the case above. I can read from the socket without getting an EndOfStream exception even when the telnet program is no longer running. The resulting octetsTransfered is zero, but I would expect that since no octets were available to be read. Reinout further suggested: "To ensure detection of net split the send process (on the same side of the connection) will send a no-op message every minute or so". I have a write happening in the above example, but still no exception. BTW, I am running both the client and server on localhost, so there is no net splitting or other such things happening here AFAIK. Also, with this writing suggestion, what is a no-op? Surely this is protocol dependent, unless there is some out-of-band way sending something over the socket that the client does not get to see? Marten suggested: "The only way to recognize a broken connection is to send data (at least under Windows)". Well, I'm running on Linux and the write in the above example completes without an exception, thought it does return 0 to say that nothing was actually written. So, perhaps with this example I can put the question as: How can I modify the above code to detect that the telnet client has been terminated and the socket closed? Thanks, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
Further to this if I run this test script and don't kill the telnet
client, and I instead run through the script and then close the connection socket in VW then ... the telnet client closes immediately saying that the socket has been closed by the other end. If the telnet client can do this then surely we can do it in VW. But how? The specific steps I take in this case are: o Run the script in VW o Start the telnet client using "telnet localhost 12345" o enter some text in telnet and hit return, e.g. "abcdef<cr>" o note that the first read in the script completes and I get "abcdef" in readOctets o enter some more text in telnet and hit return o note that the second read completes o the script writes the initial readOctets and I see "abcdef" on the telnet client console o In VW I close connectionSocket with "connectionSocket close" o telnet immediately reports "Connection closed by foreign host." No measurable delay at all. Thanks, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
At 02:12 PM 12/3/2007, Bruce Badger wrote:
>Further to this if I run this test script and don't kill the telnet >client, and I instead run through the script and then close the >connection socket in VW then ... the telnet client closes immediately >saying that the socket has been closed by the other end. Yes. >If the telnet client can do this then surely we can do it in VW. I seem to recall that windows is in the way, here. But I'm not certain - it might be VW. Seems more likely. Ok, no help there. It's been too long. Is your telnet client on Windows? If so, the problem is VW. If not, do you get the same result with telnet on windows? If so, the problem is VW. Otherwise, the problem is windows. >But how? > >The specific steps I take in this case are: > >o Run the script in VW >o Start the telnet client using "telnet localhost 12345" SYN packet -> server client <- SYN ACK packet >o enter some text in telnet and hit return, e.g. "abcdef<cr>" text packet (w/ ACK) -> server server gives text to script >o note that the first read in the script completes and I get "abcdef" >in readOctets >o enter some more text in telnet and hit return text packet -> server server gives text to script >o note that the second read completes >o the script writes the initial readOctets server gets text from script. client <- text packet (w/ ACK) > and I see "abcdef" on the >telnet client console >o In VW I close connectionSocket with "connectionSocket close" client <-- a FIN packet (with no data) client TCP notices the FIN in the arriving packet, and changes state to FIN-WAIT, which means that no more data will arrive - not ever. But the *connection* is still open in the other direction. Several possibilities here: 1) Socket becomes or is made aware of this state change, and a) always sends an INFORMATIVE signal to the application, or b) signalling is optional - can be enabled/disabled at the socket. when disabled, socket behavior is same as case (2). For the cases where a signal is sent, the application may freely ignore this signal, in which case application behavior is same as case (2). 2) Socket, and application, remain oblivious to the state change. The only way an application can detect the situation is to try and read some data. In your case, telnet is getting the signal, and responding to it by closing the other half of the connection. (Telnet *assumes* there is no point in sending more data to a server which cannot respond. Note that there are cases where the connection *should* remain half open - but telnet isn't one of them.) If this happens with a windows telnet client, then my recollection was wrong - and the VM is either not configuring the socket or not passing the signal up to image level. ACK FIN, -cstb >o telnet immediately reports "Connection closed by foreign host." No >measurable delay at all. > >Thanks, > Bruce >-- >Make the most of your skills - with OpenSkills >http://www.openskills.org/ |
In reply to this post by Bruce Badger
Hi Bruce,
> Thanks for your help with my socket question. I still can't figure > out how I can detect when a client has closed their socket. > > I run the following simple test case in a workspace and use the halts > to let me mess with the client. I just use telnet as the client. [code snipped] I see that you are using the Posix API (buffers). I framed my answers in terms of the 'Smalltalk API' (streams), I'll continue doing that here. Is there a good reason you are using the lower-level Posix API? The code: serverSocket := SocketAccessor newTCPserverAtPort: 12345. serverSocket listenFor: 10. connectionSocket := serverSocket accept. rs := connectionSocket readStream. ws := connectionSocket writeStream. [ [ [ [ c := rs next. c = Character cr ] whileFalse:[ Transcript print: c; flush]. " 1 halt: 'Close the client'." ws nextPutAll: 'ping'; cr; flush. ] repeat ] on: EndOfStreamNotification do: [ :ex | Transcript cr; nextPutAll: 'Close detected on read';cr;flush] ] on: OsError do: [:ex | Transcript cr; nextPutAll: 'Close detected on write';cr;flush] This will detect a close on read, if you uncomment the #halt it will detect a close on write. > > Reinout suggested: "If the socket has been closed properly you cannot > read any more data > from it, IOW if you do a #next on its readStream you should get an > EndOfStream exception" > ... but this is not the case above. I can read from the socket > without getting an EndOfStream exception even when the telnet program > is no longer running. The resulting octetsTransfered is zero, but I > would expect that since no octets were available to be read. Keep in mind that you are talking Posix, while I'm sticking to streams: The above code definitely sees an EndOfStreamNotification but note that I had to re-implement #nextLine to see it. It turns out that the #nextLine implementation would block in #atEnd and never call #next on a closed connection. Thus the end of stream notification would not be risen. This shows that relying on EndOfStreamNotification can be cumbersome. On the other hand, for a simple request-response protocol the #nextLine implementation could be used as a template: [rs atEnd] whileFalse:[ self readRequestFrom: rs. self writeResponseOn: ws] wrap the whole thing in an OsError exception handler and you should have a simple but pretty robust (wrt to network events) server. > > Reinout further suggested: "To ensure detection of net split the send > process (on the same side of the connection) will send a no-op message > every minute or so". I have a write happening in the above example, > but still no exception. My code snippet demonstrates exception on write if the #halt is uncommented. > Also, with this writing suggestion, what is a > no-op? Surely this is protocol dependent, Correct. > unless there is some > out-of-band way sending something over the socket that the client does > not get to see? TCP does have one OOB message called the urgent pointer (UP), I wouldn't consider using it for this purpose unless I'm forced to, perhaps when the protocol is fixed and has no request that can stand in for a no-op or an echo. HTH, Reinout ------- |
Reinout, Bruce,
At 06:47 AM 12/4/2007, Reinout Heeck wrote: >Hi Bruce, >[code snipped] > >I see that you are using the Posix API (buffers). >I framed my answers in terms of the 'Smalltalk API' (streams), >I'll continue doing that here. Is there a good reason you are >using the lower-level Posix API? > >The code: > >serverSocket := SocketAccessor newTCPserverAtPort: 12345. >serverSocket listenFor: 10. > >connectionSocket := serverSocket accept. >rs := connectionSocket readStream. >ws := connectionSocket writeStream. > >[ [ [ [ c := rs next. > c = Character cr > ] whileFalse:[ > Transcript print: c; flush]. >" 1 halt: 'Close the client'." > ws nextPutAll: 'ping'; cr; flush. > ] repeat > ] on: EndOfStreamNotification do: [ :ex | > Transcript cr; nextPutAll: 'Close detected on read';cr;flush] >] on: OsError do: [:ex | > Transcript cr; nextPutAll: 'Close detected on write';cr;flush] > > > >This will detect a close on read, if you uncomment the #halt it will detect a close on write. Right - but you *must* literally send or receive data to get the signal, which highlights the problem. >>Reinout suggested: "If the socket has been closed properly you cannot >>read any more data >>from it, IOW if you do a #next on its readStream you should get an >>EndOfStream exception" >>... but this is not the case above. I can read from the socket There's a technicality here, Bruce. You aren't "reading" from the socket. you're using a buffered read request, which skips the actual "read from the socket" part if no characters are available. >>without getting an EndOfStream exception even when the telnet program >>is no longer running. The resulting octetsTransfered is zero, but I >>would expect that since no octets were available to be read. > >Keep in mind that you are talking Posix, while I'm sticking to streams: >The above code definitely sees an EndOfStreamNotification but note that I had to re-implement #nextLine to see it. It turns out that the #nextLine implementation would block in #atEnd and never call #next on a closed connection. Thus the end of stream notification would not be risen. >This shows that relying on EndOfStreamNotification can be cumbersome. And this is the result of an incomplete implementation, somewhere. The socket should get a signal from the TCP layer when a FIN packet is sent -- #on: WriteChannelClosed and when a FIN packet arrives -- #on: ReadChannelClosed *not* when the last octet of data is consumed. The socket should consume these signals, and record the fact that the associated channel is now closed. Thereafter, #next would raise an exception when reading from an emptyANDclosed channel, #nextPut: would raise an exception when writing to a closed channel, and #atEnd would answer queue atEnd and: [self channelClosed] instead of just queue atEnd which is meaningless for sockets. It is still unclear to me whether it is just a bug in the VW implementation, or the bug is there because the windows API doesn't provide access to "channel closed", and VW would therefore be unable to provide an implementation of portable behavior if it included the "channel closed" logic. >... > >>unless there is some >>out-of-band way sending something over the socket >> that the client does not get to see? > >TCP does have one OOB message called the urgent pointer (UP), >I wouldn't consider using it for this purpose unless I'm forced to, >perhaps when the protocol is fixed and has no request that can stand >in for a no-op or an echo. The purpose of the UP is to jump over data that is queued up in another layer but not yet consumed - for things like ctrl-S/ctrl-Q flow control, which are trying to start/stop that other layer before it actually consumes its way to the control-character. For channelEnd signalling, we want the higher layer to finish consuming the data before it marks the channel as closed, so using UP wouild not be a good solution, in most cases. The effect would be similar to forcing a connectionReset. Regards, -cstb |
On 04/12/2007, cstb <[hidden email]> wrote:
> It is still unclear to me whether it is just > a bug in the VW implementation, > or the bug is there because the windows API > doesn't provide access to "channel closed", > and VW would therefore be unable to provide > an implementation of portable behavior if it > included the "channel closed" logic. Just on this one factor - all my work is being done in a Debian GNU/Linux environment using VW 7.5. I run all this in VMWare on my MacBooc pro which is using OS/X Tiger. No Microsoft software in the mix at all. -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by jas
Reinout, you're right. I am trying to use sockets in a Posix like
way. I tend to prefer to follow standards where I can, and that would be Posix for sockets. Another issue is that I am trying to use sockets in a non-blocking fashion (e.g. so I can retain control in my main threads using timeouts), but VW really only seems to work in a blocking way. Looking at the spec I guess what I wanted was to be able to recv from the socket and get an error response of EAGAIN if the socket was up but no data was available and ECONNRESET (or some such) if the socket was closed. But I can't have that, so I'm now looking at simulating a non-blocking socket by having a separate thread sucking on the socket and having that notify me if the socket is closed vs. the socket being OK but no data available. ... and that seems to work, but more testing is needed. And just in general ... "sockets" as such is just an API. It would be good, IMO, if that API were simply projected into Smalltalk behaviour such that calls in the spec matched methods in Smalltalk and that the semantics (e.g. blocking vs. non-blocking, exceptions corresponding to error conditions etc.) were preserved too. But this is just me daydreaming ... unless the ANSI standard work can address this, perhaps? Reinout, cstb and Marten. Thanks for you help with this. :-) All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
Bruce Badger wrote:
> Reinout, you're right. I am trying to use sockets in a Posix like > way. I tend to prefer to follow standards where I can, and that would > be Posix for sockets. Another issue is that I am trying to use > sockets in a non-blocking fashion (e.g. so I can retain control in my > main threads using timeouts), but VW really only seems to work in a > blocking way. > Well now I'm starting to get confused by this thread.. because VW is meant to opens sockets in non-blocking mode. You can see it clearly in the windows ntio.c for primitive 670 and the big comment that says "Default behavior of the VM should be NON-BLOCKING!" and the following line that enables non-blocking for all new sockets. What platforms are you running on? It's possible one of the platforms isn't enabling non-blocking? Michael |
On 19/12/2007, Michael Lucas-Smith <[hidden email]> wrote:
> What platforms are you running on? It's possible one of the platforms > isn't enabling non-blocking? This is all being done on Debian Linux tested in VW 7.4.1, 7.5 and 7.5.1. Given that you expect sockets in VW to be non blocking, under what circumstances can one get an EAGAIN and how does it look? EAGAIN is what the POSIX docs say indicates that a read returned nothing but that the socket is still OK. I don't see anything like that in VW. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
OSErrorHolder class>>restartableError: code
"Test if this is a process block or restartable error." ^#('EWOULDBLOCK' 'EAGAIN' 'EINTR' 'EALREADY' 'ENODATA' 'ERROR_UNEXP_NET_ERROR' 'ERROR_TOO_MANY_POSTS') includes: (self errorSymbolFor: code) Looks like sockets will wait them out then raise OSErrorHolder needRetrySignal, -Boris -- +1.604.689.0322 DeepCove Labs Ltd. 4th floor 595 Howe Street Vancouver, Canada V6C 2T5 http://tinyurl.com/r7uw4 [hidden email] CONFIDENTIALITY NOTICE This email is intended only for the persons named in the message header. Unless otherwise indicated, it contains information that is private and confidential. If you have received it in error, please notify the sender and delete the entire message including any attachments. Thank you. > -----Original Message----- > From: Bruce Badger [mailto:[hidden email]] > Sent: Wednesday, December 19, 2007 9:56 AM > To: VWNC, > Subject: Re: Socket isActive until what or when? > > On 19/12/2007, Michael Lucas-Smith <[hidden email]> wrote: > > What platforms are you running on? It's possible one of the platforms > > isn't enabling non-blocking? > > This is all being done on Debian Linux tested in VW 7.4.1, 7.5 and 7.5.1. > > Given that you expect sockets in VW to be non blocking, under what > circumstances can one get an EAGAIN and how does it look? EAGAIN is > what the POSIX docs say indicates that a read returned nothing but > that the socket is still OK. I don't see anything like that in VW. > > All the best, > Bruce > -- > Make the most of your skills - with OpenSkills > http://www.openskills.org/ |
On 19/12/2007, Boris Popov <[hidden email]> wrote:
> OSErrorHolder class>>restartableError: code > "Test if this is a process block or restartable error." > > ^#('EWOULDBLOCK' 'EAGAIN' 'EINTR' 'EALREADY' 'ENODATA' > 'ERROR_UNEXP_NET_ERROR' 'ERROR_TOO_MANY_POSTS') includes: (self > errorSymbolFor: code) > > Looks like sockets will wait them out then raise OSErrorHolder > needRetrySignal, A non blocking socket should not wait anything out. It should immediately return with either octets or an EAGAIN. I've never seen an EAGAIN, though I have had sockets in a situation where I *should* get one if the socket were really non-blocking. In practice, VW sockets always seems to be blocking. My work-around is working fine, BTW, so this is not a "problem" for me now. It's just something that perhaps could be improved, even if only in terms of clarity. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
In reply to this post by Michael Lucas-Smith-2
I think there's confusion over what it means to be blocking.
VW sockets are non-blocking in terms of the underlying socket API. So
doing socket operations doesn't cause the VM to wait (although DNS lookup
does). They are blocking operations in terms of VW processes, so your
process waits. What Bruce means, I think, is that he doesn't want his
Smalltalk-level processes to block.
At 10:31 AM 12/19/2007, Michael Lucas-Smith wrote: Bruce Badger wrote: --
Alan Knight [|], Cincom Smalltalk Development
|
In reply to this post by Michael Lucas-Smith-2
On 20/12/2007, Alan Knight <[hidden email]> wrote:
> ... What > Bruce means, I think, is that he doesn't want his Smalltalk-level processes > to block. Right. I acknowledge that a socket read (c.f. recv()) in VW does not block the whole VM. It only blocks the thread (VW Process) that made the call. ... and this means that what we have in VW works, but is a not-quite-like-anything-else implementation of sockets which introduces all kinds of exciting opportunities to confuse people (e.g. me). So what I would wish for, as Alan says, is for the current thread not to block when I read but rather return -1 as the number of octets read and throw an EAGAIN exception (or something recognisably like that). I don't currently have a problem I can't work around, but it would be a Good Thing (IMO) if the Smalltalk (i.e. not just VW) implementation of sockets mapped onto some public specification, e.g.: http://linux.die.net/man/2/recv Abstractions to make sockets easier to use could (and should!) sit on top of a basic sockets layer but I think we should always be able to understand what is happening in terms of the spec. All the best, Bruce -- Make the most of your skills - with OpenSkills http://www.openskills.org/ |
> ... and this means that what we have in VW works, but is a > not-quite-like-anything-else implementation of sockets which > introduces all kinds of exciting opportunities to confuse people (e.g. > me). > I agree with that. That's the Smalltalk-80 design of streams. I think we could iterate to a new streaming design at some point. I don't think this is something can have a 'quick fix'.. Mind you, you can call the read:into:startingAt: calls yourself and catch the EAGAIN exception.. it is Smalltalk code that does the blocking so you can simply not call that Smalltalk code. Michael |
Free forum by Nabble | Edit this page |