Listening on sockets

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Listening on sockets

Sean P. DeNigris
Administrator
Why doesn't the second listen fail? And is there a way to fail if a port is already in use?

socket := Socket newTCP.
socket listenOn: 8088.

socket2 := Socket newTCP.
socket2 listenOn: 8088.

(socket isValid and: [ socket2 isValid ])
        ifTrue: [ self error: 'how can I be listening twice on the same port?!' ].

socket destroy.
socket2 destroy
Cheers,
Sean
Reply | Threaded
Open this post in threaded view
|

Re: Listening on sockets

Sven Van Caekenberghe

On 08 May 2012, at 19:24, Sean P. DeNigris wrote:

> Why doesn't the second listen fail? And is there a way to fail if a port is
> already in use?
>
> socket := Socket newTCP.
> socket listenOn: 8088.
>
> socket2 := Socket newTCP.
> socket2 listenOn: 8088.
>
> (socket isValid and: [ socket2 isValid ])
> ifTrue: [ self error: 'how can I be listening twice on the same port?!' ].
>
> socket destroy.
> socket2 destroy

Welcome to the strange world of Sockets, Sean ;-)

I have this test in Zn:

ZnSingleThreadedServer>>isListening
        "Return true when I have a valid server socket listening at the correct port"
       
        ^ self serverSocket notNil
                and: [ self serverSocket isValid and: [ self serverSocket localPort = self port ] ]

You can try, maybe it solves your 'problem'.

Sven

--
Sven Van Caekenberghe
http://stfx.eu
Smalltalk is the Red Pill


Reply | Threaded
Open this post in threaded view
|

Re: Listening on sockets

Sean P. DeNigris
Administrator
Sven Van Caekenberghe wrote
You can try, maybe it solves your 'problem'.
Thanks, Sven!

It works, but not for ip6. I guess this is more complicated than I thought. OS X lets me listen on a port that's already listening with ip6.

before I open my port, lsof shows:
java      ...  IPv6 ...  TCP *:port_number (LISTEN)

and after:
java      ...  IPv6 ...  TCP *:port_number (LISTEN)
CogVM  ...  IPv4 ...  TCP *:same_port_number!!! (LISTEN)

WTF!

For now, I'm using the ugly and non-portable:
        | lsof output p portsInUse |
        lsof := PipeableOSProcess waitForCommand: 'lsof -i -P -FnT'.
        output := p output.
        portsInUse := Set new.
        "Output is e.g. 'n*:52844TST=LISTENTQR=0TQS=0', where there is an lf between each field i.e. between the 4 at the end of the example's port number and the T, which starts the status info, and the next address"
        "So we're matching the colon before the port number, the number, lf (now we're in the status info), and then LISTEN before the next lf (when the next address begins)"
        output regex: '\:[[:digit:]]+\s\S*LISTEN' matchesDo: [ :e | portsInUse add: e allButFirst asNumber ].
        isAvailable := (portsInUse includes: my_port) not.

but it seems like it'd be nice to have a portable way, since it's generally useful, e.g. in zinc's tests, if I open port 1700 and run the server tests, they fail intermittently. With an appropriate comment to the ip6 limitation, I think this would be very useful:

  "Would this go in ZnNetworkingUtils? ZnUtils?"
  isPortAvailable: aNumber
        "Does not take ip6 into account i.e. if the port is already open with ip6, will indicate it is available"

        | socket isAvailable |
        socket := Socket newTCP listenOn: aNumber.
        isAvailable := socket isValid and: [ socket localPort = aNumber ].
        socket destroy.
        ^ isAvailable.

then the server tests could be more robust...
  ZnServerTests>>port
        ^ (1700 to: 1750) detect: [ :e | ZnNetworkingUtils isPortAvailable: e ].

and here's a test for the new functionality...
    testIsPortAvailable

        | availablePort utils socket |
        utils := ZnNetworkingUtils default.
        availablePort := (1700 to: 1800) detect: [ :e | utils isPortAvailable: e ].
       
        socket := Socket newTCP listenOn: availablePort.
        self deny: (utils isPortAvailable: availablePort).

        socket destroy.
        self assert: (utils isPortAvailable: availablePort).
Cheers,
Sean
Reply | Threaded
Open this post in threaded view
|

Re: Listening on sockets

Sven Van Caekenberghe
Thanks for the code, Sean, I will consider it.

Now, the tests can indeed 'fail' but on the explicitely tested condition that they are 'running alone on that port'. It is not really a failure, more a 'resource not available'.

The difference between the current approach and what you propose is like the difference between optimistic and pessimistic locking. And even in your solution there might be a very small time window were things still do wrong.

As for the actual functionality of the socket plugin code, let's say it could be modernized.

Sven

On 08 May 2012, at 21:01, Sean P. DeNigris wrote:

>
> Sven Van Caekenberghe wrote
>>
>> You can try, maybe it solves your 'problem'.
>>
>
> Thanks, Sven!
>
> It works, but not for ip6. I guess this is more complicated than I thought.
> OS X lets me listen on a port that's already listening with ip6.
>
> before I open my port, lsof shows:
> java      ...  IPv6 ...  TCP *:port_number (LISTEN)
>
> and after:
> java      ...  IPv6 ...  TCP *:port_number (LISTEN)
> CogVM  ...  IPv4 ...  TCP *:same_port_number!!! (LISTEN)
>
> WTF!
>
> For now, I'm using the ugly and non-portable:
> | lsof output p portsInUse |
> lsof := PipeableOSProcess waitForCommand: 'lsof -i -P -FnT'.
> output := p output.
> portsInUse := Set new.
> "Output is e.g. 'n*:52844TST=LISTENTQR=0TQS=0', where there is an lf
> between each field i.e. between the 4 at the end of the example's port
> number and the T, which starts the status info, and the next address"
> "So we're matching the colon before the port number, the number, lf (now
> we're in the status info), and then LISTEN before the next lf (when the next
> address begins)"
> output regex: '\:[[:digit:]]+\s\S*LISTEN' matchesDo: [ :e | portsInUse add:
> e allButFirst asNumber ].
> isAvailable := (portsInUse includes: my_port) not.
>
> but it seems like it'd be nice to have a portable way, since it's generally
> useful, e.g. in zinc's tests, if I open port 1700 and run the server tests,
> they fail intermittently. With an appropriate comment to the ip6 limitation,
> I think this would be very useful:
>
>  "Would this go in ZnNetworkingUtils? ZnUtils?"
>  isPortAvailable: aNumber
> "Does not take ip6 into account i.e. if the port is already open with ip6,
> will indicate it is available"
>
> | socket isAvailable |
> socket := Socket newTCP listenOn: aNumber.
> isAvailable := socket isValid and: [ socket localPort = aNumber ].
> socket destroy.
> ^ isAvailable.
>
> then the server tests could be more robust...
>  ZnServerTests>>port
> ^ (1700 to: 1750) detect: [ :e | ZnNetworkingUtils isPortAvailable: e ].
>
> and here's a test for the new functionality...
>    testIsPortAvailable
>
> | availablePort utils socket |
> utils := ZnNetworkingUtils default.
> availablePort := (1700 to: 1800) detect: [ :e | utils isPortAvailable: e ].
>
> socket := Socket newTCP listenOn: availablePort.
> self deny: (utils isPortAvailable: availablePort).
>
> socket destroy.
> self assert: (utils isPortAvailable: availablePort).
>
> --
> View this message in context: http://forum.world.st/Listening-on-sockets-tp4618129p4618367.html
> Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
>


Reply | Threaded
Open this post in threaded view
|

Re: Listening on sockets

Douglas Brebner
In reply to this post by Sean P. DeNigris
On 08/05/2012 20:01, Sean P. DeNigris wrote:

> It works, but not for ip6. I guess this is more complicated than I thought.
> OS X lets me listen on a port that's already listening with ip6.
>
> before I open my port, lsof shows:
> java      ...  IPv6 ...  TCP *:port_number (LISTEN)
>
> and after:
> java      ...  IPv6 ...  TCP *:port_number (LISTEN)
> CogVM  ...  IPv4 ...  TCP *:same_port_number!!! (LISTEN)
>
> WTF!
>
>

Hmm, aren't IPv4 and IPv6 ports completely separate namespaces?