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使える人なら文字コード変換くらいできますよ)