socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う

このエントリーをはてなブックマークに追加
はてなブックマーク - socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う

Express3.x(connect2.3+)とSocket.IOでのセッション管理mochaとsuperagentでexpressを使ったサービスのログイン周りのテストの続き。

今回はsocket.ioの同時接続のテストについて。今回は

  • 1000人同時接続が出来るか
  • 正しくメッセージのやりとりが出来るか(チャットなので)
  • ログイン済みユーザーのみが接続できるか
のテストを行なってみました。

socket.io-clientを用いれば、コンソールから同時接続のテストが簡単に出来ます。
ただし、環境によってはOS自体のファイルディスクリプタの最大数を引き上げないと1000人同時接続は難しいです。


ソースコード

今回は今までのを全て含めたものをgithubに上げています。
pxsta / express-socket.io-chat-test


今回の環境

  • Mac OSX 10.7.4 Lion
  • socket.io@0.9.10
  • socket.io-client@0.9.10
  • express@3.0.0rc3


やったこと

ファイルディスクリプタの最大数の変更(環境によっては不要)

1000人同時接続のテストを行う際にはOS自体のファイルディスクリプタの最大数を引き上げる必要があります。
少なくともMacでは256などというとても低い値に設定されているのでそのままでは無理です。

Mac OSX 10.7.4 Lionの環境で1000人接続のテストを行った所、117人あたりで接続を受け付けなくなるか、以下のようなエラーで止まってしまいまいした。
[ERROR] console - Error: EBADF, Bad file descriptor
/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:68
                .replace('{style}', style)
                 ^
TypeError: Cannot call method 'replace' of undefined
    at /node_modules/express/node_modules/connect/lib/middleware/errorHandler.js:68:18
    at [object Object]. (fs.js:80:5)
    at [object Object].emit (events.js:64:17)
    at fs.js:820:12

Macの場合は
$ ulimit -n
256
$ sysctl kern.maxfilesperproc
kern.maxfilesperproc: 10240
$ ulimit -n  10240
$ ulimit -n  
10240
のようにして変更可能です。ulimitにkern.maxfilesperprocの値を設定すれば最大値まで引き上げられます。


socket.io-clientによる同時接続のテスト(1000人くらい)

socket.io-clientを用いれば同時接続のテストをコンソールから行うことが出来ます。
以下のように、接続自体は今までクライアント側で書いていたものをサーバ側でそのまま使うことが出来ます。
var io = require('socket.io-client');
var options = {
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : 3000
};

//接続
var socket = io.connect("",options);
socket.on('connect', function(data) {
    done("wrong behaviour");
});

//メッセージの送信
socket.emit("chat","send message test");

そのため、複数人接続のテストはmochaと組み合わせて以下のようにして簡単に出来ます。
var should = require('should')
  , expect = require('expect.js');
var options = {
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : 3000,
};

//socket.ioの接続テスト
describe('socket.io test', function() {
    it('1000件接続出来るかどうか。ファイルディスクリプタの最大値を大きくしないと一度に接続できない', function(done) {
        this.timeout(600000);
        var maxCount = 100;
        var clientCount = 0;
        var clients = [];
        
        //全てが接続し終わったら全て切断してから終了
        var end = function() {
            for (var k=0;k<clients.length;k++) {
                clients[k].disconnect();
            }
            done();
        }; 
        
        //maxCount個のクライアントを接続する
        for (var i = 0; i < maxCount; i++) {
            var client = helper.login({
                auth : {
                    userID : 'test' + i,
                    password : 'test'
                },
                server : {
                    host : "",
                    details : options
                }
            }, function(err, client) {
                clients.push(client);
                
                //接続出来たクライアントがmaxCount個に達するまで待つ
                client.on('connect', function(data) {
                    if ((++clientCount) == maxCount) {
                        end();
                    }
                });
            });
        }
    });
});
接続する際のパラメータを
'force new connection':true
と設定しておかないと全てが1つのコネクションとして扱われてしまいます。
パラメータさえ指定しておけば後は繋ぎたいだけforで回して接続すれば出来てしまいます。簡単!


socket.ioの認証まわりのテスト

socket.io-clientではcookieをセットすることができません。

socket.ioの接続認証はexpressのセッションIDをcookieより取得、さらにそのセッションIDを元にsessionStoreからセッションデータを取得、正しいセッションデータがあれば接続を認める、という流れになっています。

そのため、cookieが無いとsocket.ioの認証をそもそも受けられないのですが、
expressやsocket.ioのテストはこんな感じで書いてます、というお話 – アルパカDiary
という素晴らしいエントリーが合ったので参考にさせていただきました。

socket.io-client/lib/socket.js内のio.Socket.prototype.handshakeをsupport/helper.js内でオーバーライドしてしまい、
cookieをx-set-cookieというキーでヘッダに格納しているようです。

support/helper.jsに補助的なメソッドを追加&テスト用にcookieをセットする、という手法を使わせて頂き、
socket.ioの認証テストは以下のように。

var should = require('should')
  , expect = require('expect.js')
  , helper = require('./support/helper')
  , app = require('../app.js')

var options = {
    transports : ['websocket'],
    'force new connection' : true,  //別々のコネクションとして認識させるために必要
    port : app.get('port'),
};


//socket.ioの接続テスト
describe('socket.io test', function() {
    before(function(done) {
        helper.initDataStore(done);
    });
    
    it('ログイン前はsocket.ioで接続できないはず', function(done) {
        this.timeout(10000);
        helper.login({
            auth : {
                userID : 'test',
                password : 'wrong password'
            },
            server : {
                host : "",
                details : options
            }
        }, function(err, client) {
            //接続できてはいけない
            client.on('connect', function(data) {
                done("wrong behaviour");
            });
            
            //エラーが起こるはず
            client.on('error', function(data) {
                //ハンドシェイクのエラーのはず
                should.equal(data.toString(),"handshake error");
                done();
            });
        });
    });
    it('ログインした後はsocket.ioで接続できるはず', function(done) {
        this.timeout(10000);
        helper.login({
            auth : {
                userID : 'test',
                password : 'test'
            },
            server : {
                host : "",
                details : options
            }
        }, function(err, client) {
            client.on('connect', function(data) {
                //接続完了するはず
                done();
            });
        });
    });
});
helper.loginではログインしてexpressのセッションの発行を受けたのち、コールバック関数でsocket.io-clientのsocketインスタンスを返しています。

socket.ioの認証でエラーが起きるとerrorイベントが発火するためclient.on(‘error’, function(data) {})の用にしてそのエラーをキャッチするようにしたいます。
testUserはログイン済みのため接続できてconnectionイベントが発火しますが、invalidUserはログインしていないため接続できずにerrorイベントが発火します。


メッセージ交換が出来るかどうかのテストも以下のように簡単に行うことが出来ます。
    it('メッセージの送受信がclient0とclient1でできるかどうか', function(done) {
        this.timeout(10000);
        var maxCount = 2;
        var clientCount = 0;
        var clients = [];
        
        var connectCompleat = function() {
            clients[0].on("message",function(message){
                //client0が受信したメッセージはclient1が送信したメッセージなはず
                should.equal(message, clients[1].userID + ' ' + "message");
                
                //テスト終了前に切断する
                clients[0].disconnect();
                clients[1].disconnect();
                done();
            });
            clients[1].emit("chat","message");
        }; 
        
        for (var i = 0; i < maxCount; i++) {
            (function(userID) {
                var client = helper.login({
                    auth : {
                        userID : userID,
                        password : 'test'
                    },
                    server : {
                        host : "",
                        details : options
                    }
                }, function(err, client) {
                    client.userID = userID;
                    clients.push(client);
                    
                    //client0とclient1が接続するまで待つ
                    client.on('connect', function(data) {
                        if ((++clientCount) == maxCount) {
                            connectCompleat();
                        }
                    });
                });
            })('test' + i);
        }
    });

1000人同時接続のテストの際にファイルディスクリプタ最大数が原因だと気づかずにはまってしまいましたが、
それさえ忘れなければ普段クライアントで利用しているコードがサーバ側でも使えるのは便利ですね。


前々回・前回・今回のサンプルコードを全て含めたものはpxsta / express-socket.io-chat-testです。

このエントリーをはてなブックマークに追加
はてなブックマーク - socket.io-clientを用いてExpress+Socket.IOで作ったサービスへ同時接続テストを行う

mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト

このエントリーをはてなブックマークに追加
はてなブックマーク - mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト

前回のexpress+soket.ioでのセッション管理に続いて、今回はexpressのセッション管理のテストについて。

mochasuperagentを使えば

  • ページの表示確認(ステータスコード・ページbody本文)
  • ログインページヘのpostで正しくログイン出来るか
  • ログイン状態管理(セッションでの振り分けが出来ているか・リダイレクトが正しく行われているか)
などのログイン周りのテストが簡単に出来るようなので試してみました。

ソースコードとサンプル

コード
mochaとsuperagentを用いてexpressのアプリのログイン周りのテスト — Gist
.
├── app.js
├── chat.html
├── index.html
├── node_modules
│   ├── connect-redis
│   ├── cookie
│   ├── ejs
│   ├── express
│   ├── should
│   ├── socket.io
│   └── superagent
├── package.json
└── test
     ├── app.test.js
     └── mocha.opts


テスト対象の動作サンプル
Socket.io-Express3.0.0rc3 session sample
簡単なチャット。passwordがtestなら何でもログイン可能。やけに時間がかかるのはherokuだから?


やったこと

インストール

npm install mocha
npm install superagent
npm install should
shouldもとりあえず。
mochaについてはテストフレームワーク mocha – hokaccha.hamalog v2がとても参考になりました。

app.js側の準備

基本的に前回とほとんど同じですが、expressの設定とsessionStoreをテストコードから参照するために2行ほど書き換えます。
var app = module.exports = express();
app.configure(function() {
    //テスト用
    app.set("sessionStore",sessionStore);
});


ログインが必要なページのテスト

まず、/chatにアクセスするためにはログインが必要。
ログイン前に直接アクセスすると/にリダイレクトされるはずです。そのテストを行うコードは次のようになります。
var url = require('url')
  , should = require('should')
  , assert = require('assert')
  , superagent = require('superagent');

describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();
    
    it('ログイン前にチャットページにアクセスしたらログインページにリダイレクトされるはず', function(done) {
        testUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/");
            done();
        });
    });
});
superagent.agent()でユーザーを作成して、
getでHTTP GETの要求を/chatにした後リダイレクト先が/であることを確かめています。

testディレクトリにapp.test.jsなどという名前で保存してmochaを実行すれば
$ mocha
   info  - socket.io started

  ․

  ✔ 1 test complete (17ms)
などと表示され無事にテストが通ったことが分かります。

ログイン処理のテスト

次に、/ページではログインを行います。
ログインフォームは
<form action="/user/login" method="post">
    ID: <input type="text"  name="userID" value="test"/> <br/>
    PASS: <input type="text"  name="password" value ="test"/>
    <input type="submit" value="login" />
</form>
postを受け付ける部分は
//ログイン処理
app.post('/user/login', function(req, res) {
    var postData = {
        userID : req.body.userID,
        password : req.body.password
    };
    //passがtestならログイン成功させる
    if ( typeof postData.userID !== 'undefined' && typeof postData.password !== 'undefined' && postData.password.toString() === 'test') {
        //sessionにユーザーID保存
        req.session.userID = postData.userID.toString();
        res.redirect('/chat');
    }
    else {
        console.log('login failed');
        res.redirect('/');
    }
});
のようになっていて、/user/loginにHTTP POSTでuserID, passwordを送るようになっています。

superagentではユーザーごとのcookieの管理が出来るため、セッション管理のテストもできます。
このログイン処理のテストを行うコードをmocha+superagentで書くと以下のように。
describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();  //正規にログインするユーザー
    var invalidUser = superagent.agent(); //ログインしないユーザー

    it('ログインに成功したら/chatにリダイレクトされるはず', function(done) {
        //ログインページに対してのPOST送信でログイン処理を行う
        testUser.post('http://localhost:3000/user/login').send({
            userID : 'test',
            password : 'test'
        }).end(function(err, res) {
            should.not.exist(err);
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/chat"); 
            done();
        });
    });

    it('ログイン済みのtestUserは/chatに直接アクセスすると/chatにアクセスできるはず', function(done) {
        testUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);

            //ページが表示され、それは/chatなはず
            should.equal(res.statusCode, 200);
            should.equal(res.req.path, "/chat");
            done();
        });
    });

    it('ログインしてないinvalidUserは/chatに直接にしても/にリダイレクトされるはず', function(done) {
        invalidUser.get('http://localhost:3000/chat').end(function(err, res) {
            should.not.exist(err);

            //リダイレクト先が存在し、/にリダイレクトされるはず
            res.redirects.should.not.be.empty;
            should.equal(url.parse(res.redirects[0], false, true).pathname, "/");
            done();
        });
    });
});

testUserとinvalidUserの2ユーザーを作り、testUserは.post(‘user/login’).send({userID : ‘test’,password : ‘test’})でログイン処理を。
testUserはログイン済みのため/chatにアクセスできますが、invalidUserは未ログインなため/にリダイレクトされます。

セッションストアにデータが収められているかのテスト

最後に、sessionStoreに正しくセッション情報が保存されているかどうかのテスト。
ヘッダのcookieからセッションIDを取り出して、それを元にセッションストアからセッションデータを取り出してユーザー名を確かめます。
これはsocket.ioの認証時にも同じ事をしています。

var url = require('url')
  , connect = require("express/node_modules/connect")
  , should = require('should')
  , assert = require('assert')
  , superagent = require('superagent')
  , app = require('../app.js');


describe('HTTP Server Test', function() {
    //テストに使用するユーザーを作成
    var testUser = superagent.agent();

    it('ログインに成功したらセッションストレージにユーザー名が保存されているはず', function(done) {
        testUser.post('http://localhost:3000/user/login').send({
            userID : 'test',
            password : 'test'
        }).end(function(err, res) {
            should.not.exist(err);
            
            //sessionStoreにsessionデータが保存されているはず
            var header_cookie = res.req._headers.cookie;
            var cookie = require('cookie').parse(decodeURIComponent(header_cookie));
            cookie = connect.utils.parseSignedCookies(cookie, app.get('secretKey'));
            var sessionID = cookie[app.get('cookieSessionKey')];
            
            app.get("sessionStore").get(sessionID,function(err,session){
                 should.not.exist(err);
                 
                 //userIDがログイン時のものと一致するはず
                 should.equal(session.userID,"test");
                 done();
            });
        });
    });
});
sessionStoreにセッション情報が保存されていることを確認。

と、いうことで、mocha+superagentを使えばログイン処理まわりのテストも簡単に出来そうです!
superagentを使えば複数人の接続のテストも出来るかと!

今回のサンプルコードはここにまとめて置いておきました。
https://gist.github.com/3968061

このエントリーをはてなブックマークに追加
はてなブックマーク - mochaとsuperagentでexpressを使ったサービスのログイン周りのテスト

Express3.x(connect2.3+)とSocket.ioでのセッション管理

このエントリーをはてなブックマークに追加
はてなブックマーク - Express3.x(connect2.3+)とSocket.ioでのセッション管理

久しぶりにnode.jsを触ったらexpressがいつの間にか2.xから3.0.3rcになっていて微妙に挙動が違う!
そしてconnect@2.3.2からはparseCookieが無くなってて昔のままだとセッションの共有が出来ない!


そして一番ハマったのはセッションIDが署名されているにも関わらずcookieからparseCookieで取り出した値をそのままセッションIDとして使っていたために、
req.sessionIDとcookieから取り出したsessionIDが別のものになっていて
sessionStoreから正しくセッションデータを取り出せなかった、ってこと。
署名したセッションID入りのcookieはconnect.utils.parseSignedCookies(cookies:object, secret:string)で元に戻しましょう。

と、いうことでexpress3.xとsocket.ioを使った簡単なチャットを新たに作ってみました。

今回の環境

  • node.js v0.8.7
  • express@3.0.0rc3
  • connect@2.4.3
  • socket.io@0.9.10

ソースコードとサンプル

コード
Socket.io-Express3.x-session — Gist

サンプル
Socket.io-Express3.0.0rc3 session sample
簡単なチャット。passwordがtestなら何でもログイン可能。やけに時間がかかるのはherokuだから?
(herokuってこんなに遅いんですか?)

やったこと

connect2.3以降はparseCookieがconnect内に無いため、変わりにcookieモジュールを使う。npmから普通に入る。
npm install cookie

express3.xからはexpress()がhttp.Server型のオブジェクトを返さなくなったため、http.createServerでhttp.Server型のインスタンスを生成してからsocket.ioに渡す。
var express = require('express')
  , app = express()
  , http = require('http')
  , io = require('socket.io').listen(http.createServer(app).listen(3000));

また、express.sessionミドルウェアがsecretパラメータを使用しなくなり、変わりにexpress.cookieParserがsecretパラメータを受け付けるようになった。
secretパラメータの値を元に、expressのセッションが署名される。
//メモリストアかRedisのどちらかでセッションを保存
//var sessionStore = new express.session.MemoryStore()
var RedisStore = require('connect-redis')(express)
  , sessionStore = new RedisStore();

app.configure(function() {
    app.set('secretKey', 'mySecret');
    app.set('cookieSessionKey', 'sid');

    //expressでセッション管理を行う
    app.use(express.cookieParser(app.get('secretKey')));     //セッションの署名に使われるキーを設定
    app.use(express.session({
        key : app.get('cookieSessionKey'),     //cookieにexpressのsessionIDを保存する際のキーを設定
        store : sessionStore
    }));
});
express.cookieParserに渡した値でセッションが署名される。
また、express.sessionのkeyパラメータはブラウザにセッションIDが保存される際のキーとなる。
storeに指定したストレージにセッション情報が保存される。今回はRedis。


そして肝心のsocket.ioの認証部分。ここでsocket.ioとexpressのセッション情報を紐つける。


今までのようにconnectのparseCookieが使えないため、cookieモジュールのparseCookieを用いる。
また、cookie中のセッションIDは署名されているため、単にparseCookieに掛けただけではreq.sessionIDと異なるセッションIDしか取れない。sessionStoreにはreq.sessionIDと同じ値がキーとなって保存されているためそれは不都合。

res.cookie(name, val, options)にてcookieをセットする際にutils.sign(val, secret)にて署名が行われ、セッションIDは
s:' + セッションID + '.' + secretのハッシュ値
という形に変換されている。
そのためpaeseCookieだけでは「s:本来のセッションID.ハッシュ値」という値しか取れない。

さらに、handshakeData.headers.cookieはURLエンコードされているので、
s%3A:' + URLエンコードされたセッションID + '.' + secretのハッシュ値
というように頭の「s:」 が「s%3A」となって格納されている。

そこで、decodeURIComponentでURLデコードしてたのち同じくutils.js内のutil.parseSignedCookiesを用いて本来のセッションIDを取得する。


ということでsocket.ioの認証部分。parseSignedCookiesにcookieのオブジェクトと署名に用いた値を渡すだけ。
//socket.ioのコネクション認証時にexpressのセッションIDを元にログイン済みか確認する
io.set('authorization', function(handshakeData, callback) {
    if (handshakeData.headers.cookie) {
        //cookieを取得
        var cookie = require('cookie').parse(decodeURIComponent(handshakeData.headers.cookie));
        //cookie中の署名済みの値を元に戻す
        cookie = connect.utils.parseSignedCookies(cookie, app.get('secretKey'));
        //cookieからexpressのセッションIDを取得する
        var sessionID = cookie[app.get('cookieSessionKey')];

        // セッションデータをストレージから取得
        sessionStore.get(sessionID, function(err, session) {
            if (err) {
                //セッションが取得できなかったら
                console.dir(err);
                callback(err.message, false);
            }
            else if (!session) {
                console.log('session not found');
                callback('session not found', false);
            }
            else {
                console.log("authorization success");

                // socket.ioからもセッションを参照できるようにする
                handshakeData.cookie = cookie;
                handshakeData.sessionID = sessionID;
                handshakeData.sessionStore = sessionStore;
                handshakeData.session = new Session(handshakeData, session);

                callback(null, true);
            }
        });
    }
    else {
        //cookieが見つからなかった時
        return callback('cookie not found', false);
    }
});


だらだらと書いてきましたが、結局のところ
app.config(function(){
    app.use(express.cookieParser("署名に使うキー"));
    app.use(express.session({
        key : "expressのセッションIDがcookieに保存される際のキー(デフォルトはconnect.sid)",
        store : sessionStore
    }));
});
のように署名に使うキー、cookieに保存される際のキー、ストレージを設定して、
socket.ioの認証部分で
//ヘッダーからcookie取得
var cookie = require('cookie').parse(decodeURIComponent(handshakeData.headers.cookie));
//cookie中の署名済みの値を上で設定した'署名に使うキー'を元にして戻して
cookie = connect.utils.parseSignedCookies('署名に使うキー');
var sessionID = cookie[app.get('cookieSessionKey')];
署名されたcookieからconnect.utils.parseSignedCookiesで元のsessionIDを取得しましょう、ただそれだけです。
cookieからparseCookieして取得したsessionIDとreq.sessionIDが違うせいでsessionStoreからsessionデータを取得できない、エラーはないけどundefinedになる、というのに中々気づけず結構ハマったので。

このエントリーをはてなブックマークに追加
はてなブックマーク - Express3.x(connect2.3+)とSocket.ioでのセッション管理

オーストラリア行って来ました

このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行って来ました

ということで夏休み最終週に5泊7日でオーストラリア行って来ました。

夏休み中、バイトやらインターンやらでプログラミングしかしてなかったし、長い休み取れるの最後っぽかったから勢いで!

人数はもちろん1人&ツアーとかは無しで!

心細い場面もあったけど、やっぱり誰かに気を使うことなく自由気ままに行動できるのはかなり気持ちいいですね〜


とりあえず日曜日の夜の飛行機で台湾経由でオーストラリアに。

成田〜台湾までが3時間、台湾からシドニーまでが10時間くらいで、だいたい13時間くらいですかね。


飛行機はちょっと心配なチャイナエアラインでwでも機内食普通に美味しいというね!

で、翌日の11時ごろにシドニー到着!

到着した時(やべーついに来ちまったww)みたいなワクテカ感はやばかったw

 
 

で、まあ、シドニー、普通に都会って感じでした

人多すぎて写真とってないけど、シドニー中心部のcity周辺なんかは渋谷って感じ。

歩道にはこれでもかってほど人がいて、しかもみんな早歩きだから落ち着かないw

信号が全部押しボタン式だったけど、何故かみんな連打してた。どんだけせっかちなんだよw

あと、タンクトップの人からコート着てる人、中国人から黒人まで、いろんな人が居ました。そして寒かったw

——————————————————————————————————————————————–

で、さっそく1日目の行動開始。

スーツケース邪魔だったから早めにホテルにチェックインしたかったんだけど・・・

紙の地図使うのが久しぶり過ぎて2時間近く歩きさまようというねww


ネット繋がらないからGoogleMap使えないし、1人だから誰かに相談することもできないし、正直焦ったww

空港の2つ隣の駅のセントラル駅近くのホテルにしたけど、
人多いし、皆せっかちに歩いてるわで、あんまり人に聞けるような雰囲気でもなかった。

で、まあ結局、清掃活動?してる人に地図見せて、「ここ行きたいんですけど〜」的なこと言ってなんとかホテル到着!

チェックインの時の英語はplease speak more slowly 連呼しつつもなんとか。


見知らぬ土地での落ち着ける場所を確保出来た時の安心感と言ったらもうねw


—————————–

その後はネット環境を手に入れるべくTelstraの店舗を探しに。



これまた店舗探すのに1時間くらいかかって、
さらに店の中に実物がおいてなくて、しょうがないから店員にpocket wifi的なものがあるはずだからそれ下さいって頼んで、
奥から持ってきてくれたと思ったら今度はレジの店員がくっそ早口でわけわかめw

購入〜アクティベートまでさらに1時間くらいかかったかなw

で、telstraのPocket wifi的なもの+プリペイド5GBの3G回線げっと!これがあれば何の不自由なしにスマホやらMBAからネットにアクセスできます。

3G回線を通信量5GBまで使えて、端末+プリペイド3G回線で70$前後だったと思います。

 

MBAもiPadもGoogleMapもOK。
多分、これがオーストラリア旅行の中で1番役に立ったものかなwGoogleMap使えるのは大きいw


店舗探しやら店員との格闘で疲れて、向かいにあったマクドナルドで休憩。

日本のチーズバーガーセット的なものが$4ちょいだったかな?サイズ・値段はあんまり変わらない。
店員さんかわいすぎわろりんぬって感じだったw

で、結局、ホテル探し&telstra探しで1日目終了w先が思いやられる1日目でした。
———————————————————————————————————————

2日目は朝からシドニーの市内観光。


市内は電車で移動。7日間乗り放題のパスが$50くらい。
シドニー市内には電車の駅がたくさんあるし、乗り放題パスがあるから適当に歩いててもどこかしらの駅に付いてホテルまで戻れる。


シドニータワーからとか。普通に高層ビル多い。



オペラハウスとハーバーブリッジ見て、ああシドニー来たんだなってさらに実感w


夜景もなかなか!


動物園。定番のコアラ


写真じゃ大きさ伝わらないけど、かなり大きくて$3くらい。安くてお腹いっぱいになれる。

2日目はこんな感じですかね。
正直、市内は人多くて結構疲れましたw

そして更に、シドニー地味に寒いw
最高気温が22度前後で、あまり気温が上がらない。普通に長袖着てたけど風が冷たかったかな。


で、向こうにもスーパーマーケット的なものが。


野菜から生活食品、そして日本のお菓子やら味噌汁の元なども売ってました。
そして日本人の方も普通に店内に居ましたw


持ち運び・保存に便利なクッキーとパンを購入。
シドニー、いろんな所に広めの公園があって、ベンチに座って休憩するときにパンなどがあると重宝しました。

———————————————————————————————————————

3日目はちょっと遠出。ブルーマウンテン。

シドニーから電車で2時間くらいですかね。



3時間近く山道を歩いたわけですが、久しぶりに自然の中歩いたって感じで気持ちよかった!

人多すぎの市内とは大違い!




そして眺めよすぎ!


ふもとの町は、さすがにシドニーから2時間の距離ってだけあって、落ち着いた雰囲気でした。

それとお土産用にユーカリキャンディ購入。

3日目はこんな感じですかね。

夜にまたスーパーに繰り出してパンやら飲み物やらクッキーを購入。

コーラの500mlペットボトルが$4前後だったのは日本と比べるとかなり高いですねw

———————————————————————————————————————
4,5日目は待ちに待ったエアーズロックへ!


シドニーからエアーズロックまでは飛行機で3.5時間くらいかかったかな。
地味に遠いwというかオーストラリア広すぎw


で、本当はエアーズロック登りたかったのに雨降りそうだからってことで登山道閉まってました・・・;;
正直これはかなり残念でした・・・。


登山道。登れる時はこの先の頂上に人が居るんだとか・・・。
写真だと大したことなさそうだけど実際は45度の細い急斜面をチェーン頼りに登って行くんだとか。


ということで麓歩き!



見渡す限り砂地というか岩地というか。そして日差しが強くてさすがにサングラスかけましたw



暑いし砂地だけど、岩の麓には水場も。




近くで見るとかなり大きいし、ゴツゴツしてました。


普通にトカゲとかもいました。何食べるんだろう


結局エアーズロックでは1泊のみ。シドニー&日本に帰らないと行けないので;;
登れなかったのが残念だったけど、あたり一面砂地なのは新鮮だったり、間近でエアーズロック見れたときは嬉しかったですね。

ちなみに街頭なんてものは勿論無いので日没後は真っ暗でした。星とか見えまくりんぐ


で、5日目の14時くらいの飛行機で再びシドニーに戻って・・・1,2,3日目とは違うホテルにチェックイン


$199/1泊のところ(実際は$60/1泊で予約出来たw)だけあって、朝食付きだったりテレビ付いてたりしてなかなか快適でしたw

———————————————————————————————————————
6日目は最終日!22時の便に乗る予定だったから遠出はせずに市内をブラブラと。


なんとかマーケット。セントラル駅から歩いて行けた。
お土産用のガラクタとかたくさん売ってました。

そのあとはオペラ・ハウス近くの駅で降りてまたお土産探したり、公園入って休憩したり、空港内でも買い物したり。



バイト先用にTimTamたくさん買ったり、自分用にコアラの小さいぬいぐるみ買ったりストラップ買ったりペンケースやらステッカーやら!

—————————————–
で、22時(GMT+10)の飛行機に乗って、台湾に着いたのが6時(GMT+8)くらい。だいたい10時間くらい。

台湾桃園国際空港綺麗だった!んだけど特に買うもの無しw
どこかのブースにでかい液晶テレビ置いてあって、しかもなぜかNHK流れてたから1時間くらい時間潰したw

で、台湾からまた飛行機3時間乗って無事帰国!
いやあ、シドニー寒かっただけに日本暑いですね!!!w



そしてオーストラリア遠すぎ!w
出発が夏休み最終週だからってのもあったけど5泊7日とかあっという間すぎ!!もう少し向こうに居たかったかな。

——————————————–
なんだかんだで海外行ったの初めてだったし、ツアーなしの1人旅だったから心細くなる場面はあったけど、
誰にも気を使うことなく行動出来たしバイトのことやら大学のことから開放されてかなりリフレッシュできました。

1人旅って言っても、オーストラリアならネットガンガン繋がったから特に不便は感じなかったかな。ホテルの予約もネットでできるし、オーストラリアならもちろん英語通じるから全く会話ができないってわけじゃないですし。

ちなみに最終日のホテルは現地でオンライン予約しましたw
MBAたん大活躍でした


英語の聞き取りが全然出来なかったのは残念だったかな。
勉強してないから当然っちゃ当然だったけど、次行くときはもっと勉強してからがいいですね。


次は・・・タイとかベトナムみたいな日本から近くてもう少し自然豊かなところに行ってみたいですね!
いずれにしても英語の勉強が必要なんですけどね><


いずれにしても夏休み最終週にリフレッシュ出来たのはすごくよかったです!楽しかったまた行きたい!今度こそエアーズロック登りたい!今度の拠点はケアンズで!

このエントリーをはてなブックマークに追加
はてなブックマーク - オーストラリア行って来ました

Dansette