# Question About TCPServer & TCPSocket classes

**URL:** <https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896>\
**Category:** ruby-talk\
**Created:** [6 September 2008 05:44 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896 "2008-09-06T05:44:06Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Steve\_Lewis](https://avatars.discourse-cdn.com/v4/letter/s/5f9b8f/32.png) [@Steve\_Lewis](https://rubytalk.org/u/Steve_Lewis)\
**Post date:** [6 September 2008 05:44 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896/1 "2008-09-06T05:44:06Z")

</div>

Hello Rubyists,

I've been messing around with socket programming in Ruby lately, and I've  
hit a snag with some of my experiments. My first, (successful),  
experiment was to create a basic client/server script wherein the client  
sends a string to the server, and the server responds by simply writing  
the string back to the client...that code is as follows:

Server:

> **···**
>
> #####################################  
> require 'socket'
> 
> class PingServer \< TCPServer
> 
> &nbsp;&nbsp;def start\_server  
> &nbsp;&nbsp;&nbsp;&nbsp;loop do  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.start(self.accept) do |s|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection accepted from server #{s.inspect}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request = s.readline.gsub(/\n$/, '')  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Request was #{request}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.write "Your request was as follows: #{request}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.write Time.now if request == 'time'  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.close  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection #{s} closed"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> end
> 
> PingServer.new('localhost', 3000).start\_server
> 
> ###############################################
> 
> The client code is thus:  
> ########################################  
> &nbsp;&nbsp;require 'socket'  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;def send\_message(message)  
> &nbsp;&nbsp;&nbsp;&nbsp;TCPSocket.open('localhost', 3000) do |client|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.write(message)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stuff = client.read(100)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.close  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p stuff  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get\_message  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;def get\_message  
> &nbsp;&nbsp;&nbsp;&nbsp;message = gets  
> &nbsp;&nbsp;&nbsp;&nbsp;send\_message(message) unless message.gsub(/\n/, '') == 'quit'  
> &nbsp;&nbsp;&nbsp;&nbsp;exit  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;  
> &nbsp;&nbsp;get\_message
> 
> ##################################
> 
> As I said, the above code works as expected, I start the server, then run  
> the client program...I can send strings to the server all day long, and  
> the server writes them right back to me.
> 
> The problem occurs when I try to send the following instead of a string:
> 
> Marshal.dump(%w(foo bar baz))
> 
> Sending the resulting string over the wire causes the server to hang...it  
> doesn't return the string back to the client...I'm at a loss to figure  
> out why it seems to have trouble handling the string...
> 
> Ideas would be appreciated!!
> 
> Thanks
> 
> Steve

---

<div class="post-metadata">

**Author:** ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)\
**Post date:** [6 September 2008 06:48 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896/2 "2008-09-06T06:48:52Z")

</div>

Steve Lewis wrote:

> Hello Rubyists,
> 
> I've been messing around with socket programming in Ruby lately, and I've hit a snag with some of my experiments. My first, (successful), experiment was to create a basic client/server script wherein the client sends a string to the server, and the server responds by simply writing the string back to the client...that code is as follows:
> 
> Server:  
> #####################################  
> require 'socket'
> 
> class PingServer \< TCPServer
> 
> &nbsp;&nbsp;def start\_server  
> &nbsp;&nbsp;&nbsp;&nbsp;loop do  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.start(self.accept) do |s|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection accepted from server #{s.inspect}"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request = s.readline.gsub(/\n$/, '')

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;^^^^^^^^

This method reads until newline, and binary data (such as Marshal.dump output) is not generally delimited that way. Use IO#read or Socket#recv instead.

If you want to read a binary message, you can do one of several things:

1. pick a delimiter char and escape that wherever it occurs within the binary data (yuck)

2. send a length field before sending the data, so the receiver knows how much to wait for

3. send one message per socket connection

4. encode the binary data in some non-binary form: e.g. base64, using pack("m"), and delimit using blank lines.

> **···**
>
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

**Author:** ![Robert\_K1](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/robert_k1/32/1830_2.png) [@Robert\_K1](https://rubytalk.org/u/Robert_K1)\
**Post date:** [6 September 2008 08:59 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896/3 "2008-09-06T08:59:07Z")

</div>

Steve, you are mixing #read, #write on one side and #readline on the other side. Since Marshal.dump creates binary data your #readline on the server won't properly work. You should either use a binary protocoll \*or\* a textual (line based) protocol. Mixing both causes problems.

Just an additional note: if you use Marshal, you can directly read to and write from the socket without the intermediate String. This is likely more efficient and also you avoid the issue of not knowing how long the Marshal sequence is.

And yet another note: just in case you need a solution with communicating processes where all of them are Ruby applications you can use DRuby. But I guess at the moment you want to learn about Ruby socket programming so this is probably just for keeping in the back of your head.

Kind regards

&nbsp;&nbsp;robert

> **···**
>
> On 06.09.2008 07:45, Steve Lewis wrote:
> 
> > Hello Rubyists,
> > 
> > I've been messing around with socket programming in Ruby lately, and I've hit a snag with some of my experiments. My first, (successful), experiment was to create a basic client/server script wherein the client sends a string to the server, and the server responds by simply writing the string back to the client...that code is as follows:
> > 
> > Server:  
> > #####################################  
> > require 'socket'
> > 
> > class PingServer \< TCPServer
> > 
> > &nbsp;&nbsp;def start\_server  
> > &nbsp;&nbsp;&nbsp;&nbsp;loop do  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.start(self.accept) do |s|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection accepted from server #{s.inspect}"  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request = s.readline.gsub(/\n$/, '')  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Request was #{request}"  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.write "Your request was as follows: #{request}"  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.write Time.now if request == 'time'  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.close  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection #{s} closed"  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;end
> > 
> > end
> > 
> > PingServer.new('localhost', 3000).start\_server
> > 
> > ###############################################
> > 
> > The client code is thus:  
> > ########################################  
> > &nbsp;&nbsp;require 'socket'  
> > &nbsp;&nbsp;  
> > &nbsp;&nbsp;def send\_message(message)  
> > &nbsp;&nbsp;&nbsp;&nbsp;TCPSocket.open('localhost', 3000) do |client|  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.write(message)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stuff = client.read(100)  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.close  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p stuff  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get\_message  
> > &nbsp;&nbsp;&nbsp;&nbsp;end  
> > &nbsp;&nbsp;end  
> > &nbsp;&nbsp;&nbsp;&nbsp;def get\_message  
> > &nbsp;&nbsp;&nbsp;&nbsp;message = gets  
> > &nbsp;&nbsp;&nbsp;&nbsp;send\_message(message) unless message.gsub(/\n/, '') == 'quit'  
> > &nbsp;&nbsp;&nbsp;&nbsp;exit end  
> > &nbsp;&nbsp;  
> > ##################################
> > 
> > As I said, the above code works as expected, I start the server, then run the client program...I can send strings to the server all day long, and the server writes them right back to me.
> > 
> > The problem occurs when I try to send the following instead of a string:
> > 
> > Marshal.dump(%w(foo bar baz))
> > 
> > Sending the resulting string over the wire causes the server to hang...it doesn't return the string back to the client...I'm at a loss to figure out why it seems to have trouble handling the string...
> > 
> > Ideas would be appreciated!!

---

<div class="post-metadata">

**Author:** ![Joel\_VanderWerf1](https://avatars.discourse-cdn.com/v4/letter/j/94ad74/32.png) [@Joel\_VanderWerf1](https://rubytalk.org/u/Joel_VanderWerf1)\
**Post date:** [6 September 2008 17:38 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896/4 "2008-09-06T17:38:46Z")

</div>

Robert Klemme wrote:

> Just an additional note: if you use Marshal, you can directly read to and write from the socket without the intermediate String. This is likely more efficient and also you avoid the issue of not knowing how long the Marshal sequence is.

Good point. I forgot that the output of Marshal records how long the data is, and therefore #load can read the correct number of bytes directly from an io.

> And yet another note: just in case you need a solution with communicating processes where all of them are Ruby applications you can use DRuby. But I guess at the moment you want to learn about Ruby socket programming so this is probably just for keeping in the back of your head.

Another possibility, if you are communicating to C code or other languages:

[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/177704](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/177704)

It's a lib for both C and ruby that uses length fields to make tcp sockets "message oriented" rather than stream oriented.

> **···**
>
> --  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

---

<div class="post-metadata">

**Author:** ![Steve\_Lewis](https://avatars.discourse-cdn.com/v4/letter/s/5f9b8f/32.png) [@Steve\_Lewis](https://rubytalk.org/u/Steve_Lewis)\
**Post date:** [7 September 2008 00:58 UTC](https://rubytalk.org/t/question-about-tcpserver-tcpsocket-classes/48896/5 "2008-09-07T00:58:59Z")

</div>

Robert,

Thanks for the help! You're correct, my curiosity here is mostly  
academic, but DRuby looks like the practical solution to use when I  
decide I'd like to try something more substantial.

> **···**
>
> On Sat, 06 Sep 2008 11:04:43 +0200, Robert Klemme wrote:
> 
> > On 06.09.2008 07:45, Steve Lewis wrote:
> > 
> > > Hello Rubyists,
> > > 
> > > I've been messing around with socket programming in Ruby lately, and  
> > > I've hit a snag with some of my experiments. My first, (successful),  
> > > experiment was to create a basic client/server script wherein the  
> > > client sends a string to the server, and the server responds by simply  
> > > writing the string back to the client...that code is as follows:
> > > 
> > > Server:  
> > > ##################################### require 'socket'
> > > 
> > > class PingServer \< TCPServer
> > > 
> > > &nbsp;&nbsp;def start\_server  
> > > &nbsp;&nbsp;&nbsp;&nbsp;loop do  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.start(self.accept) do |s|  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection accepted from server #{s.inspect}" request =  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.readline.gsub(/\n$/, '')  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Request was #{request}"  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.write "Your request was as follows: #{request}" s.write  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Time.now if request == 'time' s.close  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p "Connection #{s} closed"  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
> > > &nbsp;&nbsp;&nbsp;&nbsp;end  
> > > &nbsp;&nbsp;end
> > > 
> > > end
> > > 
> > > PingServer.new('localhost', 3000).start\_server
> > > 
> > > ###############################################
> > > 
> > > The client code is thus:  
> > > ########################################  
> > > &nbsp;&nbsp;require 'socket'  
> > > &nbsp;&nbsp;  
> > > &nbsp;&nbsp;def send\_message(message)  
> > > &nbsp;&nbsp;&nbsp;&nbsp;TCPSocket.open('localhost', 3000) do |client|  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.write(message)  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stuff = client.read(100)  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.close  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p stuff  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get\_message  
> > > &nbsp;&nbsp;&nbsp;&nbsp;end  
> > > &nbsp;&nbsp;end  
> > > &nbsp;&nbsp;  
> > > &nbsp;&nbsp;def get\_message  
> > > &nbsp;&nbsp;&nbsp;&nbsp;message = gets  
> > > &nbsp;&nbsp;&nbsp;&nbsp;send\_message(message) unless message.gsub(/\n/, '') == 'quit' exit  
> > > &nbsp;&nbsp;end  
> > > &nbsp;&nbsp;  
> > > &nbsp;&nbsp;get\_message
> > > 
> > > ##################################
> > > 
> > > As I said, the above code works as expected, I start the server, then  
> > > run the client program...I can send strings to the server all day long,  
> > > and the server writes them right back to me.
> > > 
> > > The problem occurs when I try to send the following instead of a  
> > > string:
> > > 
> > > Marshal.dump(%w(foo bar baz))
> > > 
> > > Sending the resulting string over the wire causes the server to  
> > > hang...it doesn't return the string back to the client...I'm at a loss  
> > > to figure out why it seems to have trouble handling the string...
> > > 
> > > Ideas would be appreciated!!
> > 
> > Steve, you are mixing #read, #write on one side and #readline on the  
> > other side. Since Marshal.dump creates binary data your #readline on  
> > the server won't properly work. You should either use a binary  
> > protocoll \*or\* a textual (line based) protocol. Mixing both causes  
> > problems.
> > 
> > Just an additional note: if you use Marshal, you can directly read to  
> > and write from the socket without the intermediate String. This is  
> > likely more efficient and also you avoid the issue of not knowing how  
> > long the Marshal sequence is.
> > 
> > And yet another note: just in case you need a solution with  
> > communicating processes where all of them are Ruby applications you can  
> > use DRuby. But I guess at the moment you want to learn about Ruby  
> > socket programming so this is probably just for keeping in the back of  
> > your head.
> > 
> > Kind regards
> > 
> > &nbsp;&nbsp;robert
