Node.js+Express+Socket.IO入門

Socket.IO を使うと双方向の非同期通信が可能になります。今回はその入門編ということで、HTML の input 要素に入力した文字列をサーバに送信し、それをサーバで受信し、そのデータを再びクライアントに送信するプログラムを作成したので紹介します。Express や Socket.IO を使うことで、このようなプログラムをとても簡単に作成することができます。

 

Express と Socket.IO のインストール(Windows

まず Node.js をインストールしてください。Node.js をインストールすれば、npm も一緒にインストールされます。 続いて、D ドライブの直下に webapp フォルダを作成し、コマンドプロンプトから下記のように入力すると Express と Socket.IO をローカルインストールすることができます。

コマンドプロンプト(cmd.exe)〉

cd /d D:\webapp
npm init(全てEnterでOK)
npm install express socket.io

 

プログラムの作成

データを送信・受信する関数は、クライアント側もサーバ側も次のようになります。
 データの送信:emit(event_name, data)
 データの受信:on(event_name, function (data) { ・・・・・ })
注)イベント名(event_name)は何でも構いませんが、送信する側と受信する側でイベント名を一致させる必要があります!

socket.io.htmlUTF-8で保存してください)

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Socket.IO</title>
</head>
<body>

<div>
  <input id="Input" type="text">
  <input type="button" value="送信" onclick="send();">
  <output id="Output"></output>
</div>

<script src="/socket.io/socket.io.js"></script>

<script>
const socket = io();

function send() {
  socket.emit("ClientMessage", document.getElementById("Input").value);
}

socket.on("ServerMessage", function (data) { document.getElementById("Output").innerHTML = data["message"]; });
</script>

</body>
</html>
 

 

socket.io.js

const webapp = require("express")();
const http = require("http").Server(webapp);
const io = require("socket.io")(http);

const port = 8080;
const host = "127.0.0.1";

webapp.get("/:file", (request, response) => response.sendFile("D:/webapp/public/" + request.params.file));
io.on("connection", (socket) => socket.on("ClientMessage", (data) => socket.emit("ServerMessage", {message:data})));
http.listen(port, host, console.log(`HTTP Server running at http://${host}:${port}/`));
 

 

〈ファイルの配置〉

D:\webapp\public\socket.io.html
D:\webapp\socket.io.js

 

Webアプリケーションの起動

コマンドプロンプトから下記のように入力すると、Web サーバのアプリケーション(socket.io.js)を起動させることができます。

コマンドプロンプト(cmd.exe)〉

cd /d D:\webapp
node socket.io.js

Ctrl+C(強制終了)

 

ブラウザからWebサーバにアクセス

Web アプリケーション(socket.io.js)が起動したら、ブラウザのアドレスバーに下記のように入力してください。


http://localhost:8080/socket.io.html

すると、次のような画面が表示されます。

文字列を入力し、送信ボタンをクリックして、入力した文字列が表示されたら成功です^^

 

参考サイト

 

AjaxとNode.js+Expressの連携

今回は、HTML の input 要素に二つの数値を入力し、それらを POST でサーバに送信し、Node.js + Express で受け取って、それらの加算を行い、その結果をクライアントに返すプログラムを紹介します。とても簡単です^^

 

Express のインストール(Windows

まず Node.js をインストールしてください。Node.js をインストールすれば、npm も一緒にインストールされます。続いて、D ドライブの直下にフォルダ webapp(フォルダ名は任意)を作成し、コマンドプロンプトから下記のように入力すると Express をローカルインストールすることができます。


cd /d D:\webapp
npm init(全てEnter)
npm install express

 

プログラムの作成

下記のようにプログラムを作成してください。

ajax.html(UTF-8 で保存)

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Ajax</title>
</head>
<body>

<div>
  <input id="Input1" type="text"> + <input id="Input2" type="text">
  <button onclick="calculate('Input1', 'Input2', 'Output')">=</button>
  <output id="Output"></output>
</div>

<script>
function calculate(input1, input2, output) {
  const url = "/calculate";
  const request = {};
  request.input1 = document.getElementById(input1).value;
  request.input2 = document.getElementById(input2).value;
  const callback = function (response) { document.getElementById(output).innerHTML = response.output };
  post(url, request, callback);
}
</script>

<script>
function post(url, request, callback) {
  const xhr = new XMLHttpRequest();
  xhr.onload = function () { callback(JSON.parse(this.responseText)) };
  xhr.onerror = function () { console.error(this.statusText) };
  xhr.open("post", url);
  xhr.setRequestHeader("content-type", "application/json");
  xhr.send(JSON.stringify(request));
}
</script>

</body>
</html>
 

 

express.js

const host = "127.0.0.1";
const port = 8080;
const webapp = require("express")();
const bodyParser = require("body-parser");
webapp.use(bodyParser.json());
webapp.get("/:file", (request, response) => response.sendFile(__dirname + "/public/" + request.params.file));
webapp.post("/calculate", (request, response) => response.send({output:Number(request.body.input1) + Number(request.body.input2)}));
webapp.listen(port, host, console.log(`WebApp running at http://${host}:${port}/`));
 

 

〈ファイルの配置〉

D:\webapp\public\ajax.html
D:\webapp\express.js

 

サーバ側のWebアプリケーションを起動

コマンドプロンプトから下記のように入力し、サーバ側のWebアプリケーション express.js を起動させます。


cd /d D:\webapp
node express.js

 

ブラウザからクライアント側のWebアプリケーションにアクセス

サーバ側のWebアプリケーションが起動したら、ブラウザのアドレスバーに下記の URL を入力し、クライアント側のWebアプリケーション ajax.html にアクセスします。


http://localhost:8080/ajax.html

すると、次のような画面が表示されます。

+

数値を入力し、=ボタンをクリックして、結果が表示されたら成功です^^

 

参考サイト

 

JavaScript:ドラッグ&ドロップでファイルを読み込む方法

テキストファイルをテキストエリアにドラッグ&ドロップすると、ファイルの内容がテキストエリアに表示されます。 以下のテキストエリアで試すことができます。(Shift_JIS 以外でエンコードされていると文字化けするので注意してください)

プログラムは次のようになります。

readByDragDrop.html

<!doctype html>
<html>
<body>

<textarea id="TextArea" cols="100" rows="20" ondragover="handleOnDragOver();" ondrop="handleOnDrop(displayText);">
Drag & drop here.
</textarea>

<script>
function handleOnDragOver() {
  event.preventDefault();
}

function handleOnDrop(handleOnLoad) {
  event.preventDefault();
  const files = event.dataTransfer.files;
  const fileReader = new FileReader();
  fileReader.readAsText(files[0], "shift-jis"); // euc, utf-8, utf-16
  fileReader.addEventListener("load", function () { handleOnLoad(this); });
}

function displayText(fileReader) {
  document.getElementById("TextArea").innerHTML = fileReader.result;
}
</script>

</body>
</html>
 

参考サイト

 

JavaScript:文字列をファイルに保存する(書き込む)方法

Blob(binary large object)と a 要素を使って文字列(テキストデータ)をファイルに保存する関数 saveAsText を作成したので紹介します。もっと簡単な方法、readAsText があるのだから writeAsText のような関数もあるのではないかと世界中を探したのですがありませんでした。結局、国内でも海外でも Blob と a 要素を使っていました^^

注)ブラウザは Chrome(推奨)か Firefox をご利用ください。

saveAsText.js

String.prototype.saveAsText = function (file) {
  const blob = new Blob([this], {type:"text/plain"});
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = file;
  a.click();
};

"Hello!".saveAsText("sample.txt");
 

参考サイト

 

Swift:正規表現による文字列の抽出と置換

ここでは正規表現を使って、含有(include)、抽出(extractAll)、置換(replaceAll)を行う関数を紹介します。 これらの関数は、このページ末尾の“参考サイト”から集めてきたものです。尚、関数名や引数名は変えています。

また、関数 replaceAllByRule は、正規表現の中で(・・・)でグループ化された capturing group(subexpression)に置換ルールを適用することによって置換します。 下記の例では、文字列 "1+2,3+4,5+6" から正規表現 #"(\d+)\+(\d+)"# で2箇所のサブマッチ文字列を抽出し、Int型にキャストし、それらを加算した上で置換しています。結果は 3,7,11 となります。マッチした文字列全体は $0(0) で、i番目のサブマッチ文字列は $0(i) で取り出せます。

注)関数 replaceAllByRule は私の作品ですが、ライセンスフリーなので自由にご利用いただけます^^
注)下記のプログラムでは、文字列に #" ・・・ "# を使っているので、Swift 5 以降にしか対応していません。Swift 5 対応の paiza.IO では問題なく動作します。

regex.swift

import Foundation

extension String {
  func include(pattern:String) -> Bool {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.firstMatch(in:self, range:self.range(of:self)) != nil
  }

  func extractAll(pattern:String) -> [String] {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.matches(in:self, range:self.range(of:self)).map { String(self[Range($0.range, in:self)!]) }
  }

  func replaceAll(pattern:String, with:String) -> String {
    let regex = try! NSRegularExpression(pattern:pattern)
    return regex.stringByReplacingMatches(in:self, range:self.range(of:self), withTemplate:with)
  }

  func replaceAllByRule(pattern:String, replacementRule:((Int) -> String) -> String) -> String {
    let regex = try! NSRegularExpression(pattern:pattern)
    var string = self
    var location = 0
    while let firstMatch = regex.firstMatch(in:string, range:NSRange(location..<string.count)) {
      let range = firstMatch.range(at:0)  // 最初にマッチした文字列全体のrange
      let substring = { string.substring(with:firstMatch.range(at:$0)) }  // $0番目のサブマッチ文字列を取り出す関数
      let replacementString = replacementRule(substring)
      string = string.replacingCharacters(in:range, with:replacementString)
      location = range.location + replacementString.count
    }
    return string
  }
}

print("Swift".include(pattern:#"S\w*t"#))
print("St Swt8Swit".extractAll(pattern:#"S\w*t"#))
print("St Swt8Swit".replaceAll(pattern:#"S\w*t"#, with:"Swift"))
print("1+2,3+4,5+6".replaceAll(pattern:#"(\d+)\+(\d+)"#, with:"$2-$1"))
print("1+2,3+4,5+6".replaceAllByRule(pattern:#"(\d+)\+(\d+)"#) { String(Int($0(1))! + Int($0(2))!) })
 

 

〈実行結果〉

true
["St", "Swt8Swit"]
Swift Swift
2-1,4-3,6-5
3,7,11


補足

extractAll の中の $0.range の挙動は、次のようになります。


import Foundation

let pattern = #"S\w*t"#
let string = "St Swt Swit"
let regex = try! NSRegularExpression(pattern:pattern)
print(regex.matches(in:string, range:string.range(of:string)).map { $0.range })
 

 

〈実行結果〉

[{0, 2}, {3, 3}, {7, 4}]


参考サイト

 

C++:多態性と関数の多重定義

ここではC++による多態性と関数の多重定義の例を紹介します。
多態性オブジェクト指向プログラミングにおける素晴らしい技法の一つです。多態性を利用すると同じ関数名でオブジェクトに応じたオブジェクトの操作が可能になります。一方、多態性が利用できないとオブジェクトごとに関数名を変えてオブジェクトを操作しなければなりません。
以前、オブジェクト指向プログラミングの講習を受けたことがあるのですが、その時の講師は多態性を次のように説明していました。 一つの命令(関数名)でオブジェクトごとに異なる動作をさせることができる。例えば、鳥オブジェクトに“動け”と命令すれば鳥は空を飛ぶ、魚オブジェクトに“動け”と命令すれば魚は海を泳ぐ、と言ったぐあいです。
尚、C++の場合は、関数の多重定義を使って同様のことが実現できます。(下記の overloading.cpp を参照)
また、多人数でソフトウェアを開発する際、関数の多重定義が利用できる言語では、各プログラマは関数名の衝突を気にせずに関数名を付けることができます。

関数 calculate の多態性(polymorphism)の例
calculate の引数が、Operand1 のオブジェクトの場合には加算、Operand2 のオブジェクトの場合には乗算を実行します。

polymorphism.cpp

#include <iostream>

class Operand {
public:
  virtual auto calculate() -> void;
};

class Operand1 : public Operand {
  double x, y;
public:
  Operand1(double x = 0, double y = 0);
  auto calculate() -> void;
};

Operand1::Operand1(double x, double y) : x{x}, y{y} {}

auto Operand1::calculate() -> void {
  std::cout << x + y << std::endl;
}

class Operand2 : public Operand {
  double x, y;
public:
  Operand2(double x = 0, double y = 0);
  auto calculate() -> void;
};

Operand2::Operand2(double x, double y) : x{x}, y{y} {}

auto Operand2::calculate() -> void {
  std::cout << x * y << std::endl;
}

auto calculate(Operand* operand) -> void {
  operand->calculate();
}

auto main() -> int {
  auto operand1 = new Operand1(1, 1);
  auto operand2 = new Operand2(2, 2);
  calculate(operand1); // -> 1 + 1 = 2
  calculate(operand2); // -> 2 * 2 = 4
}
 

 

関数 calculate の多重定義(overloading)の例
上記と同様のことが、関数の多重定義を使っても実現できます。はっきり言って、C++の場合はこちらの方が簡単です^^

overloading.cpp

#include <iostream>

class Operand1 {
  double x, y;
public:
  Operand1(double x = 0, double y = 0);
  auto calculate() -> void;
};

Operand1::Operand1(double x, double y) : x{x}, y{y} {}

auto Operand1::calculate() -> void {
  std::cout << x + y << std::endl;
}

class Operand2 {
  double x, y;
public:
  Operand2(double x = 0, double y = 0);
  auto calculate() -> void;
};

Operand2::Operand2(double x, double y) : x{x}, y{y} {}

auto Operand2::calculate() -> void {
  std::cout << x * y << std::endl;
}

auto calculate(Operand1* operand1) -> void {
  operand1->calculate();
}

auto calculate(Operand2* operand2) -> void {
  operand2->calculate();
}

auto main() -> int {
  auto operand1 = new Operand1(1, 1);
  auto operand2 = new Operand2(2, 2);
  calculate(operand1); // -> 1 + 1 = 2
  calculate(operand2); // -> 2 * 2 = 4
}
 

 


g++ -std=c++17 -O3 *.cpp -o main.exe

 

Swift:多次元配列

Swiftによる多次元配列(multidimensional array)の作り方です。 以下のプログラムで、a1, a2, a3 は空配列(empty array)による初期化、b1, b2, b3 は要素数と初期値を指定した初期化です。

multidimensional_array.swift

let n1 = 2
let n2 = 3
let n3 = 4

/* 1-dimensional array in Swift */
var a1 = [Double]()
var b1 = [Double](repeating:0, count:n1)
print(a1)
print(b1)

/* 2-dimensional array in Swift */
var a2 = [[Double]]()
var b2 = [[Double]](repeating:[Double](repeating:0, count:n2), count:n1)
print(a2)
print(b2)

/* 3-dimensional array in Swift */
var a3 = [[[Double]]]()
var b3 = [[[Double]]](repeating:[[Double]](repeating:[Double](repeating:0, count:n3), count:n2), count:n1)
print(a3)
print(b3)
 

参考サイト