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.

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 N. [email protected]
An: [email protected]
Betreff: sending data, no strings via socket

Axel E. 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 N. [email protected]
An: [email protected]
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!

Joel VanderWerf wrote:

Javier N. 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 # ==> #
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!

Javier N. 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 # ==> #
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?).

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

On 5/24/07, Javier N. [email protected] 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/