perlでsocketを送受信する

DOS

perlを使ってsocketを送信し、サーバで受信する様子をwiresharkで確認しました。
クライアントからTCP通信を使って2バイト文字を含むStringを送信したところ、文字コードが自動変換されていることが判明しました。

環境

●サーバ 
 VMware Ubuntu 22.04 ※perl 5.34 インストール済
 IP 192.168.19.129
●クライアント 
 Windows11 HOME ※perl 5.34 でPATHを通してあります
 IP 192.168.19.1

準備

■クライアント側の準備
 ①コマンドプロンプト(PowerShell)を管理者権限で起動
 ②Wirersharkを起動し、VMnet8のキャプチャを開始((ip.src==192.168.19.1 && ip.dst==192.168.19.129) || (ip.src==192.168.19.1 && ip.dst==192.168.19.129)でフィルタリングしておきます)
 ③クライアントには、次のクライアントプログラムを配置

# filename send_sock.pl
use strict;
use warnings;
use IO::Socket::INET;
use Getopt::Long;

# 引数を解析してIPアドレスとポート番号を取得
my $ip_address;
my $port;
GetOptions(
    "ip=s" => \$ip_address,
    "p=i"  => \$port,
) or die "Usage: $0 -ip <IP_address> -p <port>\n";

# 引数が正しく指定されていない場合は終了
die "Usage: $0 -ip <IP_address> -p <port>\n" unless $ip_address && $port;

# サーバーに送信するデータ
my $data_to_send = "halo こんにちわ (-_-)";

# TCPソケットを作成してサーバーに接続
my $socket = IO::Socket::INET->new(
    PeerAddr => $ip_address,
    PeerPort => $port,
    Proto    => 'tcp',
) or die "Error in socket creation: $!\n";

# データをサーバーに送信
print $socket $data_to_send;

# ソケットを閉じる
close($socket);

■サーバの準備
 適当な場所に次のプログラムを配置(今回は/home/に配置しました)
 パーミッションは「600」のままです

# filename wait_port.pl
use strict;
use warnings;
use IO::Socket::INET;
use Getopt::Long;

# 引数を解析してポート番号を取得
my $port;
GetOptions(
    "p=i"  => \$port,
) or die "Usage: $0 -p <port>\n";

# 引数が正しく指定されていない場合は終了
die "Usage: $0 -p <port>\n" unless $port;

# ソケットを作成して待ち受ける
my $socket = IO::Socket::INET->new(
    LocalPort => $port,
    Proto     => 'tcp',
    Listen    => SOMAXCONN,
    Reuse     => 1,
) or die "Error in socket creation: $!\n";

print "Server waiting for client connection on port $port...\n";

# クライアントからの接続を待機
my $client_socket = $socket->accept();

# クライアントからのデータを読み取り
my $data_received;
$client_socket->recv($data_received, 1024);
print "Received from client: $data_received\n";

# クライアントとの接続を閉じる
$client_socket->close();

実行(windowsをクライアント、Ubuntをサーバ)

①サーバプログラムを起動(Ubuntu)
 $ > perl wait_port.pl -p 12346
 「Server Watting…」と表示されればOK
②クライアントプログラムを実行(Windows)
 c:\ > perl c:\??\perl_send.pl -ip 192.168.19.129 -p 12346
 と入力すると、今回は
   $data_to_send = “halo こんにちわ (-_-)”;
 が送信されます。

実行結果(Windows → Ubuntuへの送信)

①実行結果(サーバ)

 上のように表示され、Stringが無事に送信されていることがわかります。
 心配していた2バイト文字の日本語も無事送信できています、が、無事に遅れていることがおかしい?
 Powershell の標準文字コードはShift-JIS だったと思うのですが、変わっているのかと思い、
   c:\ > chcp
   現在のコード ページ: 932
 で文字コードを確認したところ、やっぱりShift-JISになっています。
  Ubuntuの文字コードを確認したところ
   $ > echo $LANG
   ja_JP.UTF-8
 で、UTF8になっていることが確認できました。
②Wiresharkの確認結果

SYN ,SYN/ACK,ACK の3ウェイハンドシェイクがきれいに出ていますね。
PSH(PUSH)で”halo こんにちわ (-_-)”を送信している部分を確認すると、
  68 61 6c 6f 20 e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 82 8f 20 28 2d 5f 2d 29
のバイトデータを送信しています。
 PUSHした文字列をエンコードすると
  ●UTF-8
  68 61 6c 6f 20 e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 82 8f 20 28 2d 5f 2d 29
  ●Shift-JIS
  68 61 6c 6f 20 82 b1 82 f1 82 c9 82 bf 82 ed 20 28 2d 5f 2d 29
ですので、Windowsから外部に
  UTF-8
で送信されていることがわかります。
 だから、Ubuntuに日本語を送信しても文字化けしないのですね。(納得)

実行(Ubuntuをクライアント、Windowsをサーバ)

①サーバプログラムを起動(Windows)
  c:\ > perl c:\??\wait_port.pl -p 12346
  「Server Watting…」と表示されればOK
②クライアントプログラムを実行(Ubuntu)
  $ > perl /HOME/perl_send.pl -ip 192.168.19.1 -p 12346
  「Server Watting…」と表示されればOK

実行結果(Ubuntu → Windowsへの送信)

 実行結果
 Windowsをサーバにしたところ、実行結果が返ってきません。

3ウェイハンドイェイクがそもそもできておらず、UbuntuからのSYNが一方通行で返事がない、片思い状態です。
Windows Defenderがいらぬ気を使ってくれたのだと思い、
  スタートボタン→設定→プライバシーとセキュリティ→ファイヤーウォールとネットワーク保護
  パブリックネットワークのファイヤーウィール を「OFF」
にして再実験したところ、ようやく通信がつながりました。(ファイヤーウォールは悪くないんですが…)

 UbuntuからのSocketを受信した様子

 しかし、Ubuntuから送られてきた日本語は文字化けしています・・・
 文字コードを確認すると、UTF-8で送られてきていました。

 以上の様子から、Windowsは外部との通信で2バイト文字を使用する場合
   2バイト文字を送信する際は、勝手に「UTF-8」に変換する
   外部から「UTF-8」で送られてきた2バイト文字は変換しない
ことが判明しました。
 Socketを使って日本語を送受信する場合は、混乱しそうですね。
 Microsoftさん がんばってください!
  (PowershellもUTF-8にしたらどうでしょうか。Powershell使える人なら文字コード変換くらいできますよ)