I have a client and server. I start up the server, and run the client, and the first time it works fine. The second time I run the client(without restarting the server), the client appears to hang. Can anyone see what is wrong?
I have a client:
# Code example originated from p069dtclient.rb at http://rubylearning.com/satishtalim/ruby_socket_programming.html
require 'socket'
x = 0;
streamSock = TCPSocket.new( 'localhost', 20000 )
while x < 10
streamSock.send( "Hello #{x}",0 )
str = streamSock.recv( 100 )
puts "#{x} " + str
x=x+1
end
streamSock.close
And server:
# p068dtserver.rb
require "socket"
dts = TCPServer.new('localhost', 20000)
s = dts.accept
print(s, " is accepted\n")
loopCount = 0;
loop do
Thread.start(s) do
loopCount = loopCount + 1
lineRcvd = s.recv(1024)
if ( !lineRcvd.empty? )
puts("#{loopCount} Received: #{lineRcvd}")
s.write(Time.now)
end
end
end
s.close
print(s, " is gone\n")
Each connection to the server requires a separate accept call in order to be received. What's happening is that you're accepting the first, working with it, and then effectively terminating while leaving the socket in a listening state. This means connections will be opened, but not accepted, so they hang as you describe.
You might be better off using a more robust server framework. EventMachine (http://rubyeventmachine.com/) is a little tricky to learn, but is far more powerful than a roll your own solution.
Here's a quick fix that might help:
require "socket"
dts = TCPServer.new('localhost', 20000)
while (s = dts.accept)
print(s, " is accepted\n")
loopCount = 0;
loop do
Thread.start(s) do
loopCount = loopCount + 1
lineRcvd = s.recv(1024)
if ( !lineRcvd.empty? )
puts("#{loopCount} Received: #{lineRcvd}")
s.write(Time.now)
end
end
end
s.close
print(s, " is gone\n")
end
Now the accept call is wrapped in a loop so more than one connection can be processed.
Related
Here is my code (and below it is my issue):
require "socket"
server = TCPServer.open("localhost", 2000)
loop {
thread.start(server.accept) do |nodervcr|
msg = nodervcr.gets
puts(msg)
if msg = "codeword"
puts("codeword!")
else
puts("not codeword")
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}
So I tried:
if msg == code
# then do this
elsif msg == code2
# then do this
# etc
But it's not working.
I've tried replacing msg with nodervcr, still nothing.
The strange part is that it's obviously getting the message, and msg does = what the client sends.. but it acts as if that variable dies immediately. I'm new to ruby. please help thanks.
You have a few problems in your code. But the main problem is that when you use something like Telnet, you probally are giving a enter.
So your sending "codeword" + CRLF (codeword\r\n). Thats not the same as your check: msg == "codeword"
What you should do is strip the input from these chars (strip). It removes the CRLF and it then works
Remember. Variable Assignments are = (single) comparing is double ==
Below the working code:
require "socket"
server = TCPServer.open("127.0.0.1", 2000)
loop {
Thread.start(server.accept) do |nodervcr| # Thread, not thread
msg = nodervcr.gets
# puts(msg)
p msg # use this so see all the invisiable chars
if msg.strip == "codeword" # stip the input
puts("codeword!")
else
puts("not codeword")
end
# Note this part works: it sends the server a message and it displays it
# You would think a simple if then else statement could redirect it according to the imcoming message from the client; which is my issue.
end
}
I've this Ruby server that uses a Unix Socket:
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
And I've this client in JavaScript that uses node.js net api:
Net = require('net');
var client = Net.connect({path: '/tmp/ciccio.sock'}, () => {
console.log('write data');
client.write('hello world!');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('end');
});
client.on('error', (error) => {
console.log(error.toString());
});
The problem is that at the first iteration of server loop, recv receives the right string and the server replies to the client with the string data. But at the second iteration recv receives empty data and so a infinite loop begins... server continues to receive empty data and recv does not block...
If I use a ruby client like this all works fine...
require 'socket'
sock = UNIXSocket.new('/tmp/ciccio.sock')
loop do
sock.write('hello world!')
data = sock.recv(1024)
puts data
sleep(10)
end
sock.close
Your client closes the connection upon connecting, writing "hello world", and then receiving a response. Now your server is calling recv on a socket that has been closed, which will return an empty string. Since you're looping to read on this socket indefinitely, it will never return control back to the outer loop to call accept for the second client. The ruby version of your client works because it never closes the connection to the server.
Breaking out of your inner loop after receiving an empty string should get you what you want,
require 'socket'
server = UNIXServer.new('/tmp/ciccio.sock')
loop do
sock = server.accept
loop do
begin
data = sock.recv(1024)
break if data.empty?
data = "DO SOMETHING -> #{data}"
sock.write(data)
sock.flush
rescue Errno::EPIPE, Errno::ENOTCONN
break
end
end
end
Another potential problem is that your server can only service a single connection to a single client, as the inner loop blocks any subsequent calls to accept until the current connection ends.
TCP sockets are streams, not messages, so berkeley sockets send() function on some systems may send less data than required. Since Ruby Socket is very thin wrapper over berkeley sockets, AFAIK Socket#send will behave exactly like berkeley sockets send(). So what is the correct way to send a complete message via Ruby TCP sockets? In python it's a special function for that called sendall(). But in Ruby i need to manually write code like that:
while (sent = sock.send( data, 0 ) < data.length do
data = data[ sent..-1 ]
end
To expand on what danielnegri says, IO.write ends up calling io_binwrite in io.c
The relevant bit of the ruby source is below (n and len are initially set to the length of your data and offset to 0)
retry:
arg.offset = offset;
arg.length = n;
if (fptr->write_lock) {
r = rb_mutex_synchronize(fptr->write_lock, io_binwrite_string, (VALUE)&arg);
}
else {
long l = io_writable_length(fptr, n);
r = rb_write_internal(fptr->fd, ptr+offset, l);
}
if (r == n) return len;
if (0 <= r) {
offset += r;
n -= r;
errno = EAGAIN;
}
if (rb_io_wait_writable(fptr->fd)) {
rb_io_check_closed(fptr);
if (offset < RSTRING_LEN(str))
goto retry;
}
return -1L;
As you can see, until it has written all the data it will keep on doing goto retry and trying again. rb_io_wait_writable basically checks that the value of errno is such that one should try again (as opposed to something more fatal) and then calls select to avoid busy-waiting.
Recently I used 'write' method:
require "socket"
server = TCPServer.new('localhost', 20000)
loop do
Thread.start(server.accept) do |s|
print(s, " is accepted\n")
server.write(Time.now)
print(server, " is gone\n")
server.close
end
end
Try:
require 'socket'
sock = Socket.open(Socket::PF_INET,Socket::SOCK_STREAM,Socket::IPPROTO_TCP)
#data = "anyThing"
#addr = pack_sockaddr_in(port, host)
sock.connect(#addr) #make the connection
sock.send(#data, 0)
You may also want to try using the TCPSocket class. I haven't used any of this Ruby code, so I'm not used to this particular library; please let me know if I got this all wrong.
require 'socket'
sock = TCPSocket.new(host, port)
#data = "anyThing"
sock.send(#data, 0)
I have the following code that I put together for a simple Ruby TFTP server. It works fine in that it listens to port 69 and my TFTP client connects to it and I am able to write the packets to the test.txt, but instead of just writing packets, I want to be able to TFTP a file from my client to the /temp directory.
Thanks in advance for your help!
require 'socket.so'
class TFTPServer
def initialize(port)
#port = port
end
def start
#socket = UDPSocket.new
#socket.bind('', #port)
while true
packet = #socket.recvfrom(1024)
puts packet
File.open('/temp/test.txt', 'w') do |p|
p.puts packet
end
end
end
end
server = TFTPServer.new(69)
server.start
Instead of writing to the /temp/test.txt you can use ruby's Tempfile class
So in your example:
require 'socket.so'
require 'tempfile'
class TFTPServer
def initialize(port)
#port = port
end
def start
#socket = UDPSocket.new
#socket.bind('', #port)
while true
packet = #socket.recvfrom(1024)
puts packet
Tempfile.new('tftpserver') do |p|
p.puts process_packet( packet )
end
end
end
end
server = TFTPServer.new(69)
server.start
This will create a guaranteed unique temporary file in your /tmp directory with a name based off of 'tftpserver'.
EDIT: I noticed you wanted to write to /temp (not /tmp) to do this you can do Tempfile.new('tftpserver', '/temp') to specify a specific temporary directory.
Edit 2: For anyone interested there is a library that will do this https://github.com/spiceworks/net-tftp
you'll not get it so easily, the tftp protocol is relatively easy, but put/get is not stateless, or at least if the file does not fit in a single packet, that is something like 512, but some extensions allow a bigger packet
the file on the wire is splited and you'll get a sequence of packets
each packet has a sequence number so the other end can send error for a specific packet
you should take a look at wikipedia page:
http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
here a sample code I wrote in 2005 but id does the specular thing (it sends a file)
it's python but reasonably similar to ruby :D
def send_file(self, addr, filesend, filesize, blocksize):
print '[send_file] Sending %s (size: %d - blksize: %d) to %s:%d' % (filesend, filesize, blocksize, addr[0], addr[1])
fd = open(filesend, 'rb')
for c in range(1, (filesize / blocksize) + 2):
hdr = pack('!H', DATA) + pack('!H', c)
indata = fd.read(blocksize)
if debug > 5: print '[send_file] [%s] Sending block %d size %d' % (filesend, c, len(indata))
self.s.sendto(hdr + indata, addr)
data, addr = self.s.recvfrom(1024)
res = unpack('!H', data[:2])[0]
data = data[2:]
if res != ACK:
print '[send_file] Transfer aborted: %s' % errors[res]
break
if debug > 5:
print '[send_file] [%s] Received ack for block %d' % (filesend, unpack('>H', data[:2])[0] + 1)
fd.close()
## End Transfer
pkt = pack('!H', DATA) + pack('>H', c) + NULL
self.s.sendto(pkt, addr)
if debug: print '[send_file] File send Done (%d)' % c
you can find constants in arpa/tftp.h (you need a unix or search online)
the sequence number is a big endian (network order) short !H format for struct pack
ruby has something like python struct in String class
I'm trying to use unix sockets and SOCK_DGRAM in ruby, but am having a really hard time figuring out how to do it. So far, I've been trying things like this:
sock_path = 'test.socket'
s1 = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
s1.bind(Socket.pack_sockaddr_un(sock_path))
s2 = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
s2.bind(Socket.pack_sockaddr_un(sock_path))
s1.send("HELLO")
s2.recv(5) # should equal "HELLO"
Does anybody have experience with this?
In common case you need use connect and bind for both client and server sockets, so you need two different address for binding
require 'socket'
sock_path = 'test.socket'
sock_path2 = 'test2.socket'
s1 = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
s1.bind(Socket.pack_sockaddr_un(sock_path))
s2 = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
s2.bind(Socket.pack_sockaddr_un(sock_path2))
s2.connect(Socket.pack_sockaddr_un(sock_path))
s1.connect(Socket.pack_sockaddr_un(sock_path2))
s1.send("HELLO", 0)
puts s2.recv(5)
=> HELLO