Sending data, no strings via socket

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.

···

--
Posted via http://www.ruby-forum.com/.

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

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.

--
Posted via http://www.ruby-forum.com/\.

--
Psssst! Schon vom neuen GMX MultiMessenger gehört?
Der kanns mit allen: http://www.gmx.net/de/go/multimessenger

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

--
        vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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

···

On 5/24/07, Javier None <javierisassi@gmail.com> wrote:

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.

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

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!

···

--
Posted via http://www.ruby-forum.com/\.

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:

BitStruct

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!

···

--
Posted via http://www.ruby-forum.com/\.

Javier Isassi wrote:

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!

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.

···

--
        vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407