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. |