Sending Binary Data?

Hello Ruby Gurus!

My friend and I are writing a chat/IM client in Ruby. It’s nothing
special, but it’s helping us learn the language, especially the lower
level networking parts really well and I’m thoroughly enjoying the
project so far. I thought implementing file transfers would be pretty
nifty, but I’m running into some problems. I did a simple copy by
reading the file (a picture) and adding the lines to a string, and
writing that string out to a new file:

a = String.new

IO.foreach(“lighter.jpg”) do |line|
a += line
end

file = File.open(“new.jpg”,“w”)
file.write(a)
file.close

And it worked swimmingly, however, when I tried to do this over a
TCPClient/Server:

server.rb

require ‘socket’
port = 9191
file = File.open(“omgnew.jpg”,“w+”)
server = TCPServer.new(‘localhost’, port)
while session = server.accept
file.write(session.gets)
server.close
file.close
end

client.rb

require ‘socket’

server = TCPSocket.new(‘localhost’,9191)
file = String.new
IO.foreach(“lighter.jpg”) do |line|
file += line.chomp
end

server.send(file,0)

It works for a bit, then I get this error:

server.rb:7:in `accept’: closed stream (IOError)
from server.rb:7

The file sizes are nearly identical, I’m off by 479 bytes:

ynadji@onizuka:file_transfer$ du -b *
122753 lighter.jpg
122274 omgnew.jpg

Any ideas? I could be doing this entirely wrong, searching didn’t yield
any helpful results, so I figured I’d turn here. I was reading around
with Array#pack and String#unpack which seems to be heading in the right
direction, but I’m completely lost :P.

Thanks!

Yacin Nadji

Yacin Nadji wrote:

a = String.new

IO.foreach(“lighter.jpg”) do |line|
a += line
end

This approach is quite inefficient in two ways:

  • you use + for string concatenation which constantly creates new
    objects
    and throws old ones away. Better use <<

  • You need to keep the whole file in mem while with a streaming
    approach
    (reading and writing a chunk at a time only) you can deal with arbitrary
    sized files without worrying about memory.

file = File.open(“new.jpg”,“w”)

You should use mode “wb” - even if on Linux.

file = File.open(“omgnew.jpg”,“w+”)
“wb” again…

server = TCPServer.new(‘localhost’, port)
while session = server.accept
file.write(session.gets)

Better use #read and #write instead of gets!

server.close
file.close
end

client.rb

require ‘socket’

server = TCPSocket.new(‘localhost’,9191)

You want TCPServer here.

file = String.new
IO.foreach(“lighter.jpg”) do |line|
file += line.chomp
end

untested

srv = TCPServer.new(‘localhost’,9191)

while ( sess = srv.accept )
Thread.new(sess) do |session|
File.open(“lighter.jpg”, “rb”) |io|
session.write(io.read(1024))
end
end
end

ynadji@onizuka:file_transfer$ du -b *
122753 lighter.jpg
122274 omgnew.jpg

Any ideas? I could be doing this entirely wrong, searching didn’t
yield any helpful results, so I figured I’d turn here. I was reading
around with Array#pack and String#unpack which seems to be heading in
the right direction, but I’m completely lost :P.

Hope I could give some valuable hints.

Kind regards

robert

On Feb 28, 2006, at 3:52 AM, Yacin Nadji wrote:

server = TCPSocket.new(‘localhost’,9191)
file = String.new
IO.foreach(“lighter.jpg”) do |line|
file += line.chomp
end

try deleting the .chomp. Jpg’s aren’t really line oriented anyway.
Also if you are on windows not opening the file in binary mode will
wreak also sorts of havoc. As an alternative try this:

a = File.open(“lighter.jpg”, “rb”) { |f| f.read }