3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

個人利用のためのインターネット速度テストツールの紹介

Posted at

皆さん、こんにちは!今日は、私が個人利用のために開発したインターネット速度テストツールを紹介したいと思います。このツールは、ユーザーのインターネット接続速度を簡単にテストできるように設計されています。また、リンクを皆さんと共有します。以下に、コードとその動作について詳しく説明します。

[SpeedTest] (https://speedtest.tehito.com)

Screenshot 745.png

コードの説明

speedtest.js

// speedtest.js

var worker = new Worker('speedtest_worker.js');
worker.onmessage = function (event) {
    if (event.data.type === 'result') {
        // 結果の処理
        console.log('ダウンロード速度: ' + event.data.downloadSpeed + ' Mbps');
        console.log('アップロード速度: ' + event.data.uploadSpeed + ' Mbps');
    }
};

function startSpeedtest() {
    worker.postMessage('start');
}

このスクリプトは、Webワーカー(speedtest_worker.js)を作成し、速度テストの結果を処理します。startSpeedtest関数は、ワーカーにメッセージを送り、速度テストを開始します。

speedtest_worker.js

// speedtest_worker.js

self.onmessage = function (event) {
    if (event.data === 'start') {
        // ダウンロード速度とアップロード速度を計算するコード
        var downloadSpeed = calculateDownloadSpeed();
        var uploadSpeed = calculateUploadSpeed();
        self.postMessage({
            type: 'result',
            downloadSpeed: downloadSpeed,
            uploadSpeed: uploadSpeed
        });
    }
};

function calculateDownloadSpeed() {
    // ダウンロード速度を計算するロジック
    return Math.random() * 100; // プレースホルダーデータ
}

function calculateUploadSpeed() {
    // アップロード速度を計算するロジック
    return Math.random() * 100; // プレースホルダーデータ
}

このスクリプトは、メインスクリプトからメッセージを受け取ったときにダウンロード速度とアップロード速度を計算し、その結果をメインスクリプトに送信します。速度の計算はここではプレースホルダーデータを使用しています。

getIP.php

<?php

error_reporting(0);

define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');

require_once 'getIP_util.php';

function getLocalOrPrivateIpInfo($ip)
{
    if ('::1' === $ip) {
        return 'localhost IPv6 access';
    }
    if (stripos($ip, 'fe80:') === 0) {
        return 'link-local IPv6 access';
    }
    if (strpos($ip, '127.') === 0) {
        return 'localhost IPv4 access';
    }
    if (strpos($ip, '10.') === 0) {
        return 'private IPv4 access';
    }
    if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
        return 'private IPv4 access';
    }
    if (strpos($ip, '192.168.') === 0) {
        return 'private IPv4 access';
    }
    if (strpos($ip, '169.254.') === 0) {
        return 'link-local IPv4 access';
    }
    return null;
}

function getIpInfoTokenString()
{
    if (!file_exists(API_KEY_FILE) || !is_readable(API_KEY_FILE)) {
        return '';
    }

    require API_KEY_FILE;

    if (empty($IPINFO_APIKEY)) {
        return '';
    }

    return '?token=' . $IPINFO_APIKEY;
}

function getIspInfo($ip)
{
    $json = file_get_contents('https://ipinfo.io/' . $ip . '/json' . getIpInfoTokenString());
    if (!is_string($json)) {
        return null;
    }

    $data = json_decode($json, true);
    if (!is_array($data)) {
        return null;
    }

    return $data;
}

function getIsp($rawIspInfo)
{
    if (is_array($rawIspInfo)) {
        if (array_key_exists('org', $rawIspInfo) && is_string($rawIspInfo['org']) && !empty($rawIspInfo['org'])) {
            return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
        }

        if (array_key_exists('asn', $rawIspInfo) && is_array($rawIspInfo['asn']) && !empty($rawIspInfo['asn']) && array_key_exists('name', $rawIspInfo['asn']) && is_string($rawIspInfo['asn']['name'])) {
            return $rawIspInfo['asn']['name'];
        }
    }

    return 'Unknown ISP';
}

function getServerLocation()
{
    $serverLoc = null;
    if (file_exists(SERVER_LOCATION_CACHE_FILE) && is_readable(SERVER_LOCATION_CACHE_FILE)) {
        require SERVER_LOCATION_CACHE_FILE;
    }
    if (is_string($serverLoc) && !empty($serverLoc)) {
        return $serverLoc;
    }

    $json = file_get_contents('https://ipinfo.io/json' . getIpInfoTokenString());
    if (!is_string($json)) {
        return null;
    }

    $details = json_decode($json, true);
    if (!is_array($details) || !array_key_exists('loc', $details) || !is_string($details['loc']) || empty($details['loc'])) {
        return null;
    }

    $serverLoc = $details['loc'];
    $cacheData = "<?php\n\n\$serverLoc = '" . addslashes($serverLoc) . "';\n";
    file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);

    return $serverLoc;
}

function distance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo)
{
    $rad = M_PI / 180;
    $theta = $longitudeFrom - $longitudeTo;
    $dist = sin($latitudeFrom * $rad) * sin($latitudeTo * $rad) + cos($latitudeFrom * $rad) * cos($latitudeTo * $rad) * cos($theta * $rad);
    return acos($dist) / $rad * 60 * 1.853;
}

function getDistance($rawIspInfo)
{
    if (!is_array($rawIspInfo) || !array_key_exists('loc', $rawIspInfo) || !isset($_GET['distance']) || !in_array($_GET['distance'], ['mi', 'km'], true)) {
        return null;
    }

    $unit = $_GET['distance'];
    $clientLocation = $rawIspInfo['loc'];
    $serverLocation = getServerLocation();

    if (!is_string($serverLocation)) {
        return null;
    }

    return calculateDistance($clientLocation, $serverLocation, $unit);
}

function calculateDistance($clientLocation, $serverLocation, $unit)
{
    list($clientLatitude, $clientLongitude) = explode(',', $clientLocation);
    list($serverLatitude, $serverLongitude) = explode(',', $serverLocation);
    $dist = distance($clientLatitude, $clientLongitude, $serverLatitude, $serverLongitude);

    if ('mi' === $unit) {
        $dist /= 1.609344;
        $dist = round($dist, -1);
        if ($dist < 15) {
            $dist = '<15';
        }

        return $dist . ' mi';
    }

    if ('km' === $unit) {
        $dist = round($dist, -1);
        if ($dist < 20) {
            $dist = '<20';
        }

        return $dist . ' km';
    }

    return null;
}

function sendHeaders()
{
    header('Content-Type: application/json; charset=utf-8');

    if (isset($_GET['cors'])) {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST');
    }

    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
    header('Cache-Control: post-check=0, pre-check=0', false);
    header('Pragma: no-cache');
}

function sendResponse($ip, $ipInfo = null, $distance = null, $rawIspInfo = null)
{
    $processedString = $ip;
    if (is_string($ipInfo)) {
        $processedString .= ' - ' . $ipInfo;
    }

    if (is_array($rawIspInfo) && array_key_exists('country', $rawIspInfo)) {
        $processedString .= ', ' . $rawIspInfo['country'];
    }

    if (is_string($distance)) {
        $processedString .= ' (' . $distance . ' distance to server)';
    }

    $response = [
        'rawIp' => $ip,
        'ip' => $processedString,
        'isp' => getIsp($rawIspInfo),
        'loc' => (is_array($rawIspInfo) && array_key_exists('loc', $rawIspInfo)) ? $rawIspInfo['loc'] : null,
        'distance' => $distance,
    ];

    echo json_encode($response);
}

sendHeaders();

$ip = getClientIp();

$ipInfo = getLocalOrPrivateIpInfo($ip);
if (is_string($ipInfo)) {
    sendResponse($ip, $ipInfo);
    return;
}

$rawIspInfo = getIspInfo($ip);

sendResponse($ip, $ipInfo, getDistance($rawIspInfo), $rawIspInfo);
?>

このスクリプトは、クライアントのIPアドレスを検出し、ipinfo.ioからISP情報を取得します。結果はJSON形式で返されます。

  • getLocalOrPrivateIpInfo関数は、ローカルまたはプライベートIPアドレスに対して特別なメッセージを返します。

  • getIpInfoTokenString関数は、APIキーが設定されている場合にトークン文字列を生成します。

  • getIspInfo関数は、ipinfo.ioからISP情報を取得します。

  • getIsp関数は、ISP名を解析します。

  • getServerLocation関数は、サーバーの位置情報を取得します。

  • distance関数は、2点間の距離を計算します。

  • getDistance関数は、クライアントとサーバー間の距離を計算します。

  • sendHeaders関数は、適切なヘッダーを送信します。

  • sendResponse関数は、応答を生成し送信します。

getIP_util.php

<?php

/**
 * @return string
 */
function getClientIp() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
        $ip = $_SERVER['HTTP_X_REAL_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        $ip = preg_replace('/,.*/', '', $ip); # ホストはコンマ区切りであり、クライアントは最初に位置します
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }

    return preg_replace('/^::ffff:/', '', $ip);
}
?>

このスクリプトは、クライアントのIPアドレスを取得する関数を提供します。

getClientIp関数は、クライアントのIPアドレスを取得します。複数のヘッダーをチェックし、最初に見つかった有効なIPアドレスを返します。

garbage.php

<?php

// 圧縮を無効にする
@ini_set('zlib.output_compression', 'Off');
@ini_set('output_buffering', 'Off');
@ini_set('output_handler', '');

/**
 * @return int
 */
function getChunkCount()
{
    if (
        !array_key_exists('ckSize', $_GET)
        || !ctype_digit($_GET['ckSize'])
        || (int) $_GET['ckSize'] <= 0
    ) {
        return 4;
    }

    if ((int) $_GET['ckSize'] > 1024) {
        return 1024;
    }

    return (int) $_GET['ckSize'];
}

/**
 * @return void
 */
function sendHeaders()
{
    header('HTTP/1.1 200 OK');

    if (isset($_GET['cors'])) {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST');
    }

    // ファイルのダウンロードを示す
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=random.dat');
    header('Content-Transfer-Encoding: binary');

    // キャッシュ設定: このリクエストをキャッシュしない
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
    header('Cache-Control: post-check=0, pre-check=0', false);
    header('Pragma: no-cache');
}

// 送信するデータ量を決定
$chunks = getChunkCount();

// データを生成
if (function_exists('random_bytes')) {
    $data = random_bytes(1048576);
} else {
    $data = openssl_random_pseudo_bytes(1048576);
}

// 1048576バイトのチャンクを送信
sendHeaders();
for ($i = 0; $i < $chunks; $i++) {
    echo $data;
    flush();
}
?>

このスクリプトは、ランダムなデータを生成し、クライアントに送信します。これは主にアップロード速度のテストに使用されます。

  • getChunkCount関数は、送信するデータチャンクの数を決定します。
  • sendHeaders関数は、適切なHTTPヘッダーを送信します。
  • データ生成と送信のためのロジックが含まれています。

まとめ

このインターネット速度テストツールは、ユーザーが簡単に接続速度をテストできるように設計されています。各コード部分の理解が深まることで、ツールを最大限に活用できるようになります。

3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?