Windowsをサーバーにして、チャットシステムを作ってみよう...ってことです。

これが一番単純な方法かも、で作業のメモです








●Node.jsインストール

    Node.js

    node-v0.12.5-x86.msi(Win-32bit版)をダウンロードしてインストール

    Node.jsのコマンドプロンプトを開いて、動作確認

    以降、コマンドの発行はNode.jsのコマンドプロンプトで行います

    以下の2つのコマンドでバージョンチェック

    >node -v

    >npm -v

    バージョンが表示されたらOK

    npmはExpressやsocket.ioやをインストールするために使います

●チャット用ソース

    2つのソースは、Cドライブにフォルダーを作ってそこにに置いておきます

    例:C:\chat

    serverは3000番ポートで待ちうけ

    【server.js】

   
var html = require('fs').readFileSync('index.html');
var http = require('http').createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end(html);
});
var io = require('socket.io')(http);
http.listen(3000);
io.on('connection', function(socket) {
  socket.on('msg', function(data) {
    io.emit('msg', data);
  });
});


    【index.html】

   
<!doctype html>
<meta charset="utf-8">
<title>chatA</title>
<script src="/socket.io/socket.io.js"></script>
<div>
ニックネーム:<input type="text" id="nickname" value="">
<br><br>
メッセージ:<input type="text" id="chat_mes" value=""><input type="button" value="chat" onClick="send_message()">
</div>

<div id="messages"></div>


<script>
var socket = io();

function send_message(){
    var nickname = document.getElementById("nickname").value;
    if(nickname == ""){
        nickname = "unknown";
    }
    
    var chat_message = nickname + "->" + document.getElementById("chat_mes").value;
    
    socket.emit('msg', chat_message);
    document.getElementById("chat_mes").value = "";
}

socket.on('msg', function(data) {
 
  var messages = document.getElementById("messages").innerHTML;
  
  document.getElementById("messages").innerHTML = data + '<br>' + messages;
  
});

</script>


●Socket.IOインストール

    C:\chatに移動

    >cd C:\chat

    >npm install socket.io

●nodeでserver.jsを起動

    >cd C:\chat

    >node server.js

    loop処理のようだけど、リソースはそんなに消費していない

   

    ソースを変更する場合は、serverを停止させておきます

    プロンプトの停止は、Ctrl + C

●3000番ポートでindex.htmlにアクセス

    ローカルでやってみる

    http://localhost:3000/index.html

    http://192.168.0.12:3000/index.html

こんな感じ

   

●ついでに、Expressインストール

    ExpressはNode.js用のWebアプリ作成のためのフレームワーク

    便利そうなのでインストールしておく

    >cd C:\chat

    >npm install express




メンバーがオフラインになった時、通知する場合のコード例

メッセージを通知するロジックも少々変えてます、メッセージを入力してエンターキーで送ります



    【server.js】

   
var html = require('fs').readFileSync('index.html');
var http = require('http').createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end(html);
});
var io = require('socket.io')(http);
http.listen(3000);

io.on('connection', function(socket) {
    
    
    socket.on('msg', function(data) {
        io.emit('msg', data);
        
        socket.name = data[0];
        
    });
    
    
    
    
    socket.on('disconnect', function(data) {
        
        if (this.name){
            var mes = this.name + "さんがログアウトしました";
            io.emit('logout', mes);
        }
        
    });
    
});


    【index.html】

   
 <!doctype html>
<meta charset="utf-8">
<title>chat</title>
<script src="/socket.io/socket.io.js"></script>
<div>
ニックネーム:<input type="text" id="nickname" value="">
<br><br>
メッセージ:<input type="text" id="chat_mes" value="" onKeyPress="enter();">
</div>

<div id="messages"></div>


<script>
var socket = io();

function enter(){
    if(window.event.keyCode == 13 ){
        
        var nickname = document.getElementById("nickname").value;
        if(nickname == ""){
            nickname = "unknown";
        }
        //配列で送ってます
        var chat_message = [];
        chat_message[0] = nickname;
        chat_message[1] = document.getElementById("chat_mes").value;
        socket.emit('msg', chat_message);
        document.getElementById("chat_mes").value = "";
    }
}
socket.on('msg', function(data) {
   
    var messages = document.getElementById("messages").innerHTML;
    //配列で受けてます
    document.getElementById("messages").innerHTML = data[0] + "->" + data[1] + '<br>' + messages;
  
});

socket.on('logout', function(data) {
    
    var messages = document.getElementById("messages").innerHTML;
    //ここのデータは単なるテキスト
    document.getElementById("messages").innerHTML = data + '<br>' + messages;
  
});


</script>


    cocoさんが突然ブラウザーを閉じたり、リロードしたり、PCそのものがダウンしたりした場合

   



server.jsがkillされた場合は、通信そのもが途絶しますが、再度node server.jsで起動すれば、通信はそまま再開されます

ブラウザーをリロードする必要はないです、socketのidは変更されますが、通信そのものには影響ないです

なので、socket.idでメンバー管理をする場合は注意が必要です

socketはブラウザーのページ単位で生成されるので、同じブラウザーで複数開いてテストしてみましょう



チャットルームを設ける場合

server.jsでは、これに接続しているすべてのクライアントにデータをブロードキャストします

なので、データにルームのIDを持たせて、クライアント側でデータを表示するかどうか判断する必要があります

あるいは、ポート番号の異なるserver.jsを複数立ち上げて(同じポート番号の場合は起動できません)、これでルーム分けをするという手もあります

または、emitで渡す引数にルームのIDを持たせるとか......




公開する場合

1:使用ポートが開放されていることを確認(いなければ開いておく)

     例:J:COMインターネット-> 指定ポートを開放(80を有効に設定すると何故かインターネット接続が無効になるので注意)

2:グローバルIPが当てられていることを確認

     例:J:COMインターネット->プランによってはプライベートIPしか使えない場合あり

3:外部からグローバルIPでポートをスキャンして確認

     例:サービスを使って確認

     J:COMインターネット->ご自分のサーバーをDMZの領域に置いておくことが必要な場合あり

4:DDNSに登録しておく

     例:MyDNS.jp




上記と同じロジックで、canvasの描画を同期させることもできます

    こんな感じ、ちょっと感動します(ちょっとね)

   

   

    ソース

    canvas要素のバックに、別画像を貼っておけば、画像の上の書き込みを共有しながらチャットできます

   

    【index.html】

   
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>chatC</title>
<style>
    @charset "utf-8";
     
    body{
        margin:0;
        padding:0;
        background-color: #fff;
        font-family:Helvetica, HiraKakuProN-W3, sans-serif; 
        font-size:12px;
        color:#000;
    }
    
    #contents{
        position: absolute;
        top: 50px;
        left: 50px;
        width: 800px;
        height:800px;
        border: 1px solid #000;
        overflow:hidden;
        z-index:2;
    }
     
    #canvas{
        position: absolute;
        top: 0;
        left: 0;
        width: 800px;
        height:800px;
    }
     
    #dis{
        position: absolute;
        top: 10px;
        left: 10px;
        width: 200px;
        height:50px;
    }
    
</style>

<script src="/socket.io/socket.io.js"></script>
<script>
    //device browser check
    var g_mobiledevice = false;
    var g_browsername = 'unknown';
    var g_browserver = -1;
     
    if(checkUserAgent() == false){
        window.alert('このブラウザは対象外です');
    }
     
    function checkUserAgent(){
        var ua = navigator.userAgent;
      
        var mstr = ua.match(/iPhone OS \d+/);
        if(mstr != null){
        var vstr = mstr[0].match(/\d+/);
        if(parseInt(vstr[0]) >= 3) {
            g_mobiledevice = true;
            g_browsername = 'iPhone';
            return true;
        }
        }

        if(ua.indexOf('iPad') > -1){
        mstr = ua.match(/CPU OS \d+/);
        if(mstr != null){
            var vstr = mstr[0].match(/\d+/);
            if(parseInt(vstr[0]) >= 3) {
                g_mobiledevice = true;
                g_browsername = 'iPad';
                return true;
            }
        }
        }

        var mstr = ua.match(/Android \d+\.\d+/);
        if(mstr != null){
        g_browsername = 'Android';
        var vstr = mstr[0].match(/\d+\.\d+/);
        g_browserver = parseFloat(vstr[0]);
        g_mobiledevice = true;
        if(pg_browserver > 2.1) {
            return true;
        }
        }

        mstr = ua.match(/Chrome\/\d+/);
        if(mstr != null){
        g_browsername = 'Chrome';
        var vstr = mstr[0].match(/\d+/);
        g_browserver = parseInt(vstr[0]);
        if(g_browserver >= 9) {
            return true;
        }
        }

        if(ua.indexOf('Safari') > -1){
        mstr = ua.match(/Version\/\d+/);
        if(mstr != null){
            var vstr = mstr[0].match(/\d+/);
            if(parseInt(vstr[0]) >= 5) {
                g_browsername = 'Safari';
                return true;
            }
        }
        }

        mstr = ua.match(/MSIE \d+/);
        if(mstr != null){
        var vstr = mstr[0].match(/\d+/);
        if(parseInt(vstr[0]) >= 9) {
            g_browsername = 'MSIE';
            return true;
        }
        }

        mstr = ua.match(/Firefox\/\d+/);
        if(mstr != null){
        var vstr = mstr[0].match(/\d+/);
        if(parseInt(vstr[0]) >= 4) {
            g_browsername = 'Firefox';
            return true;
        }
        }

        if(ua.indexOf('Opera') > -1){
        mstr = ua.match(/Version\/\d+/);
        if(mstr != null){
            var vstr = mstr[0].match(/\d+/);
            if(parseInt(vstr[0]) >= 11) {
                g_browsername = 'Opera';
                return true;
            }
        }
        }

        return false;
    }
</script>

<script>
    //socket
    var socket = io();
    
    socket.on('draw', function(data) {
        
        document.getElementById("x").value = data[1];
        document.getElementById("y").value = data[2];
        
        var command = data[0];
        
        if(command == 'down'){
            ctx.strokeStyle="#0000ff";
                ctx.lineWidth=3;
                ctx.beginPath();
                ctx.moveTo(data[1], data[2]);
            
        }else if(command == 'move'){
            ctx.lineTo(data[1], data[2]);
            ctx.stroke();
            
        }else if(command == 'clear'){
            document.getElementById("x").value = "";
            document.getElementById("y").value = "";
            ctx.clearRect(0,0,data[1],data[2]);
            
        }
      
    });
    
</script>

<script>
    //canvas
    window.addEventListener("load", init, false);
    
    var mouseX = 0;
    var mouseY = 0;
    var offsetX = 0;
    var offsetY = 0;
    var ctx = null;
    var w = 5;
    var h = 5;
    var buttonStart = null;
    var buttonMove = null;
    var buttonEnd = null;
    var theCanvas = null;
     
    function init(){
        var theContents = document.getElementById("contents");
        offsetX = (theContents.currentStyle || document.defaultView.getComputedStyle(theContents,'')).width;
        offsetX = Number(offsetX.replace('px',''));
        offsetX =  offsetX / 2;
        offsetY = (theContents.currentStyle || document.defaultView.getComputedStyle(theContents,'')).height;
        offsetY = Number(offsetY.replace('px',''));
        offsetY =  offsetY / 2;
         
        document.getElementById('canvas').addEventListener('touchstart', function() {
            event.preventDefault();
        });
        document.getElementById('canvas').addEventListener('touchmove', function() {
            event.preventDefault();
        });
        document.getElementById('canvas').addEventListener('touchend', function() {
            event.preventDefault();
        });
        
        if(g_mobiledevice==true){
            buttonStart = 'touchstart';
            buttonMove = 'touchmove';
            buttonEnd = 'touchend';
        }else{
            buttonStart = 'mousedown';
            buttonMove = 'mousemove';
            buttonEnd = 'mouseup';
        }
         
        theCanvas = document.getElementById("canvas");
        ctx = theCanvas.getContext("2d");
        theCanvas.addEventListener(buttonStart, mouseDownHandler, false);
        theCanvas.addEventListener(buttonEnd, mouseUpHandler, false);
         
        ctx.strokeStyle="#000000";
        ctx.fillStyle = "#000000";
        ctx.lineWidth=1;
    }
     
    function setMouseXY(e){
        if(g_mobiledevice){
            var rect = event.currentTarget.getBoundingClientRect();
            mouseX = event.touches[0].pageX - rect.left;
            mouseY = event.touches[0].pageY - rect.top;
        }else if(g_browsername == 'MSIE' || g_browsername == 'Opera'){
            rect = document.getElementById("canvas").getBoundingClientRect();
            mouseX = event.clientX - rect.left;
            mouseY = event.clientY - rect.top;
        }else if(g_browsername == 'Firefox'){
            rect = document.getElementById("canvas").getBoundingClientRect();
            mouseX = e.clientX - rect.left;
            mouseY = e.clientY - rect.top;
        }else{
            rect = event.currentTarget.getBoundingClientRect();
            mouseX =  event.clientX - rect.left;
            mouseY =  event.clientY - rect.top;
        }
    }
     
    function mouseDownHandler(e){
        setMouseXY(e);
        //ブロードキャストはすべてのクライアントに向かうので、実はここは不要
        /*
        ctx.strokeStyle="#000000";
        ctx.lineWidth=1;
        ctx.beginPath();
        ctx.moveTo(mouseX, mouseY);
        */
        theCanvas.addEventListener(buttonMove, mouseMoveHandler, false);
        
        document.getElementById("x").value = mouseX;
        document.getElementById("y").value = mouseY;
        var chat_message = [];
        
        chat_message[0] = "down";
        chat_message[1] = mouseX;
        chat_message[2] = mouseY;
        socket.emit('draw', chat_message);
        
    }
     
    function mouseUpHandler(){
        theCanvas.removeEventListener(buttonMove, mouseMoveHandler, false);
    }
     
    function mouseMoveHandler(e){
        setMouseXY(e);
        //ブロードキャストはすべてのクライアントに向かうので、実はここは不要
        /*
        ctx.lineTo(mouseX, mouseY);
        ctx.stroke();
        */
        document.getElementById("x").value = mouseX;
        document.getElementById("y").value = mouseY;
        var chat_message = [];
        chat_message[0] = "move";
        chat_message[1] = mouseX;
        chat_message[2] = mouseY;
        socket.emit('draw', chat_message);
        
        
    }
    
    function rect_clear(){
        ctx.clearRect(0,0,800,800);
        var chat_message = [];
        
        chat_message[0] = "clear";
    chat_message[1] = 800;
    chat_message[2] = 800;
    socket.emit('draw', chat_message);
    }
    
</script>

</head>
<body>


<div id="contents">
    <canvas id="canvas" width="800" height="800">
    </canvas>
</div>




<div style="top:10px;left:50px;width:700px;height:100px;border:0px solid blue;position:absolute">
<div>
X:<input type="text" id="x" style="width:30px" value="">
Y:<input type="text" id="y" style="width:30px" value="">
&nbsp;&nbsp;&nbsp;
<input type="button" value="クリア" onClick="rect_clear()">

</body>
</html>


    【server.js】

   
var html = require('fs').readFileSync('index.html');
var http = require('http').createServer(function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end(html);
});
var io = require('socket.io')(http);
http.listen(3000);


io.on('connection', function(socket) {
    
    
    socket.on('draw', function(data) {
        io.emit('draw', data);
        
    });
    
    
});





ストリームを使ってみる

    工事中