ruby socket dgram example - ruby

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

Related

Minecraft protocol status

Started making some kind of proxy client to Minecraft. After reading a protocol, I wrote everything how it was described and still can't get json status as response. Here's code I wrote:
def shift(val, imp)
mask = (1 << (32 - imp)) - 1
return (val >> imp) & mask
end
def writeVarInt(value, sock)
while (true) do
if ((value & 0x7F) == 0) then
sock.send([value].pack('C'), 0)
return
end
sock.send([(value & 0x7F) | 0x80].pack('C'), 0)
value = shift(value, 7)
end
end
server = TCPSocket.new('connect.2b2t.org', 25565)
writeVarInt(340, server)
server.send(['127.0.0.1'].pack('A'), 0)
server.send([25565].pack('S'), 0)
writeVarInt(1, server)
puts server.recv(1024)
If needed, there is link to protocol: https://wiki.vg/Protocol#Definitions

How to correctly handle partial write in Ruby Sockets?

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)

Ruby TFTP server

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

Write byte to Arduino from Ruby over serial

I'm trying to send an integer over the serial port to my Ardunio. The chip is then going to display the number in binary on the LED's. However I'm having lots of trouble trying to send the data as a byte over the serial port, as far as I can debug the following code sends it as the ASC char values.
Can anyone point me in the right direction or spot the mistake? I'd really appreciate it. I've been pulling my hair out over this for a long time.
Ruby
require 'rubygems'
require 'serialport' # use Kernel::require on windows, works better.
#params for serial port
port_str = "/dev/tty.usbserial-A700dZt3" #may be different for you
baud_rate = 9600
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE
sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)
i = 15
#just write forever
while true do
sp.write(i.to_s(2))
sleep 10
end
Arduino
int ledPin = 10;
int ledPin1 = 11;
int ledPin2 = 12;
int ledPin3 = 13;
byte incomingByte; // for incoming serial data
void setup() {
pinMode(ledPin, OUTPUT); // initialize the LED pin as an output:
pinMode(ledPin1, OUTPUT); // initialize the LED pin as an output:
pinMode(ledPin2, OUTPUT); // initialize the LED pin as an output:
pinMode(ledPin3, OUTPUT); // initialize the LED pin as an output:
Serial.begin(9600);
Serial.println("I am online");
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
incomingByte = Serial.read();
Serial.println(incomingByte, DEC);
int value = (incomingByte, DEC) % 16;
digitalWrite(ledPin, (value >> 0) % 2);
digitalWrite(ledPin1, (value >> 1) % 2);
digitalWrite(ledPin2, (value >> 2) % 2);
digitalWrite(ledPin3, (value >> 3) % 2); // MSB
}
}
I'm guessing you are trying to write the value 15 in order to light all the LEDs at once. However, 15.to_s(2) is "1111". The ASCII value of the character '1' is 49, so instead of writing 15 once you are writing 49 four times in rapid succession.
The write command you are looking for is therefore probably sp.putc(i). This writes only one character with the given binary value (= machine-readable for Arduino) instead of an ASCII string representation of the value expressed in binary (= human-readable for you).
So keeping everything else the same, replace the while loop in your Ruby code with:
loop do
sp.putc(i)
puts 'Wrote: %d = %bb' % [ i, i ]
i = (i == 15) ? 0 : (i + 1)
sleep(10)
end
If you wish to read the responses from Arduino, you can use e.g. sp.gets to get one line of text, e.g. try placing puts 'Arduino replied: ' + sp.gets in the loop before sleep (and one puts sp.gets before the loop to read the "I am online" sent when the connection is first established).
Edit: I just spotted another problem in your code, on the Arduino side: value = (incomingByte, DEC) % 16; always results in the value 10 because (incomingByte, DEC) has the value DEC (which is 10). You should use value = incomingByte % 16; instead. Or do away with value altogether and modify incomingByte itself, e.g. incomingByte %= 16;.
Your problems may be caused by buffering. To disable buffering, you can do one of the following:
Set sp to unbuffered after creating it (before writing): sp.sync = true
Call flush after the write
Use the unbuffered syswrite instead of write
It's been so long since I did anything with serial ports that I can't help there, but I do see one thing.
>> 15.to_s #=> "15"
and
>> 15.to_s(2) #=> "1111"
I think if you want the binary value to be sent you'll want "\xf" or "\u000F".
Change your code from:
while true do
sp.write(i.to_s(2)) # <-- this sends a multi-character ASCII representation of the "i" value, NOT the binary.
sleep 10
end
to:
while true do
sp.write(i.chr) # <-- this sends a single byte binary representation of the "i" value, NOT the ASCII.
sleep 10
end
To show the difference, here's the length of the strings being output:
>> 15.to_s(2).size #=> 4
>> 15.chr.size #=> 1
And the decimal values of the bytes comprising the strings:
>> 15.to_s(2).bytes.to_a #=> [49, 49, 49, 49]
>> 15.chr.bytes.to_a #=> [15]
I've had this Ruby code work before
while true do
printf("%c", sp.getc)
end
rather than using sp.write(i.to_s). It looks like you are explicitly converting it to a string, which may be the cause of your problems.
I found the original blog post I used:
http://www.arduino.cc/playground/Interfacing/Ruby

Ruby TCPSocket keeps losing connection

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 &lt 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.

Resources