Questions about Perl & Ruby Sockets Programming - ruby
Here are four codes:
Code A: (Perl TCP Server)
prompt> perl -e '
use IO::Socket;
$s = IO::Socket::INET->new(LocalPort => 8080, Type => SOCK_STREAM, Reuse => 1, Listen => 10, Proto => "tcp") or die "$!";
while ($c = $s->accept) {
print while <$c>;
}'
Code B: (Perl TCP Client)
prompt> perl -e '
use IO::Socket;
$c = IO::Socket::INET->new(PeerAddr => "localhost:8080") or die "$!";
while (<>) {
print $c $_
}'
Code C: (Ruby TCP Server)
prompt> ruby -e '
require "socket"
s = TCPServer.new("localhost", 8080)
while( c = s.accept)
while l = c.gets
puts l
end
end'
Code D: (Ruby TCP Client)
prompt> ruby -e '
require "socket"
c = TCPSocket.new("localhost", 8080)
while l = gets
c.puts l
end'
The following issues confused me:
Code A and Code B can be run simultaneously. I thought it should threw an "Address already be used" Error when the latter process starts which bind to the same TCP port with former process.
Two (maybe more than two) instance of Code C can be run simulataneously, while I can't run two instance of Code A.
while Code A and Code C were being running simultaneously, I visited "http://localhost:8008" via Google Chrome, then Code C printed the HTTP messages, while Code A did not.
while I run Code C singlehandedly, Code B can not connect to it.
while I run Code A singlehandedly, Code D can connect to it.
while Code A and Code C were being running simultaneously, code D connected to C and Code B connected A.
Code A and Code B can be run simultaneously. I thought it should threw an "Address already be used" Error when the latter process starts which bind to the same TCP port with former process.
The addresses wouldn't conflict in this situation, only ports. More specifically, if the source ports were the same. Destinations ports are regularly the same, as this defines where a service may exist.
Ex: http servers generally use source port 80 with destination ports "randomized". http clients generally use destination port 80 with source ports "randomized". (Not truly random, but that's beyond the scope of the question.)
while Code A and Code C were being running simultaneously, I visited "http://localhost:8008" via Google Chrome, then Code C printed the HTTP messages, while Code A did not.
This statement in particular leads me to believe the code above wasn't actually was was ran. Specifically this line:
s = TCPServer.new("localhost", 8080)
This would explain most of the issues you're describing. Try putting each of these into files and running them. You'll lessen the possibility of typos from one run to the next.
The only remaining unsolved issue is this guy:
Two (maybe more than two) instance of Code C can be run simulataneously, while I can't run two instance of Code A.
Try running lsof -Pni:8080 (or something similar in your environment), to see what services are listening on that port.
There appears to be a dual-stack issue with the Ruby script. It's defaulting to IPv6 localhost, then IPv6 site-local, and lastly IPv4 localhost. It's looks as if it's specifying the source address internally.
The Perl script is functioning correctly. It's likely opening a socket with in6addr_any and listening comfortably on v4 and v6.
Code A and Code B can be run simultaneously. I thought it should threw an "Address already be used" Error when the latter process starts which bind to the same TCP port with former process.
The server is bound to 127.0.0.1:8080, and the client is bound to 127.0.0.1:<some free port> (since you didn't request to bind the client to a specific a port). 127.0.0.1:8080 != 127.0.0.1:<some free port>, so no problem.
Two (maybe more than two) instance of Code C can be run simulataneously, while I can't run two instance of Code A.
You can't run more than two working instances of "C". It's impossible to have two sockets bound to the same IP address and port. It's like trying to give two people the same mailing address.
while Code A and Code C were being running simultaneously, I visited "http://localhost:8008" via Google Chrome, then Code C printed the HTTP messages, while Code A did not.
Of course. Because "C" manged to bind to 127.0.0.1:8080, "A" can't and dies.
while I run Code C singlehandedly, Code B can not connect to it.
I don't see why. What error do you get?
When I tried it in a file with one modification from above code
Ruby Client <-> Ruby server was Success
Perl Client <-> Perl Server was Success
Ruby Client <-> Perl Server was Success
Perl Client <-> Ruby Server FAILED
I got the below Error..
"No connection could be made because the target machine actively
refused it. at pc.pl line 4."
So I was wondering what was going on!!! So I checked with a different client program and it was still not connecting to the Ruby client server. So I thought to focus on ruby server first
sample Socket Tester:
http://sourceforge.net/projects/sockettest/
Ruby Server:
s = TCPServer.new("localhost", 20000)
while( c = s.accept)
while l = c.gets
puts l
end
end
Perl Client:(pc.pl)
#!/usr/bin/perl
use IO::Socket::INET;
print "Client\n";
$c = IO::Socket::INET->new(PeerAddr => "localhost:20000") or die "$!";
while (<>) {
print $c $_
}
The other code for reference:
Perl Server:
#!/usr/bin/perl
use IO::Socket::INET;
print "Server\n";
$s = IO::Socket::INET->new(LocalPort => 20000, Type => SOCK_STREAM, Reuse => 1, Listen => 10, Proto => "tcp") or die "$!";
while ($c = $s->accept) {
print while <$c>;
}
Ruby Client:
require "socket"
c = TCPSocket.new("localhost", 20000)
while l = gets
c.puts l
end
The FIX
I had 3 systems connected in my house and replacing localhost to the actual IP of the current PC resolved the issue. (ipconfig in WINDOWS to get the IP)
Ruby Server:
require "socket"
s = TCPServer.new("192.168.1.3", 20000)
while( c = s.accept)
while l = c.gets
puts l
end
end
Perl Client:
#!/usr/bin/perl
use IO::Socket::INET;
print "Client\n";
$c = IO::Socket::INET->new(PeerAddr => "192.168.1.3:20000") or die "$!";
while (<>) {
print $c $_
}
Thx Abraham
Related
How do I know what ports are on my Macbook?
I'm trying to go through this particular code example from "The Well Grounded Rubyist" regarding TCPServer and threads. The code is below: require 'socket' server = TCPServer.new(3939) connect = server.accept connect.puts "Hi. Here's the date." connect.puts 'date' connect.close server.close How do I know what port is on my Macbook? The docs has 2000 in the example. However, when I try both of these numbers the code doesn't execute, it continues to hang indefinitely. How can I check if these numbers are verified ports? I tried telnetting to the port number and the connection is refused everytime.
server.accepts waits for a client to connect to the server. If that does not happen, it just keeps waiting. Run the code, then open terminal and type: require 'socket' s = TCPSocket.new 'localhost', 3939 At this point you will create TCPSocket, which will connect with your server. This will cause the rest of the code to execute. You can check it with your socket: while line = s.gets # Read lines from socket puts line # and print them end
how to detect if FTP is working or not onremote machine?
I have 3 machines(A, B & C) connected to a Router. A,B & C are in same subnet. All these three machines are interconnected using STAF. I am using machine A as an FTP server & machine B as an FTP client. Using STAF command from machine C I am starting FTP program (TCL script) on machine B. Now the question is, How C will know whether FTP traffic is flowing between A & B?
The ftp package allows you to specify a progress monitor callback in the ftp::Open command: package require ftp proc progressMessage {bytesSoFar} { puts "Transferred $bytesSoFar; looking good..." } set handle [ftp::Open $A $user $pass -progress progressMessage] # Everything after this is just standard for the ftp package if {$handle < 0} { error "could not connect" } if {![ftp::Get $handle $remoteFile $localFile]} { ftp::Close $handle error "could not transfer" } ftp::Close $handle puts "Transfer completed" This will print a message every time a chunk is transferred (the chunk size is configurable in the options to ftp::Open via the -blocksize option; it's 4096 by default). On modern networks, this is probably going to write messages very rapidly…
package require ftp set handle [::ftp::Open $host $user $passwd] if {$handle < 0} { error "Connection refused!" return 0 }
TCP Minecraft Server in Ruby
I'm attempting to create a script in ruby that connects to a Minecraft server via TCP and fetches the current number of players much like the PHP script at http://www.webmaster-source.com/2012/07/05/checking-the-status-of-a-minecraft-server-with-php/ When running the code below I get �Took too long to log in require 'socket' server = TCPSocket.new '192.241.174.210', 25565 while line = server.gets puts line end server.close What am I doing wrong here?
you're not sending this: fwrite($sock, "\xfe"); from the script you linked. You have to send that before you call read, like they do. Basically the server is waiting for you to send data and when you don't after a timeout, you are disconnected.
Why can't I see SNMP Traps coming in?
I'm attempting to use Ruby SNMP to capture SNMP traps from various devices. In order to test them I'm attempting to send them from my laptop using the 'snmptrap' command. I can see that the traps are being sent and arriving at my server (the server is the manager) in packet captures, as well as in the 'snmptrapd' utility when I run it. I'm using the following example code exactly as it is, in the demo from the documentation to set up a TrapListener. require 'snmp' require 'logger' log = Logger.new(STDOUT) m = SNMP::TrapListener.new do |manager| manager.on_trap_default do |trap| log.info trap.inspect end end m.join I'm sending an SNMPv2c trap, and nothing ever appears on the screen... Here is the command I'm using to send a test SMTP trap, in the even that it's useful: snmptrap -v 2c -c public hostname_goes_here SNMP-NOTIFICATION-MIB::snmpNotifyType SNMPv2-MIB::sysLocation Any suggestions appreciated! Thanks!
I was stuck on this for a long time as well. It turns out that by default, Traplistener only opens ports on 127.0.0.1. To make it listen on ALL interfaces on the port you specified (or default port 162), specify a :Host option. '0' makes it listen on ALL interfaces, or you can provide an IP address. log = Logger.new(STDOUT) m = SNMP::TrapListener.new(:Host => 0) do |manager| manager.on_trap_default do |trap| log.info trap.inspect end end m.join
Getting the Hostname or IP in Ruby on Rails
I'm in the process of maintaining a Ruby on Rails app and am looking for an easy way to find the hostname or IP address of the box I'm on (since it's a VM and new instances may have different hostnames or IP addresses). Is there a quick and easy way to do this in Ruby on Rails? Edit: The answer below is correct but the clarification Craig provided is useful (see also provided link in answer): The [below] code does NOT make a connection or send any packets (to 64.233.187.99 which is google). Since UDP is a stateless protocol connect() merely makes a system call which figures out how to route the packets based on the address and what interface (and therefore IP address) it should bind to. addr() returns an array containing the family (AF_INET), local port, and local address (which is what we want) of the socket.
Hostname A simple way to just get the hostname in Ruby is: require 'socket' hostname = Socket.gethostname The catch is that this relies on the host knowing its own name because it uses either the gethostname or uname system call, so it will not work for the original problem. Functionally this is identical to the hostname answer, without invoking an external program. The hostname may or may not be fully qualified, depending on the machine's configuration. IP Address Since ruby 1.9, you can also use the Socket library to get a list of local addresses. ip_address_list returns an array of AddrInfo objects. How you choose from it will depend on what you want to do and how many interfaces you have, but here's an example which simply selects the first non-loopback IPV4 IP address as a string: require 'socket' ip_address = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
From coderrr.wordpress.com: require 'socket' def local_ip orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily UDPSocket.open do |s| s.connect '64.233.187.99', 1 s.addr.last end ensure Socket.do_not_reverse_lookup = orig end # irb:0> local_ip # => "192.168.0.127"
Try this: host = `hostname`.strip # Get the hostname from the shell and removing trailing \n puts host # Output the hostname
A server typically has more than one interface, at least one private and one public. Since all the answers here deal with this simple scenario, a cleaner way is to ask Socket for the current ip_address_list() as in: require 'socket' def my_first_private_ipv4 Socket.ip_address_list.detect{|intf| intf.ipv4_private?} end def my_first_public_ipv4 Socket.ip_address_list.detect{|intf| intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast? and !intf.ipv4_private?} end Both return an Addrinfo object, so if you need a string you can use the ip_address() method, as in: ip= my_first_public_ipv4.ip_address unless my_first_public_ipv4.nil? You can easily work out the more suitable solution to your case changing the Addrinfo methods used to filter the required interface address.
Simplest is host_with_port in controller.rb host_port= request.host_with_port
This IP address used here is Google's, but you can use any accessible IP. require "socket" local_ip = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}
Similar to the answer using hostname, using the external uname command on UNIX/LINUX: hostname = `uname -n`.chomp.sub(/\..*/,'') # stripping off "\n" and the network name if present for the IP addresses in use (your machine could have multiple network interfaces), you could use something like this: # on a Mac: ip_addresses = `ifconfig | grep 'inet ' | grep -v 127.0.0.1 | cut -d' ' -f 2`.split => ['10.2.21.122','10.8.122.12'] # on Linux: ip_addresses = `ifconfig -a | grep 'inet ' | grep -v 127.0.0.1 | cut -d':' -f 2 | cut -d' ' -f 1`.split => ['10.2.21.122','10.8.122.12']
The accepted answer works but you have to create a socket for every request and it does not work if the server is on a local network and/or not connected to the internet. The below, I believe will always work since it is parsing the request header. request.env["SERVER_ADDR"]
Put the highlighted part in backticks: `dig #{request.host} +short`.strip # dig gives a newline at the end Or just request.host if you don't care whether it's an IP or not.
You will likely find yourself having multiple IP addresses on each machine (127.0.0.1, 192.168.0.1, etc). If you are using *NIX as your OS, I'd suggest using hostname, and then running a DNS look up on that. You should be able to use /etc/hosts to define the local hostname to resolve to the IP address for that machine. There is similar functionality on Windows, but I haven't used it since Windows 95 was the bleeding edge. The other option would be to hit a lookup service like WhatIsMyIp.com. These guys will kick back your real-world IP address to you. This is also something that you can easily setup with a Perl script on a local server if you prefer. I believe 3 lines or so of code to output the remote IP from %ENV should cover you.
io = IO.popen('hostname') hostname = io.readlines io = IO.popen('ifconfig') ifconfig = io.readlines ip = ifconfig[11].scan(/\ \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\ /) The couple of answers with require 'socket' look good. The ones with request.blah_blah_blah assume that you are using Rails. IO should be available all the time. The only problem with this script would be that if ifconfig is output in a different manor on your systems, then you would get different results for the IP. The hostname look up should be solid as Sears.
try: Request.remote_ip remote_ip() Determine originating IP address. REMOTE_ADDR is the standard but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR are set by proxies so check for these if REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- delimited list in the case of multiple chained proxies; the last address which is not trusted is the originating IP. Update: Oops, sorry I misread the documentation.