ホーム » 2018

年別アーカイブ: 2018

2019年3月
« 2月    
 12
3456789
10111213141516
17181920212223
24252627282930
31  

最近の投稿(電子情報)

アーカイブ

カテゴリー

Office365のOneDriveの設定

学生の皆さんも BYOD でパソコンを活用しているけど、Windows10 を使っているなら、OneDrive を使うとデスクトップのパソコンと持ち運びのノートパソコンでファイル共有ができて便利。すでに、自分のパソコンで Microsoft ID を登録していれば、すでに標準の OneDrive が使えているはず。

この状態で、高専機構の OneDrive も登録すると、高専の学生さんであれば、1TB/人が使えるので、容量を気にせず使える。設定は以下の通り。

高専機構のOneDriveの追加設定

最初にタスクバーに表示されている、OneDrive のアイコンの上で、右ボタンでメニューを表示し、設定。

アカウントのタブを選び、「アカウントの追加」を選び、

サインインの入力欄に、高専機構のメールアドレスを入力し、サインインを押してパスワード設定をすれば、タスクバーに青色の OneDrive が追加されます。

ただし、このままこの設定では、すべてのファイルが同期されるため、情報処理センターのパソコンを起動すると、大量のファイル転送が発生してログインに時間がかかる。

このため、OneDrive のアカウントタブの、「フォルダの選択」を選び、情報処理センターでは不要のファイルは同期しないように設定をすること。

ハッシュ法(オープンアドレス法)

ハッシュ法

ここまでの授業では、配列(データ検索は、登録順保存ならO(N)、2分探索ならO(log N)となる)、単純リスト(データ検索(シーケンシャルアクセスしかできないのでO(N)となる)、2分探索木( O(log N) ) といった手法を説明してきた。しかし、もっと高速なデータ検索はできないのであろうか?

究極のシンプルなやり方(メモリの無駄)

最も簡単なアルゴリズムは、電話番号から名前を求めるようなデータベースであれば、電話番号自身を配列添え字番号とする方法がある。しかしながら、この方法は大量のメモリを必要とする。

// メモリ無駄遣いな超高速方法
struct PhoneName {
   int  phone ;
   char name[ 20 ] ;
} ;

// 電話番号は6桁とする。
struct PhoneName table[ 1000000 ] ;

// 配列に電話番号と名前を保存
void entry( int phone , name ) {
   table[ phone ].phone = phone ;
   strcpy( table[ phone ].name , name ) ; 
}

// 電話番号から名前を調べる
char* search( int phone ) {
   return table[ phone ].name ;
}

しかし、50人程度のデータであれば、電話番号の末尾2桁を取り出した場合、同じ数値の人がいることは少ないであろう。であれば、電話番号の末尾2桁の値を配列の添え字番号として、データを保存すれば、配列サイズは100件となり、メモリの無駄を減らすことができる。

ハッシュ法

先に述べたように、データの一部を取り出して、それを配列の添え字番号として保存することで、高速にデータを読み書きできるようにするアルゴリズムはハッシュ法と呼ばれる。データを格納する表をハッシュ表、データの一部を取り出した添え字番号はハッシュ値、ハッシュ値を得るための関数がハッシュ関数と呼ばれる。

// ハッシュ衝突を考えないハッシュ法

#define HASH_SIZE 100 ;
struct PhoneName table[ HASH_SIZE ] ;

// ハッシュ関数
int hash_func( int phone ) {
   return phone % HASH_SIZE ;
}

// 配列に電話番号と名前を保存
void entry( int phone , name ) {
   int idx = hash_func( phone ) ;
   table[ idx ].phone = phone ;
   strcpy( table[ idx ].name , name ) ; 
}

// 電話番号から名前を調べる
char* search( int phone ) {
   int idx = hash_func( phone ) ;
   return table[ idx ].name ;
}

ただし、上記のプログラムでは、電話番号の末尾2桁が偶然他の人と同じになることを考慮していない。
例えば、データ件数が100件あれば、同じ値の人も出てくるであろう。このように、異なるデータなのに同じハッシュ値が求まることを、ハッシュ衝突と呼ぶ。

たとえ話で言うなら、100個の椅子が連番付きで並んでいて、自分の電話番号末尾2桁の場所に座ろうとしたら、先に座っている人がいるような状態である。このような状態で、あなたなら何処に座るだろうか?

ハッシュ関数に求められる特性

ハッシュ関数は、できる限り同じような値が求まるものは、ハッシュ衝突が多発するので、避けなければならない。例えば、6桁の電話番号の先頭2桁であれば、電話番号の局番であり、同じ学校の人でデータを覚えたら、同じ地域の人でハッシュ衝突が発生してしまう。また、ハッシュ値を計算するのに、配列の空き場所を一つ一つ探すような方式では、データ件数に比例した時間がかかり、高速なアルゴリズムでなくなってしまう。このことから、ハッシュ関数には以下のような特徴が必要となる。

  • デタラメのように見える値であること。(同じ値になりにくい)
  • 簡単な計算で求まること。
  • 同じデータであれば、同じハッシュ値が求まること。

オープンアドレス法

先の椅子取りゲームの例え話であれば、先に座っている人がいた場合、最も簡単な椅子に座る方法は、隣が空いているか確認すればいい。これをプログラムにしてみると、以下のようになる。このハッシュ法は、求まったアドレスの場所にこだわらない方式でオープンアドレス法と呼ばれる。

// オープンアドレス法
// table[] は帯域変数で0で初期化されているものとする。

// 配列に電話番号と名前を保存
void entry( int phone , name ) {
   int idx = hash_func( phone ) ;

   while( table[ idx ].phone != 0 )
      idx = (idx + 1) % HASH_SIZE ;
   }
   table[ idx ].phone = phone ;
   strcpy( table[ idx ].name , name ) ;
}

// 電話番号から名前を調べる
char* search( int phone ) {
   int idx = hash_func( phone ) ;

   while( table[ idx ].phone != 0 ) {
      if ( table[ idx ].phone == phone )
         return table[ idx ].name ;
      idx = (idx + 1) % HASH_SIZE ;
   }
   return NULL ; // 見つからなかった
}

注意:このプログラムは、ハッシュ表すべてにデータが埋まった場合、無限ループとなるので、実際にはもう少し改良が必要である。

ポート番号とメールが届くまで

ポート番号

サーバとなるコンピュータでは、1台のコンピュータで様々なサービスを提供することから、サービスを区別する必要がある。このためにポート番号が使われる。1台毎のコンピュータに割り当てられたIPアドレスを電話番号に例えるなら、ポート番号は内線電話番号に例えることができる。

サーバと通信する場合、サービスを提供するプログラムに応じて標準的なポート番号が決められている。サーバに届いたパケットは、ポート番号に応じてサービスプログラムを起動する。以下の表によく使われるポート番号の一例をあげる。

通信パケットには、送信元IPアドレス送信元ポート番号送信先IPアドレス送信先ポート番号の情報がある。
パソコンがサーバと通信する場合は、(1)自分のIPアドレスを送信元IPアドレス、(2)その時に使われていないポート番号をランダムに選び、送信元ポート番号とする。(3)通信相手のIPアドレスと、(4)通信先のサービスのポート番号をセットして、パケットを送付する。サーバは、サービスを要求してきたクライアントの送信先ポート番号をみて、対応するサーバのプログラムを起動する。プログラムの結果を送り返す時は、送信元と送信先のIPアドレス、ポート番号を入替えてパケットを送信する。

1024未満のポート番号は、サービスを受けとるために用途が決められている(Well Known Port)ので、通常の通信では使われない。

ファイアウォール

ネットワークのサービスの中には、組織外に見せたくないものも多い。また、インターネットでは、悪意のあるプログラマが通信して攻撃を加えてくるかもしれない。こういった場合には、ルータなどで、パケットの送信相手のポート番号や、送信元のIPアドレスをみて、パケットを廃棄する場合がある。こういう、ネットワークからの攻撃を防ぐ装置は、ファイアウォール(防火壁)と呼ばれる。

メールが届くまで

電子メールは、非常に迅速にメッセージを相手に届けることができ、そのメッセージを蓄積・加工・編集・転送できる。また、音声や画像といった情報も、複雑な文字情報に置き換えることで、転送できるようになっている。

メールは、利用者のコンピュータに直接届けられるわけではなく、多くの場合はメールを蓄積するメールサーバに送られる。利用者がメールを読む場合、メールサーバから自分の端末に蓄積されたメッセージを読み込み、メッセージを確認する。このメールのやり取りにおいて、メールを送る時、あるいはメールサーバ間でメールを中継するときには、SMTP(Simple Mail Transfer Protocol) が用いられる。一方、メールサーバからメール
を読み出すときには、POP(Post Office Protocol)IMAP(Internet Message Access Protocol) と呼ばれるプロトコルが用いられる。

メールが届くまでの流れは、aさんが”foo@bar.jp”に送る場合、

  1. aさんは、自分の組織のメールサーバに、SMTPでメールを送る。
  2. メールサーバは、メールアドレスのコンピュータ名部分”bar.jp”をDNSに問合せ、そのIPアドレスを調べ、そのコンピュータにSMTPでメールを送る。
  3. “bar.jp”のメールサーバは、メールアドレスのユーザ名部分を取り出し、各ユーザ毎にメールを保存する。
  4. “foo”さんは、自分宛のメールを確認するために、POPまたはIMAPで自分のメールサーバ”bar.jp”に接続し、ユーザ名,パスワードで認証して自分宛のメールを受け取る。

上記の手順2で、相手のメールサーバに直接送れない場合は、コンピュータ名のMXレコードをDNSに問合せを行い、そこで得られたメールサーバに中継を依頼する。

$ nslookup -query=MX fukui-nct.ac.jp
Non-authoritative answer:
fukui-nct.ac.jp mail exchanger = 10 ews.ip.fukui-nct.ac.jp.

POPは、一般的に、メールサーバから自分のメールをダウンロードして削除してしまうため、メールはダウンロードしたコンピュータにしか残らない。このため、様々なコンピュータでメールを読む人には不便となってきた。IMAPでは、メールを読んでも、サーバに残しておく方式であり、別のコンピュータを使う時にもサーバに残っているメールを読むことができる。

通常、SMTPでメールを送る際には、ユーザ認証が行われない。このため、ウィルスに感染したプログラムから迷惑メールを出すことに利用されることが多い。そこで、SMTP送信の前にPOP/IMAP接続しユーザ認証を行った時だけメールを送れる、POP before SMTP(or IMAP before SMTP)といった方式をとる場合もある。

理解度確認

  • メールの送信から受信までの処理を、それに使われるプロトコルを交えて説明せよ。

JavaScriptでマージソート

配列のマージソートを、難しいテクニックを使わないスタンダードな書き方でのサンプル。

function merge_sort( a , start , end ) {
   if ( start + 1 == end ) {
      // a[start]の1件だけなので並び替える必用なし
   } else {
      // 真ん中
      var mid = Math.floor( (start + end) / 2 ) ;
      // 左半分を並べ替え
      merge_sort( a , start , mid ) ;
      // 右半分を並べ替え
      merge_sort( a , mid , end ) ;

      // 2つの山をマージする。
      var temp = new Array() ;          // マージ結果の一時的保存用
      var lt = start ;                  // 左の山の先頭
      var rt = mid ;                    // 右の山の先頭
      // ここがまだダサい。
      for( i = 0 ; i < end - start ; i++ ) {
         var w ;
         if ( lt >= mid ) {        // 左の山が空、右の山から取り出す
            w = a[ rt ] ;
            rt++ ;
         } else if ( rt >= end ) { // 右の山が空、左の山から取り出す
            w = a[ lt ] ;
            lt++ ;
         } else if ( a[lt] > a[rt] ) {  // 左の山から取り出す
            w = a[ lt ] ;
            lt++ ;
         } else {                       // 右の山から取り出す
            w = a[ rt ] ;
            rt++ ;
         }
         temp[ i ] = w ;
      }
      // マージした結果を、a[]のstart..endに戻す
      for( var i = 0 ; i < end - start ; i++ ) {
         a[ start + i ] = temp[ i ] ;
         console.log( "a["+(start+i)+"]="+a[start+i]) ;
      }
   }
}

array = new Array( 11 , 45 , 22 , 33 , 77 , 88 , 44 , 55 ) ;
merge_sort( array , 0 , array.length ) ;
for( var i = 0 ; i < array.length ; i++ )
   console.log( "a["+i+"]=" + array[i] ) ;

GASを使ってLINEボットを作る

高専の KOSEN ハッカソン LINE にて、学生さんがいろいろと作品を作る間、自分でも LINE メッセージAPIで遊んでみた。

LINEボットのオーム返しの例

参考にしたサイトのプログラムを若干修正してある。

// line developersに書いてあるChannel Access Token
var access_token = "xxxxxx";

// pushしたいときに送る先のuser_id or group_idを指定する
var admin = "Uxxxx";

// postされたログを残すスプレッドシートのid
var spreadsheet_id = "xxxx";

// スプレッドシートの"user-db"シートにユーザIDと名前の対応表を保存
//   通常のサーバ処理なら、データベースに保存なんだろうけど、
//   すべてをGoogle Driveで完結させたいので、Google Spredsheed を
//   データベースのように使う。
var user_db_size = 10 ;

function get_user_name(source) {
  // スプレッドシートのuser-dbシートに、IDと名前の一覧を記録
  //  Uxxxxxxxx なまえ
  var array = SpreadsheetApp.openById(spreadsheet_id)
                .getSheetByName('user-db')
                .getRange(1,1,2,user_db_size).getValues() ;

  for( var i = 0 ; i < array.length ; i++ ) {
    if ( array[i][0] == "" ) // 無記入欄で終了
      return source.userId ;
    else if ( array[i][0] == source.userId )
      return array[i][1] ;   // 対応するものを見つけた
  }
  return source.userId ;
}

/**
 * 指定のuser_idにpushをする
 */
function push(text,to) {
  var url = "https://api.line.me/v2/bot/message/push";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };
 
  var postData = {
    "to" : to,
    "messages" : [
      {
        'type':'text',
        'text':text,
      }
    ]
  };
 
  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
 
  return UrlFetchApp.fetch(url, options);
}
/**
 * 管理者宛にメッセージを送る
 */
function push_admin(text){
  return push(text,admin);
}
 
/**
 * reply_tokenを使ってreplyする
 */
function reply(event,text) {
  var url = "https://api.line.me/v2/bot/message/reply";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };

  var postData = {
    "replyToken" : event.replyToken,
    "messages" : [
      {
        'type':'text',
        'text':text ,
      }
    ]
  };
 
  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };
 
  return UrlFetchApp.fetch(url, options);
}

function do_reply(event) {
  // リプライの処理
  var text = '' ;
  switch( event.message.type ) {
    case 'text' : // メッセージ
      text = event.message.text ;
      break ;
    case 'sticker' : // ステッカー
      text = 'すてっかーID=' + event.message.id ;
      break ;
    case 'image' : // 画像
    default :
      text = 'event.message.type=' + event.message.type ;      
      break ;
  }
  return reply(event,'りぷらい:'+text);
}

/**
 * push api で beacon 受信時の処理
 */
function do_beacon(event) {
  var text = '' ;

  var user = get_user_name( event.source );

  var bcn = event.beacon ;
  switch( bcn.type ) {
    case 'enter' :  // ビーコン領域に入った
      text += 'ENTER' + bcn.hwid ;
      break ;
    case 'leave' :  // ビーコン領域を抜けた(実験したけど通知は実質来ない!?)
    case 'banner' : // ビーコンバナーをタップした
    default :
      text += bcn.type + '=' + bcn.hwid ;
      break ;
  }
  
  return push_admin( 'びーこん=' + text + '(' + user + ')' , admin ) ;
}

/**
 * postされたときの処理
 */
function doPost(e) {
  var json = JSON.parse(e.postData.contents);
  
  // 利用者のuseridを取得するためのlog
  var data = SpreadsheetApp.openById(spreadsheet_id)
               .getSheetByName('log')
               .getRange(1, 1).setValue(json.events);

  // 1回のPostで、メッセージと画像が複数のイベントで来る場合がある
  for( var i = 0 ; i < json.events.length ; i++ ) {
    var event = json.events[i] ;
    switch( event.type ) {
      case 'message' : // Message API の処理
        do_reply(event);
        break ;
      case 'beacon' : // Beacon API の処理
        do_beacon(event);
        break ;
      default: // デバッグのため
        push_admin( 'それ以外=' + event.type ) ;
        break ;
    }
  }
}
 
/**
 * pushをしてみる
 */
function test() {
  push_admin('test()の実行');
}

/**
 * getされたときの処理
 * URL?message=テキスト
 */
function doGet(e) {
  // var data = SpreadsheetApp.openById(spreadsheet_id)
  //              .getSheetByName('log')
  //              .getRange(2, 1).setValue(e.parameter.message);

  // https://script.google.com/macros/s/..../exec?message=メッセージ
  if ( "message" in e.parameter )
    push_admin(e.parameter.message);
}

ふくいソフトコンペ2018にて大賞🎉

ふくいソフトコンペ2018にて大賞

ふくいソフトコンペ2018に5EIから応募していた、水上くん,森川くん,山本くん(指導 村田先生)のチームが、大賞を受賞しました。

LINEハッカソン2018

2018/12/15,16の両日に、LINE本社で開催された”KOSENハッカソン feat.LINE”に4名の学生が参加しました。

{CAPTION}

LINEの様々なサービスを使ったものづくり

最初に、LINE Message API や、LINE クローバ , LINE LIFF , LINE Beacon などの説明を受け、これらをできたら利用して、自分たちで考えたサービスを作ろう…と頑張りました。

{CAPTION}

参加した学生さんは、他の高専からの参加者と5〜6人のグループを作り、それぞれ開発を行いました。

LINE bot や LINE Clova を使い、お小遣いの管理を目指した作品や、レポート作成支援を目指した作品英単語を覚えるサポートをする作品、さらにLINEビーコンを用いた商品販売支援の作品などを作りました。開発が2日間という短い期間であったため、基本的な機能に留まったチームもありましたが、新しい技術に触れるいい機会になったようです。

{CAPTION}

{CAPTION}

さすがLINE本社

{CAPTION}

{CAPTION}

ドメイン名とDNS

ドメイン名とDNS

インターネットでの通信では、IPプロトコルでコンピュータを指定するが、IPアドレスは無機質で覚えるのが大変であり、コンピュータに名前をつけて利用する。この際に、コンピュータの所属などが分かるようにしたものをドメイン名と呼ぶ。

例えば、電子情報工学科のドメイン名 www.ei.fukui-nct.ac.jp は、ピリオド部分で区切られ、以下のような意味を持つ。

  • .jp – 国ドメイン(.uk イギリス,.ch 中国,アメリカは無し)
  • .ac – 種別ドメイン(.co.jp,.com:会社,.ne.jp,net:ネットワーク系)
  • fukui-nct – 組織ドメイン
  • .ei. – サブドメイン(組織内が細分化されている場合)
  • www. – ホスト名

ただしアメリカでは、国ドメインを一般的に使わない。また最近では、世界的な企業では国ドメインが意味をなさないので、アメリカ以外でも .com や .net といった、トップレベルドメインが使われる。様々なサービスを展開している企業では、組織種別が意味をなさないため、toyota.jp といったものも増えてきた。

以下に、主要な組織ドメイン・国ドメインをあげる。

DNSのしくみ

DNSは、Domain Name Service であり、コンピュータ名(ドメイン名)から、IPアドレスを調べるサービスで、ポート番号53,UDPを使っている。

インターネットに接続する際には、最も身近なDNSの情報が与えられ、ユーザがコンピュータ名を問い合わせると、身近なDNSがコンピュータのIPアドレスを返してくれる。この際に、検索結果はキャッシュとして一定期間保存される。身近なDNSがそのコンピュータ名を知らない場合は、上位のDNSに問い合わせを行い、DNSルートサーバもコンピュータ名をキャッシュしていない場合は、管理元の組織のDNSに問い合わせが行われる。


DNSと様々な情報

DNS では、様々な情報が取得できる。IPアドレス以外にも、メールを送ってもらうサーバのIPアドレス(MXレコード)なども取得できる。

DNSとセキュリティ

DNSは、コンピュータ名とIPアドレスを対応付けるものであり、これには正引き(コンピュータ名からIPアドレスを求める)と、逆引き(IPアドレスからコンピュータ名を求める)がある。セキュリティ対策が厳しい場所では、

  • 正引きを使うことで、特定の組織のドメイン名を持つコンピュータからのアクセスを許可/禁止する。
  • 正引きで、コンピュータ名が登録されている所からのみ許可する。
  • IPアドレスから逆引きして求めたコンピュータ名をさらに正引きして同じIPアドレスが求まるかを確認

といった対策を行う。

  • DNSのドメイン名は、当初は最初に申請した人に割り当てられる。このため、nintendo.com といったドメイン名を、関係ない人が取得するといったトラブルがあった。(サイバースクワッティング)
  • DNSを用いたクラッキングでは、ウィルスに感染させたパソコンに偽物のIPアドレスを教えることで、偽装した別コンピュータに誘導し個人情報を盗む手口がある。(DNSポイズニング)
  • 他にもウィルスに感染させた大量のパソコンから、同時にルートサーバに大量のDNSの問合せを送ることで、処理能力を低下させると、インターネット全体でDNS参照ができなくなる攻撃もある。(DNSルートサーバへの分散DoSアタック)

香港のSTEM事情

APIEMSの発表会場近くのショッピングモールで、子供向けのパソコン教材のお店。STEM教材のロボットや、交換パーツまで売ってて驚き。
日本なら大きい電気屋の一角なんだろうけど。
{CAPTION}

{CAPTION}

{CAPTION}

APIEMS 2018 in Hong Kong

12/5-12/8まで香港にて開催されているAPIEMS 2018 にて、12/6に斉藤、12/7に、5EI 小林さん、PS2 前田くん、西先生が発表しました。12/8にPS1 田中さん、PS2 山田くんが、発表し無事に終えることができました。

{CAPTION}

12/6 初日

{CAPTION}

12/7 2日目・午前

{CAPTION}

{CAPTION}

{CAPTION}

12/7 2日目・午後

{CAPTION}

{CAPTION}

12/8 最終日

{CAPTION}

{CAPTION}