Socket Questions

Monkey Forums/Monkey Programming/Socket Questions

Tibit(Posted 2013) [#1]
If I do:

_socket=New Socket( "datagram" )
_socket.Bind("localhost", port)
Print "Socket bound to " + _socket.LocalAddress.ToString()

Output is:
Socket bound to 127.0.0.1:51464

However if I do:

_socket=New Socket( "datagram" )
_socket.Bind("", port)
Print "Socket bound to " + _socket.LocalAddress.ToString()

Output is:
Socket bound to 0.0.0.0:51464

Is not "" that same as localhost? or is a "slower" dns lookup happening here behind the scenes? Does this mean that "" returns directly, and if using localhost, 127.0.0.1 or any other address I should use the Async version?

Also what happens when I close the App without closing the socket? Should it close or be left open?


marksibly(Posted 2013) [#2]
> Socket bound to 0.0.0.0:51464

Not totally sure myself, but I think binding a socket to "localhost" means it can only be reached via a connection to "localhost", while binding a socket to "" means it can be reached via a connection to "localhost", "192.168.0.1" etc, basically 'any address that successfully identifies this machine'.

My advice would be to bind to "" (which is what I/bmx have always done in the past). I did in fact almost just leave it as just Bind( port ) but all targets support binding to host too, so I left it in there in case someone who knows what they're doing needs it.

As for BindAsync, I'm not sure what this is for either, but this is how winrt wants you to bind so I left it in there in case we need it later for some reason.

> Also what happens when I close the App without closing the socket? Should it close or be left open?

I'm assuming socket will auto-close when app exits but am yet to confirm this on all targets.


DruggedBunny(Posted 2013) [#3]
Looked this up out of curiosity. It seems that "" is treated by bind() as constant INADDR_ANY (0), which is considered to be IP address 0.0.0.0. Therefore "localhost" and "" should certainly be expected to produce different results:

http://publib.boulder.ibm.com/infocenter/zvm/v6r1/index.jsp?topic=/com.ibm.zvm.v610.edclv/bind.htm


I think binding a socket to "localhost" means it can only be reached via a connection to "localhost", while binding a socket to "" means it can be reached via a connection to "localhost", "192.168.0.1" etc, basically 'any address that successfully identifies this machine'



I get the same impression from the above link:


A couple things to notice: we used socket.gethostname() so that the socket would be visible to the outside world. If we had used s.bind(('localhost', 80)) or s.bind(('127.0.0.1', 80)) we would still have a “server” socket, but one that was only visible within the same machine. s.bind(('', 80)) specifies that the socket is reachable by any address the machine happens to have.



... and that's interesting, as it seems to suggest that binding to "localhost" should mean that *only* a client running on the same machine as the server could access it... which I never knew! It also suggests a missed trick in BlitzMax, as this would presumably mean you could create a more secure server on your local network if you could specify "localhost" or a "192.168.x.x"-style address, preventing any outside access.


marksibly(Posted 2013) [#4]
> It also suggests a missed trick in BlitzMax, as this would presumably mean you could create a more secure server on your local network if you could specify "localhost" or a "192.168.x.x"-style address, preventing any outside access.

Indeed!


Tibit(Posted 2013) [#5]
Great!

I found another thing I was confused about. It seems that when I call SendAsync that SendOnComplete and ReceiveOnComplete both called, even though no receive has happened?

Here is a runnable example:
[monkeycode]
Import mojo

Import brl.socket

Class PingClientUDP Implements IOnSendToComplete, IOnReceiveFromComplete
Private

Field _socket:Socket
Field _receivedata:= New DataBuffer(1024)
Field _senddata:= New DataBuffer(1024)
Field _localAddress:SocketAddress
Field _serverAddress:SocketAddress
Field _pingID:Int = 1
Field _verbose:Bool = True

Public
Method New(serverAddress:String, serverPort:Int)
_serverAddress = New SocketAddress(serverAddress, serverPort)
_localAddress = New SocketAddress()'"localhost", 0)
_socket=New Socket( "datagram" )
If Not _socket.Bind("", 0) Print "Bind failed"
If _verbose Then Print "Socket bound to " + _socket.LocalAddress.ToString()
_socket.ReceiveFromAsync(_receivedata, 0, _receivedata.Length, _localAddress, Self)
End

'Call this every few sec to send data to server
Method SendPingToServer:Void()
'DebugStop()
Local dataToSend:String = "Ping" + _pingID
Local n:= _senddata.PokeString(0, dataToSend)
_socket.SendToAsync(_senddata, 0, n, _serverAddress, Self)
_pingID += 1
End

Private
Method OnSendToComplete:Void( data:DataBuffer,offset:Int,count:Int,address:SocketAddress,source:Socket )
If _verbose Then Print "Sent ping [" + (_pingID - 1) + "] to server [" + address.Host + ":" + address.Port + "]"
End

Method OnReceiveFromComplete:Void( data:DataBuffer,offset:Int,count:Int,address:SocketAddress,source:Socket )
If count >= 0
If _verbose Then Print "Client Received [" + data.PeekString(offset, count).Length + "] bytes: " + data.PeekString(offset, count)
Else
If _verbose Then Print "Received nothing..."
End
_socket.ReceiveFromAsync _receivedata, 0, _receivedata.Length, _localAddress, Self
End

End

Class MyPingClientApp Extends App

Field _client:PingClientUDP

Field _delta:Float = 1.0 / 60.0
Field _clientSendTimer:Float

Method OnCreate()
_client = New PingClientUDP("localhost", 53568)
_client.SendPingToServer()

SetUpdateRate 60
End

Method OnUpdate()
UpdateAsyncEvents

Local clientSendFrequency:Int = 1
_clientSendTimer += _delta
If _clientSendTimer > clientSendFrequency
_client.SendPingToServer()
_clientSendTimer = 0
End
End

Method OnRender()
Cls
DrawText "UDP Client Online", 0, 0
End

End

Function Main()

New MyPingClientApp

End
[/monkeycode]

Notice that Received is called each send. Or is this intended?

I really liked before when I could tell the receive to "wait" for X number of bytes. Makes the packetizer code very simple. However the current "return each frame" approach is maybe more familiar since that basically how they do it in C# or nodejs.


marksibly(Posted 2013) [#6]
Hi,

Not quite sure what you mean. When I run, I get:

Socket bound to 0.0.0.0:61657
Sent ping [1] to server [127.0.0.1:53568]
Sent ping [2] to server [127.0.0.1:53568]
Sent ping [3] to server [127.0.0.1:53568]


Which looks right because there's no 'ping' server running at port 53568 as far as I know...?

If there was, then I would imagine you'd get an OnReceiveFromComplete when data is sent - because the server will be 'echoing' the data and your client is ReceveAsync-ing all the time.

> I really liked before when I could tell the receive to "wait" for X number of bytes. Makes the packetizer code very simple.

This could easily be added via a Read/ReadAsync 'flags' parameter - or perhaps a Socket.SetOptions method. It's probably easier for the Socket code to do this when necessary than it would be for app code, so it's probably a good idea. Send on the other hand should probably *always* send all bytes, correct?


marksibly(Posted 2013) [#7]
> I really liked before when I could tell the receive to "wait" for X number of bytes.

Ok, I've added ReceiveAll and ReceiveAllAsync to v71b - same params as plain Receive/ReceiveAsync (including same OnComplete handlers), only they wait until 'count' bytes have arrived (or the stream is closed) instead of 'at least 1' bytes.


Tibit(Posted 2013) [#8]
Cool :)