以下の内容はhttps://ryuichi1208.hateblo.jp/entry/2025/02/15/003341より取得しました。


【Perl】特定の対向のポートに対してtcpのsynを投げてエラーになった場合にprocfsからtcpの再送メトリクスを取る

なぜかconnection timeoutが発生しまくる事象が起きてその際の再送をみるみたいなことをしたくてコンテナで動かせるようのやつ。仕事で使うことは滅多にない気がするがサッとあると便利。(ChatGPTだと頑なにIO::Socket::INETを使ってくれなくて微妙に困った)

#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;

# --- 対象のリモートホストとポート --- 
# コマンドライン引数で指定(例: ./tcp_syn_procfs.pl 192.168.1.1 80)
my $remote_ip   = $ARGV[0] // '192.168.1.1';
my $remote_port = $ARGV[1] // 80;

# --- TCP接続の試行(内部でSYNを送信) ---
my $socket = IO::Socket::INET->new(
    PeerAddr => $remote_ip,
    PeerPort => $remote_port,
    Proto    => 'tcp',
    Timeout  => 2,
);

if ( !$socket ) {
    print "接続失敗 ($remote_ip:$remote_port): $!\n";
    
    # --- /proc/net/snmp から TCP 再送メトリクスの取得 ---
    if ( open( my $snmp_fh, '<', '/proc/net/snmp' ) ) {
        my @lines = <$snmp_fh>;
        close($snmp_fh);
        
        my ( $tcp_header_line, $tcp_value_line );
        # /proc/net/snmp には "Tcp:" という2行セットがあるので探す
        for ( my $i = 0 ; $i < @lines ; $i++ ) {
            if ( $lines[$i] =~ /^Tcp:\s*(.*)/ ) {
                $tcp_header_line = $1;
                if ( $i + 1 < @lines and $lines[ $i + 1 ] =~ /^Tcp:\s*(.*)/ ) {
                    $tcp_value_line = $1;
                    last;
                }
            }
        }
        
        if ( $tcp_header_line && $tcp_value_line ) {
            my @headers = split( /\s+/, $tcp_header_line );
            my @values  = split( /\s+/, $tcp_value_line );
            my $found   = 0;
            for ( my $i = 0 ; $i < @headers ; $i++ ) {
                if ( $headers[$i] eq 'RetransSegs' ) {
                    print "TCP RetransSegs: $values[$i]\n";
                    $found = 1;
                    last;
                }
            }
            print "RetransSegs フィールドが見つかりませんでした。\n" unless $found;
        }
        else {
            print "TCP メトリクス情報が見つかりませんでした。\n";
        }
    }
    else {
        warn " /proc/net/snmp をオープンできません: $!\n";
    }
}
else {
    print "接続成功 ($remote_ip:$remote_port)\n";
    close($socket);
}

ついでにRuby版も書いてもらった。仕事で使っていたコンテナにRubyは入ってなかったので試してはないです。

#!/usr/bin/env ruby
require 'socket'
require 'timeout'

# --- 対象のリモートホストとポート ---
remote_ip   = ARGV[0] || '192.168.1.1'
remote_port = (ARGV[1] || '80').to_i

begin
  # タイムアウト付きでTCP接続を試みる(内部的にSYN送信)
  Timeout.timeout(2) do
    socket = TCPSocket.new(remote_ip, remote_port)
    puts "接続成功 (#{remote_ip}:#{remote_port})"
    socket.close
    exit 0
  end
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Timeout::Error => e
  puts "接続失敗 (#{remote_ip}:#{remote_port}): #{e.message}"

  # --- /proc/net/snmp から TCP 再送メトリクスの取得 ---
  begin
    lines = File.readlines('/proc/net/snmp')
    tcp_header_line = nil
    tcp_value_line  = nil

    lines.each_with_index do |line, idx|
      if line.start_with?("Tcp:")
        # 最初の "Tcp:" 行をヘッダー行とし、次の行を値行とする
        unless tcp_header_line
          tcp_header_line = line.split[1..-1]  # "Tcp:" を除く
          if idx + 1 < lines.size && lines[idx + 1].start_with?("Tcp:")
            tcp_value_line = lines[idx + 1].split[1..-1]
          end
          break
        end
      end
    end

    if tcp_header_line && tcp_value_line
      index = tcp_header_line.index("RetransSegs")
      if index
        puts "TCP RetransSegs: #{tcp_value_line[index]}"
      else
        puts "RetransSegs フィールドが見つかりませんでした。"
      end
    else
      puts "TCP メトリクス情報が見つかりませんでした。"
    end
  rescue => ex
    warn "/proc/net/snmp を読み込めませんでした: #{ex.message}"
  end
rescue => e
  warn "予期しないエラーが発生しました: #{e.message}"
end



以上の内容はhttps://ryuichi1208.hateblo.jp/entry/2025/02/15/003341より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14