Sockets API a bit confusing ...
Monkey Forums/Monkey Programming/Sockets API a bit confusing ...
| ||
| Or maybe it's just me. But it's really unclear if a parameter is used to be written to or actually a read only parameter for the method. Specifically I am doing some UDP stuff and yes I have looked at the udp_echoserver example but that confuses -.- Has somebody maybe a really simple UDP client server sample? |
| ||
| I did get a Client/Server TCP system working between multiple Android devices. However, I'm having trouble because I can't seem to tell when a Client disconnects, and I can't restart or stop the server, and it seems like everything just crashes when something goes wrong and I can't seem to figure out how to do any error handling. I think I'm just not very familiar with the Async stuff. At any rate, here is the code that works for my little tcp client/server. (Plan to do the same with UDP because I read it's a little more forgiving) Lots of bugs in this, and it's just a simple test, and you have to know the IP address and Port of the device that will be the server because I can't figure out how to get the IP of the device so I know how to connect to another device automatically. I'm only posting this because I hope someone can lead us in the right direction, and by no means is this production ready or bug free...
#rem
Connect a client to a server.
Send the mouse or touch coords back and forth and display a circle in that spot on both screens.
Hardcode the server's IP into the code below.
Run on the Client Device
Run on the Server Device
Touch the word SERVER at the top on the Server device.
Touch the word CLIENT at the top on the Client device.
Start clicking/touching the screens and see the dots move around.
Works (kind-of) for me :/
#end
Import mojo
Import brl.socket
Const SERVER_IP:String = "192.168.1.129" ' Change to your Server device's IP
Const SERVER_PORT:Int = 12345 ' Change to your own
Const SOCKET_NONE = 0
Const SOCKET_SERVER = 1
Const SOCKET_CLIENT = 2
Global msgStr:String = ""
Global serverStr:String = "0,0"
Global clientStr:String = "0,0"
Global isServer:Int = 0
Global socketType:Int = SOCKET_NONE
Global hasMsg:String = "No messages"
Global killClient:Int = 0
Global clickX:Int = 0
Global clickY:Int = 0
Global serverX:Int = 0
Global serverY:Int = 0
Global clientX:Int = 0
Global clientY:Int = 0
Class TcpEchoServer Implements IOnAcceptComplete
Method New( port:Int )
_socket=New Socket( "server" )
If Not _socket.Bind( "",port ) Then
hasMsg = "Server IP Bind Failed"
Return
Else
hasMsg = "Bind Successful"
End If
_socket.AcceptAsync( Self )
End
Private
Field _socket:Socket
Field clients:TcpEchoServerClient[10]
Field onClient:Int = 0
Method OnAcceptComplete:Void( socket:Socket,source:Socket )
If Not socket Then
hasMsg = "Server Client Connection Accept Error" ' "Accept Error"
Return
End If
hasMsg = "TcpEchoServer: Accepted client connection"
clients[onClient] = New TcpEchoServerClient( socket )
onClient = onClient + 1
End
End
Class TcpEchoServerClient Implements IOnSendComplete,IOnReceiveComplete
Method New( socket:Socket )
_socket=socket
_socket.ReceiveAsync _data,0,_data.Length,Self
End
Private
Field _socket:Socket
Field _data:=New DataBuffer( 1024 )
Method OnReceiveComplete:Void( data:DataBuffer,offset:Int,count:Int,source:Socket )
If Not count Or killClient = 1 Then
hasMsg = "TcpEchoServer: Closing client connection"
_socket.Close()
killClient = 0
Return
Endif
'Print "received " + offset + ", " + count + ", " + data.PeekString( offset,count )
clientStr = data.PeekString( offset,count )
Local sstr:String[2]
sstr = clientStr.Split(",")
If sstr.Length=2 Then
clientX = Int(Float(sstr[0]))
clientY = Int(Float(sstr[1]))
End If
msgStr = serverStr+":"+clientStr
data.PokeString(0,serverStr)
_socket.SendAsync data,0,serverStr.Length(),Self ' offset
End
Method OnSendComplete:Void( data:DataBuffer,offset:Int,count:Int,source:Socket )
_socket.ReceiveAsync _data,0,_data.Length,Self
End
End
Class MyApp Extends App Implements IOnConnectComplete,IOnSendComplete,IOnReceiveComplete
Field _server:TcpEchoServer
Field _socket:Socket
Field _data:=New DataBuffer( 1024 )
Field onSocketTry:Int = 0
Method OnCreate()
socketType = SOCKET_NONE
msgStr = serverStr + "," + clientStr
SetUpdateRate 60
End
Method OnUpdate()
UpdateAsyncEvents
If MouseHit() Then
Local mx:Float = MouseX()
Local my:Float = MouseY()
If my < 30 Then
If mx >= 0 And mx <= 92 Then
If socketType = SOCKET_NONE Then
socketType = SOCKET_SERVER
_server=New TcpEchoServer( SERVER_PORT )
End If
Elseif mx > 92 And mx<= 192 Then
If socketType = SOCKET_NONE Then
socketType = SOCKET_CLIENT
_socket=New Socket( "stream" )
_socket.ConnectAsync SERVER_IP,SERVER_PORT,Self
End If
End If
End If
Elseif MouseDown() Then
clickX = MouseX()
clickY = MouseY()
If socketType=SOCKET_CLIENT Then
clientX = clickX
clientY = clickY
clientStr = clickX + "," + clickY
msgStr = serverStr+":"+clientStr
Elseif socketType=SOCKET_SERVER Then
serverX = clickX
serverY = clickY
serverStr = clickX + "," + clickY
msgStr = serverStr+":"+clientStr
End If
End If
End
Method OnRender()
Cls
If socketType=SOCKET_SERVER Then
DrawTextNormal "SERVER ON PORT " + SERVER_PORT,10,10
Elseif socketType=SOCKET_CLIENT Then
DrawTextNormal "CLIENT",10,10
Else
DrawTextNormal "Server Client",10,10
End If
DrawTextNormal msgStr,10,30
DrawTextNormal hasMsg, 10, 50
DrawTextNormal MouseX() + "," + MouseY(), 10, 70
If serverX>0 Or serverY>0 Then
SetColor 255,0,0
DrawCircle serverX,serverY,20
End If
If clientX>0 Or clientY>0 Then
SetColor 0,255,0
DrawCircle clientX,clientY,20
End If
End
Method SendMore:Void()
' we send our coords to the server
Local n:=_data.PokeString( 0, clickX + "," + clickY )
_socket.SendAsync _data,0,n,Self
End
Method OnConnectComplete:Void( connected:Bool,source:Socket )
If Not connected Then
hasMsg = "Error Connecting"
_socket.Close()
Return
Else
SendMore
End If
End
Method OnSendComplete:Void( data:DataBuffer,offset:Int,count:Int,source:Socket )
_socket.ReceiveAsync _data,0,_data.Length,Self
End
Method OnReceiveComplete:Void( data:DataBuffer,offset:Int,count:Int,source:Socket )
' The server always just sends back its coords, attach ours
serverStr = data.PeekString( offset,count )
Local sstr:String[2]
sstr = serverStr.Split(",")
If sstr.Length=2 Then
serverX = Int(Float(sstr[0]))
serverY = Int(Float(sstr[1]))
End If
msgStr = serverStr + ":" + clientStr
SendMore
End
End
Function Main()
New MyApp
End
|
| ||
| Thanks for the code ben. I still can't wrap my head around it. Especially the OnSomething/Async Methods. Also while UDP seems to be the way to go for fast paced games, it seems you can get through with tcp if your game is not in that category. At least you don't have to worry about the order of packets that way. |
| ||
| Agreed. UDP looks really similar to TCP setup as far as the EchoServer goes, I'm trying to convert this to UDP for my game now too. What confuses me is the creation of the Client inside the server. It looks like it creates the Client variables, and somehow those persist forever, even if the client creates the TcpEchoServerClient as a local variable. It makes no sense to me either, but this is as far as I've gotten so far. My other posts about the matter have had no responses, so I fear it's something very few have worked with yet. |
| ||
| > Or maybe it's just me. But it's really unclear if a parameter is used to be written to or actually a read only parameter for the method. If you mean the 'address' parameter of SendTo and ReceiveFrom, then: * The address you pass to SendTo must be initialized before the call - this is how SendTo knows where to send the message. * ReceiveFrom fills in the address with the address the message came from. The address you pass to ReceiveFrom can either be a new 'empty' address object created with 'New SocketAddress', or an existing address object you don't need any more. The example UdpEchoServer code uses a single SocketAddress object for all reads/writes. When a message arrives at the server and the server OnReceiveFromComplete is called, it simply uses the same address object to 'echo' back the message with SendToAsync. > What confuses me is the creation of the Client inside the server. It looks like it creates the Client variables, and somehow those persist forever, even if the client creates the TcpEchoServerClient as a local variable. This is just the 'magic' of garbage collection at work. The New() method of TcpEchoServerClient calls ReceiveAsync before returning, so the async system is effectively 'hanging on' to the object for you. It needs to hang on to it in order to be able to send it the receive result later. Ditto, once a result has been received, the OnReceive code replies with SendAysnc, which will also keep the client 'alive'- and so on etc. Eventually, when the external client closes the socket, ReceiveAsync will receive a count of '0' indicating 'end of stream', in which case it DOESN'T call receive (or send!), it just returns and will eventually be garbage collected. In a real world program, you may well want to build a proper list of connected clients into the server though. |