[bmx] UDP Networking Class by JoshK [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:43

Previous topic - Next topic

BlitzBot

Title : UDP Networking Class
Author : JoshK
Posted : 1+ years ago

Description : This is a base networking lib on top of which more advanced features can be added.

Code :
Code (blitzmax) Select
SuperStrict

Import brl.socket
Import brl.map

Type TNetNode

Const LOCALIP:Int=2130706433

Global map:TMap=New TMap

Field socket:TSocket
Field ip:Int
Field port:Int

Method SendNetMessage:Int(recipient:TNetNode,data:Byte Ptr=Null,size:Int=0)
If ip<>LOCALIP Return False
Return sendto_(socket._socket,data,size,0,recipient.ip,recipient.port)
EndMethod
 
Method GetNetMessage:TNetMessage()
If ip<>LOCALIP Return Null
Local message:TNetMessage,size:Int,ip:Int,port:Int,buf:Byte[]
size=socket.ReadAvail()
If size
message=New TNetMessage
message.data=New Byte[size]
recvfrom_(socket._socket,message.data,size,0,ip,port)
message.sender=Create(ip,port)
message.size=message.data.length
Return message
EndIf
EndMethod
 
Method WaitNetMessage:TNetMessage(timeout:Int=0)
Local time:Int
Local message:TNetMessage
time=MilliSecs()
Repeat
message=GetNetMessage()
If message Return message
If timeout
If MilliSecs()-time>timeout Return Null
EndIf
Delay 1
Forever
EndMethod

Function Create:TNetNode(ip:Int,port:Int=41000)
Local netnode:TNetNode
netnode=Find(ip,port)
If netnode Return netnode
netnode=New TNetNode
netnode.ip=ip
netnode.port=port
If ip=LOCALIP
netnode.socket=CreateUDPSocket()
If Not BindSocket(netnode.socket,port) Return Null
EndIf
map.insert netnode,netnode
Return netnode
EndFunction

Function Find:TNetNode(ip:Int,port:Int=41000)
Local netnode:TNetNode
netnode=New TNetNode
netnode.ip=ip
netnode.port=port
netnode=TNetNode(map.valueforkey(netnode))
Return netnode
EndFunction

Method Compare:Int(o:Object)
Local netnode:TNetNode
netnode=TNetNode(o)
If netnode.ip>ip Return 1
If netnode.ip<ip Return -1
If netnode.port>port Return 1
If netnode.port<port Return -1
Return 0
EndMethod

EndType

Type TNetMessage

Field sender:TNetNode
Field data:Byte[]
Field size:Int

Method ToString:String()
Return String.fromCString(data)
EndMethod

EndType

Function CreateNetNode:TNetNode(ip:Int=TNetNode.LOCALIP,port:Int=41000)
Return TNetNode.Create(ip,port)
EndFunction

Function SendNetMessage:Int(sender:TNetNode,recipient:TNetNode,data:Byte Ptr,size:Int)
Return sender.SendNetMessage(recipient,data,size)
EndFunction

Function GetNetMessage:TNetMessage(recipient:TNetNode)
Return recipient.GetNetMessage()
EndFunction

Function WaitNetMessage:TNetMessage(recipient:TNetNode,timeout:Int=0)
Return recipient.WaitNetMessage(timeout)
EndFunction


Comments :


JoshK(Posted 1+ years ago)

 Here is an example of usage:
SuperStrict

Framework brl.system

Import "network.bmx"

AppTitle=""

Local client:TNetNode[2]

client[0]=CreateNetNode(HostIp("127.0.0.1"),80)
client[1]=CreateNetNode(HostIp("127.0.0.1"),81)

SendNetMessage(client[0],client[1],"Hello!".toCstring(),7)

Local message:TNetMessage=WaitNetMessage(client[1],1000)
If message Notify message.ToString()



BlitzSupport(Posted 1+ years ago)

 Looks handy, thanks.


Chroma(Posted 1+ years ago)

 Very clever and streamlined.  So how many players could this handle without threading?


BlitzSupport(Posted 1+ years ago)

 Anyone tried using this? The example program creates both client and server, so can 'see' both TNetNodes.However, if you put the server in one program and the client in another, each program has no knowledge of the other TNetNode information.I've just done a little hack that manually creates a TNetNode to represent the opposite party, setting only the other party's IP and port, and seems to work, but does this seem like a sensible approach?SERVER:
Import "udpnet.bmx"

Local server:TNetNode = CreateNetNode (HostIp ("127.0.0.1"), 80)

' Hack representing client:

Local client:TNetNode = New TNetNode
client.ip = HostIp ("127.0.0.1")
client.port = 81

If server

Local netmsg:TNetMessage
Local msg:String

Repeat

netmsg = WaitNetMessage (server, 30)

If netmsg

msg = netmsg.ToString ()

Print msg

EndIf

Until msg = "quit"

EndIf
CLIENT:
Import "udpnet.bmx"

Local client:TNetNode = CreateNetNode (HostIp ("127.0.0.1"), 81)

' Hack representing server:

Local server:TNetNode = New TNetNode
server.ip = HostIp ("127.0.0.1")
server.port = 80

If client

Graphics 1024, 768

Repeat

If KeyDown (KEY_LEFT)
SendNetMessage (client, server, "LEFT".ToCString (), 4)
Else
If KeyDown (KEY_RIGHT)
SendNetMessage (client, server, "RIGHT".ToCString (), 5)
EndIf
EndIf

Cls

Flip

Until KeyHit (KEY_ESCAPE)

EndIf
I've also taken out the references to LOCALIP, which I assume were just for testing purposes on a single PC, as they'd prevent sending over a network in a real situation:UDPNET.BMX:
SuperStrict

Import brl.socket
Import brl.map

Type TNetNode

'Const LOCALIP:Int=2130706433

Global map:TMap=New TMap

Field socket:TSocket
Field ip:Int
Field port:Int

Method SendNetMessage:Int(recipient:TNetNode,data:Byte Ptr=Null,size:Int=0)
' If ip<>LOCALIP Return False
Return sendto_(socket._socket,data,size,0,recipient.ip,recipient.port)
EndMethod
 
Method GetNetMessage:TNetMessage()
' If ip<>LOCALIP Return Null
Local message:TNetMessage,size:Int,ip:Int,port:Int,buf:Byte[]
size=socket.ReadAvail()
If size
message=New TNetMessage
message.data=New Byte[size]
recvfrom_(socket._socket,message.data,size,0,ip,port)
message.sender=Create(ip,port)
message.size=message.data.length
Return message
EndIf
EndMethod
 
Method WaitNetMessage:TNetMessage(timeout:Int=0)
Local time:Int
Local message:TNetMessage
time=MilliSecs()
Repeat
message=GetNetMessage()
If message Return message
If timeout
If MilliSecs()-time>timeout Return Null
EndIf
Delay 1
Forever
EndMethod

Function Create:TNetNode(ip:Int,port:Int=41000)
Local netnode:TNetNode
netnode=Find(ip,port)
If netnode Return netnode
netnode=New TNetNode
netnode.ip=ip
netnode.port=port
'If ip=LOCALIP
netnode.socket=CreateUDPSocket()
If Not BindSocket(netnode.socket,port) Return Null
'EndIf
map.insert netnode,netnode
Return netnode
EndFunction

Function Find:TNetNode(ip:Int,port:Int=41000)
Local netnode:TNetNode
netnode=New TNetNode
netnode.ip=ip
netnode.port=port
netnode=TNetNode(map.valueforkey(netnode))
Return netnode
EndFunction

Method Compare:Int(o:Object)
Local netnode:TNetNode
netnode=TNetNode(o)
If netnode.ip>ip Return 1
If netnode.ip<ip Return -1
If netnode.port>port Return 1
If netnode.port<port Return -1
Return 0
EndMethod

EndType

Type TNetMessage

Field sender:TNetNode
Field data:Byte[]
Field size:Int

Method ToString:String()
Return String.fromCString(data)
EndMethod

EndType

Function CreateNetNode:TNetNode(ip:Int,port:Int=41000)
Return TNetNode.Create(ip,port)
EndFunction

Function SendNetMessage:Int(sender:TNetNode,recipient:TNetNode,data:Byte Ptr,size:Int)
Return sender.SendNetMessage(recipient,data,size)
EndFunction

Function GetNetMessage:TNetMessage(recipient:TNetNode)
Return recipient.GetNetMessage()
EndFunction

Function WaitNetMessage:TNetMessage(recipient:TNetNode,timeout:Int=0)
Return recipient.WaitNetMessage(timeout)
EndFunction



BlitzSupport(Posted 1+ years ago)

 I set this up in two separate MaxIDE instances, side-by-side, so I can see the server Output tab while 'playing' in the client (left/right keys), but I notice some garbage coming into the output now and then, eg:LEFT
LEFTT
LEFTTo
LEFTT
I know packets can be lost, etc (though presumably this would be almost non-existent on localhost), but any ideas as to where the extra characters might be coming from? I don't even send a lower-case 'o' from the client!


Henri(Posted 1+ years ago)

 Hello,size variable seems to remain constant, so if you change this method from the UDPNET.bmx:Type TNetMessage

Field sender:TNetNode
Field data:Byte[]
Field size:Int

Method ToString:String()
Return String.fromCString(data)
EndMethod

EndType
...to this:Type TNetMessage

Field sender:TNetNode
Field data:Byte[]
Field size:Int

Method ToString:String()
Local s:String
For Local b:Byte = EachIn data
s:+ Chr(b)
Next
Return s
EndMethod

EndType
...it seems to work.-Henri


BlitzSupport(Posted 1+ years ago)

 Hi Henri,Many thanks for taking a look, that does indeed seem to work -- much appreciated!


Hardcoal(Posted 1+ years ago)

 Josh , thanks for simplifying this


Hardcoal(Posted 1+ years ago)

 since i dont know so much about networking ive started investigating josh code.I would like to increase the commands in this example.for example how do you check if a port is already occupied.and how do you close a client.Anyway ill get it one way or the otherits all matter of practice right. [/i]