OK good. I think it would be best to add some examples to the image: https://pharo.fogbugz.com/f/cases/14275/Add-some-decent-TCP-UDP-examples-and... I will do that in the coming days.
On 21 Oct 2014, at 10:20, Annick Fron <list@afceurope.com> wrote:
OK super, this example works. I got confused because I did not understand that you had 2 variables, buffer passed as argument and result as return.
Annick Le 21 oct. 2014 à 09:53, Sven Van Caekenberghe <sven@stfx.eu> a écrit :
The buffer that you supply to #receiveUDPDataInto: is either a String or a ByteArray, preallocated to a certain size. It gets filled with the incoming data part of the datagram. It should be large enough, it never overflows, if it is too small, you simply miss data (it is lost). If you preallocate N bytes, the result, the incoming data, might be less.
The result from #receiveUDPDataInto: is a 4 element array, that I described before. The fields you are looking for are in the second (the ip address of the sender, a 4 element byte array) and in third (the port of the sender) slot. The first element is the actual number of bytes read. You need to copy from 1 to that size out of the buffer.
The other fields that are in a datagram, outside the user data, are not accessible. You don't need them, unless you are implementing a TCP/IP stack yourself.
Did you try this example ?
"An UDP echo server on port 6666" [ Socket newUDP in: [ :socket | | loop buffer result input | buffer := String new: 256. loop := true. socket setPort: 6666. [ loop ] whileTrue: [ [ result := socket receiveUDPDataInto: buffer. result first > 0 ] whileFalse: [ (Delay forMilliseconds: 10) wait ]. input := buffer copyFrom: 1 to: result first. socket sendUDPData: input toHost: result second port: result third. (input beginsWith: #quit) ifTrue: [ loop := false ] ]. socket closeAndDestroy ] ] forkAt: Processor userBackgroundPriority named: 'UDP echo server'.
"Any message sent gets echoed back" Socket newUDP in: [ :socket | | buffer result | socket sendUDPData: 'testing ', 99 atRandom asString toHost: NetNameResolver localHostAddress port: 6666. buffer := String new: 256. [ result := socket receiveUDPDataInto: buffer. result first > 0 ] whileFalse: [ (Delay forMilliseconds: 10) wait ]. socket closeAndDestroy. { result. buffer. buffer copyFrom: 1 to: result first } ].
"Send quit to stop the server" Socket newUDP in: [ :socket | | buffer result | socket sendUDPData: 'quit' toHost: NetNameResolver localHostAddress port: 6666. buffer := String new: 256. [ result := socket receiveUDPDataInto: buffer. result first > 0 ] whileFalse: [ (Delay forMilliseconds: 10) wait ]. socket closeAndDestroy. { result. buffer. buffer copyFrom: 1 to: result first } ].
In a terminal (OSX or Linux), you can send a message like this:
$ nc -u 127.0.0.1 6666 foo bar foo bar quit quit
On 21 Oct 2014, at 09:31, Annick Fron <list@afceurope.com> wrote:
Sven,
I have put a halt in your sync method, but receiveUDPData comes with 64 bytes in your app, whereas you have provisioned 48 bytes. So since you start data at 33, I guess what you receive is the usual datagram IP header on 16 bytes, then the UDP header on 16 bytes. http://fr.wikipedia.org/wiki/Internet_Protocol#mediaviewer/File:Ipv4_header.... http://fr.wikipedia.org/wiki/User_Datagram_Protocol
If this is correct, I should see the source IP address on 4 bytes starting at byte 9, and the total length should be on 4 bytes starting at byte 5, and not at byte 1.
Annick
Le 20 oct. 2014 à 17:29, Sven Van Caekenberghe <sven@stfx.eu> a écrit :
Annick,
On 20 Oct 2014, at 17:12, Annick Fron <list@afceurope.com> wrote:
Thank you but your explanation does not explain what is the type (signed unsigned) and the size of each header component. Thus it is impossible to work with that.
I think you are making this way more difficult than it is.
ZTimestampSNTPClient>>#sync contains a simple, runnable example of using datagrams (with a busy wait blocking the calling thread, which is only good for something simple, because normally datagrams are asynchronous, but that is another story).
What you get back from #receiveUDPDataInto: is an Array with 4 elements:
- number of bytes received, Integer - address of sender, ByteArray (use NetNameResolver if necessary) - port of sender, Integer - whether more datagrams are available for reading, Boolean
The data is in the reallocated buffer that you supplied, aStringOrByteArray.
This is all perfectly useable at the Pharo level.
The question about signed/unsigned sounds like a C question, for which this is the wrong list, no ? I am sure it is quite easy to find C code that works with datagrams on your platform. A byte array is an unsigned char array as far as I remember ...
What you put inside the datagram, is up to you and your application.
HTH,
Sven
Annick
Le 17 oct. 2014 à 19:09, Sven Van Caekenberghe <sven@stfx.eu> a écrit :
Yes, you have to use the methods in the protocol 'datagrams' of Socket.
When you receive a datagram, the IP:PORT of the other party is included, see #receiveUDPDataInto:'c comment.
It is all a bit low level, but it works well.
On 17 Oct 2014, at 18:15, Annick Fron <list@afceurope.com> wrote:
Thank you, but I am confused. I have checked your NTP code, and it uses sendUDPData and not sendData. Same for receiveUDPData. Besides how is it possible to know where the message comes from in a broadcast mode ?? Annick Le 17 oct. 2014 à 17:35, Sven Van Caekenberghe <sven@stfx.eu> a écrit :
UDP is pretty easy, just send and receive byte arrays basically, mostly non-blocking.
http://forum.world.st/UDP-Listener-example-td4362851.html http://forum.world.st/SysLogSender-UDP-td4745862.html
The API is all in Socket.
On 17 Oct 2014, at 17:09, Annick Fron <list@afceurope.com> wrote:
Hi,
I was not able to find a UDP example in pharo.
There are only TCP examples.
Any pointer ?
Annick