Forum: Ruby sending data, no strings via socket

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
554b0596bcb7a0cff1f51eb7c6139985?d=identicon&s=25 Javier Isassi (cabazorro)
on 2007-05-24 22:02
I'm trying to send data like

typedef struct{
   u_int32_t type;
   u_int8_t len;
   u_int8_t * buf;
}

over a tcp socket using ruby.

Anything I push over the socket comes out the other end as a string..

in Ruby..

data = 10
socket = TCPSocket.new(home,port)
socket.write(data)

on the other end I get
receieved byte dump
0000: 31 30                10

which is the ascii for 10. How can I tell Ruby to send the 0x10?
even if I write
data = 0x10
on the other side I get:
0000: 31 36                16
Which again, is the ascii getting send, not the data.
thanks for any help.
D0338c0de4cb3c5c17300396159933d1?d=identicon&s=25 Axel Etzold (Guest)
on 2007-05-24 22:24
(Received via mailing list)
Maybe "pack/unpack"

http://www.rubycentral.com/book/ref_c_array.html#Array.pack

can solve your problem ?

Best regards,

Axel
-------- Original-Nachricht --------
Datum: Fri, 25 May 2007 05:02:49 +0900
Von: Javier None <javierisassi@gmail.com>
An: ruby-talk@ruby-lang.org
Betreff: sending data, no strings via socket
554b0596bcb7a0cff1f51eb7c6139985?d=identicon&s=25 Javier Isassi (cabazorro)
on 2007-05-24 22:39
Axel Etzold wrote:
> Maybe "pack/unpack"
>
> http://www.rubycentral.com/book/ref_c_array.html#Array.pack
>
> can solve your problem ?
>
> Best regards,
>
> Axel
> -------- Original-Nachricht --------
> Datum: Fri, 25 May 2007 05:02:49 +0900
> Von: Javier None <javierisassi@gmail.com>
> An: ruby-talk@ruby-lang.org
> Betreff: sending data, no strings via socket

Indeed, if I type:

mymsg = [10]
socket.write(mymsg.pack("I"))

on the other side I get (drum roll..) an A!
0000: 0A 00 00 00                                       ....

Which exactly what I need for my scriptable protocol analyzer.
A million.to_i thanks!
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2007-05-24 22:52
(Received via mailing list)
Javier None wrote:
...
> data = 10
> socket = TCPSocket.new(home,port)
> socket.write(data)
>
> on the other end I get
> receieved byte dump
> 0000: 31 30                10
>
> which is the ascii for 10. How can I tell Ruby to send the 0x10?

IIRC, #write(arg) is converting arg to a string, so the number 10 is
converted to the string "10", as you noticed.

If you construct the string yourself, you can control what binary data
is in this string:

irb(main):029:0> data = "\012"
=> "\n"
irb(main):030:0> data[0]
=> 10

(note that \nnn uses octal).

But since it's hard to handle endianness and multibyte numbers this way,
you probably want to use pack/unpack as Axel suggested.

If you want a friendly interface to do this, check out my bit-struct
lib:

http://redshift.sourceforge.net/bit-struct/

For example:

---------------------------------
require 'bit-struct'

class MyData < BitStruct    # typedef struct{
   unsigned  :type,  32      #   u_int32_t type;
   unsigned  :len,   8       #   u_int8_t len;
   rest      :buf            #   u_int8_t * buf;
end                         # }

data = MyData.new
data.type = 1234
data.buf = "fred flintstone"
data.len = data.buf.length

p data       # ==> #<MyData type=1234, len=15, buf="fred flintstone">
p data.to_s  # ==> "\000\000\004\322\017fred flintstone"
---------------------------------

Note that the number format is big-endian (by default), which makes
sense if you're writing network code.

Also note that the pointer is replaced with the buf bytes themselves,
which is probably how you want it (you didn't really want to send a
pointer over a tcp socket, right?).
554b0596bcb7a0cff1f51eb7c6139985?d=identicon&s=25 Javier Isassi (cabazorro)
on 2007-05-24 23:36
Joel VanderWerf wrote:
> Javier None wrote:
> ...
>> data = 10
>> socket = TCPSocket.new(home,port)
>> socket.write(data)
>>
>> on the other end I get
>> receieved byte dump
>> 0000: 31 30                10
>>
>> which is the ascii for 10. How can I tell Ruby to send the 0x10?
>
> IIRC, #write(arg) is converting arg to a string, so the number 10 is
> converted to the string "10", as you noticed.
>
> If you construct the string yourself, you can control what binary data
> is in this string:
>
> irb(main):029:0> data = "\012"
> => "\n"
> irb(main):030:0> data[0]
> => 10
>
> (note that \nnn uses octal).
>
> But since it's hard to handle endianness and multibyte numbers this way,
> you probably want to use pack/unpack as Axel suggested.
>
> If you want a friendly interface to do this, check out my bit-struct
> lib:
>
> http://redshift.sourceforge.net/bit-struct/
>
> For example:
>
> ---------------------------------
> require 'bit-struct'
>
> class MyData < BitStruct    # typedef struct{
>    unsigned  :type,  32      #   u_int32_t type;
>    unsigned  :len,   8       #   u_int8_t len;
>    rest      :buf            #   u_int8_t * buf;
> end                         # }
>
> data = MyData.new
> data.type = 1234
> data.buf = "fred flintstone"
> data.len = data.buf.length
>
> p data       # ==> #<MyData type=1234, len=15, buf="fred flintstone">
> p data.to_s  # ==> "\000\000\004\322\017fred flintstone"
> ---------------------------------
>
> Note that the number format is big-endian (by default), which makes
> sense if you're writing network code.
>
> Also note that the pointer is replaced with the buf bytes themselves,
> which is probably how you want it (you didn't really want to send a
> pointer over a tcp socket, right?).

just installed it! works.

client....
class Tdata < BitStruct
  unsigned :type,  32
  rest     :buf
end

data = Tdata.new
data.type = 987 #03DB
data.buf = "push to talk"

socket = TCPSocket.new(home,port)

server writes.....
0000: 00 00 03 DB 70 75 73 68 20 74 6F 20 74 61 6C 6B   ....push to talk

we get big endian, that can be managed on the server side with
htonl,htons.
Thanks!
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2007-05-24 23:52
(Received via mailing list)
Javier Isassi wrote:
>
> server writes.....
> 0000: 00 00 03 DB 70 75 73 68 20 74 6F 20 74 61 6C 6B   ....push to talk
>
> we get big endian, that can be managed on the server side with
> htonl,htons.
> Thanks!
>

If you want to use different endianness:

   unsigned :type,  32,   :endian => :native # little endian, if on x86
   unsigned :type,  32,   :endian => :little # force little endian

Big-endian is only the default because many network protocols use it.
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2007-05-25 00:03
(Received via mailing list)
On 5/24/07, Javier None <javierisassi@gmail.com> wrote:
> Anything I push over the socket comes out the other end as a string..
>
> which is the ascii for 10. How can I tell Ruby to send the 0x10?
> even if I write
> data = 0x10
> on the other side I get:
> 0000: 31 36                16
> Which again, is the ascii getting send, not the data.
> thanks for any help.

Well, write takes a string representing the datastream to be written
so you need a string with the binary data.

if you are trying to write a single byte then
data  = 10.chr

You might also want to look at Array#pack

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

IPMS/USA Region 12 Coordinator
http://ipmsr12.denhaven2.com/

Visit the Project Mercury Wiki Site
http://www.mercuryspacecraft.com/
This topic is locked and can not be replied to.