Update: Updated code to working version.
Super! Just wanted to make sure I wasn't loosing my mind. I didn't even notice the stdC module before.
So here's what I've got so far. It isn't exactly working though; strangely, even though I have two sockets open, it reads the last bit of data that it sent. I probably missed something in conversion from using streams. Yes, this is a total hack.
Start two of them up, and enter
/connect localhost ####
Replacing the #### with the port number that the other instance reported.
Then enter anything (that doesn't start with a / or ^) in either window and it is
supposed to show up in the other.
peer.bmx
SuperStrict
Import "lib/strings.bmx"
Framework BRL.Map
Import BRL.Socket
Import BRL.StandardIO
Import BRL.Win32MaxGUI
Import BRL.Timer
Import BRL.Retro
Const BUF_SIZE:Int = 1024
AppTitle = "Peer"
Global EditWindow:TGadget
Global txtOutput:TGadget
Global eMsg:TGadget
Global btnSend:TGadget
Global Peers:TMap = CreateMap()
Global Sock:TSocket = CreateUDPSocket()
Global SendSock:TSocket = CreateUDPSocket()
Type TPeer
Field Name:String
Field IP:Int
Field Port:Int
Field Score:Int
Function Create:TPeer(Name:String, IP:Int, Port:Int, Score:Int)
Local Result:TPeer = New TPeer
Result.Name = Name
Result.IP = IP
Result.Port = Port
Result.Score = Score
Return Result
EndFunction
EndType
Function Broadcast(Msg:String)
Local p:Object
For p = EachIn Peers.Values()
Send(TPeer(p), Msg)
Next
EndFunction
Function Send(p:TPeer, Msg:String)
Local Buf:Byte Ptr = Msg.ToCString()
Local Sent:Int = sendto_(SendSock._socket, Buf, Msg.length, 0, p.IP, p.Port)
MemFree Buf
SocketListen(Sock)
EndFunction
Function gPrint(Msg:String)
Print Msg
AddTextAreaText(txtOutput, Msg + "~r~n")
EndFunction
Const Width:Int = 397
Const Height:Int = 335
EditWindow = CreateWindow(AppTitle, GadgetWidth(Desktop()) / 2 - Width / 2, ..
GadgetHeight(Desktop()) / 2 - Height / 2, ..
Width, Height, Desktop(), WINDOW_TITLEBAR|WINDOW_RESIZABLE)
txtOutput=CreateTextArea(8, 8, 376, 264, EditWindow, 0)
SetGadgetLayout txtOutput, 1, 1, 1, 1
eMsg = CreateTextField(8, 280, 320, 20, EditWindow)
SetGadgetLayout eMsg, 1, 1, 0, 1
SetGadgetText eMsg, "/connect localhost "
btnSend = CreateButton("Send", 336, 280, 48, 24, EditWindow, BUTTON_OK)
SetGadgetLayout btnSend, 0, 1, 0, 1
ActivateGadget(eMsg)
' Start listening on a new port
BindSocket(Sock, 0)
BindSocket(SendSock, SocketLocalPort(Sock) + 1)
SocketListen(Sock)
gPrint("Listening on " + SocketLocalPort(Sock))
SetGadgetText(EditWindow, AppTitle + " " + SocketLocalPort(Sock))
Local buf:Byte[BUF_SIZE]
Local receivedLen:Int
Local size:Int
Local fromIP:Int
Local fromPort:Int
Local Raw:String
Local Msg:String
Local Parms:String[]
Local Peer:TPeer
Local Peer2:TPeer
Local PeerKey:String
Local i:Int
Local Messages:String[]
' Allows us to use a single thread by interrupting WaitEvent() twice a second. This way
' we don't eat the CPU, but we can still poll the socket pretty well.
CreateTimer(8)
Repeat
Select WaitEvent()
Case EVENT_TIMERTICK
Case EVENT_GADGETACTION
Select EventSource()
Case btnSend
Msg = GadgetText(eMsg)
If Left$(Msg, 1) = "/" Then
Parms = Split(Msg, " ")
Select Parms[0]
Case "/connect"
If Parms.Length = 3 Then
' Ask existing peers to add this peer
Peer = TPeer.Create(Parms[1], HostIp(Parms[1]), Parms[2].ToInt(), 0)
MapInsert(Peers, Parms[1] + ":" + Parms[2], Peer)
' Send request to add this node to the newly added node
Send(Peer, "^add " + HostName$(SocketLocalIP(Sock)) + " " + SocketLocalPort(Sock))
' Ask for thier list of peers
Send(Peer, "^peers")
gPrint("Added " + Parms[1] + ":" + Parms[2] + " to peer list")
Else
gPrint("Usage: /connect host port")
EndIf
Case "/test"
Notify "Hello world"
Case "/quit"
Exit
EndSelect
Else
gPrint("< " + Msg)
Broadcast(Msg)
EndIf
SetGadgetText(eMsg, "")
ActivateGadget(eMsg)
End Select
Case EVENT_WINDOWCLOSE
Exit
EndSelect
size = SocketReadAvail(Sock)
If size Then
fromIP = 0
fromPort = 0
receivedLen = 0
receivedLen = recvfrom_(sock._socket, buf, size, 0, fromIP, fromPort)
Assert receivedLen > 0, "No data read"
Raw = String.FromBytes(buf, receivedLen)
fromPort = fromPort - 1
PeerKey = HostName$(fromIP) + ":" + fromPort
Peer = TPeer(MapValueForKey(Peers, PeerKey))
If Instr(Raw, "^") Then
Messages = Split(Raw, "^", "`")
If Messages.Length = 0 Then
Messages[0] = Raw
EndIf
For i = 0 To Messages.Length - 1
Msg = Messages[i]
If Msg <> "" Then
Parms = Split(Msg, " ")
If Parms.Length > 0 Then
Select Parms[0]
Case "peers"
' Send a list of add commands back to the asking peer
If Peer <> Null Then
For Peer2 = EachIn Peers.Values()
Send(Peer, "^add " + HostName$(Peer2.IP) + " " + Peer2.Port)
Next
' Ask the peers to add this new peer
Broadcast("^add " + HostName$(Peer.IP) + " " + Peer.Port)
Else
gPrint "Can't send peer list to unknown peer key: " + PeerKey
EndIf
Case "add"
' Don't want to add ourselves
If HostIp(Parms[1]) <> SocketLocalIP(Sock) And Parms[2].ToInt() <> SocketLocalPort(Sock) Then
Peer = TPeer.Create(Parms[1], HostIp(Parms[1]), Parms[2].ToInt(), 0)
MapInsert(Peers, Parms[1] + ":" + Parms[2], Peer)
gPrint("[Network] Added " + Parms[1] + ":" + Parms[2] + " to peer list")
EndIf
EndSelect
EndIf
EndIf
Next
Else
gPrint(PeerKey + "> " + Raw)
EndIf
Msg = ""
EndIf
Forever
CloseSocket(Sock)
CloseSocket(SendSock)
Can't find where I got this, it was on the forum somewhere, the function was originally called "SmartSplit" if I recall correctly:
lib/strings.bmx
Function Split:String[](str:String, dels:String, text_qual:String = "~q")
Local Parms:String[] = New String[1]
Local pPtr:Int = 0
Local chPtr:Int = 0
Local delPtr:Int = 0
Local qt:Int = False
Local str2:String = ""
Repeat
Local del:String = Chr(dels[delPtr])
Local ch:String = Chr(str[chPtr])
If ch = text_qual Then
If qt = False Then
qt = True
Else
qt = False
End If
End If
If ch = del Then
If qt = True Then str2:+ ch
Else
str2:+ ch
End If
If ch = del Or chPtr = str.Length - 1 Then
If qt = False Then
Parms[pPtr] = str2.Trim()
str2 = ""
pPtr:+ 1
Parms = Parms[..pPtr + 1]
If dels.length > 1 And delPtr < dels.length Then delPtr:+ 1
End If
End If
chPtr:+ 1
If chPtr = str.Length Then Exit
Forever
If Parms.Length > 1 Then Parms = Parms[..Parms.Length - 1]
Return Parms
End Function