base64ライブラリ

1.base64とは
 base64とは、64進数を意味する言葉で、すべてのデータをアルファベット(a~z, A~z)と数字(0~9)、一部の記号(+,/)の64文字で表すエンコード方式である。
 Eメールを送るためのプロトコルSMTPでは、ASCIIコードの7bitで表現される英数字しか送ることができなかったが、Eメールを使って画像や音声などのデータを扱えるようにするため、すべてのデータを英数字で表すMIME(Multipurpose Internet Mail Extensions)という規格が登場し、その中でbase64というデータの変換方法が定められた。
 MIMEは、SMTPに加えて設定されるメッセージ送信標準のひとつで、Eメールに画像、動画、音声などのファイルを添付できる機能。
 MIMEタイプにはファイルの種類を表わす形式で、
 ・text/plain:テキストファイル(中身が文字だけのファイル)
 ・text/html:HTMLファイル(ホームページのファイル)
 ・image/png:PNGファイル(PNG形式の画像ファイル)
がある。
 メールではContent-Typeの中で「Content-Type: text/plain; charset=iso-2022-jp」
のように指定する。

2.base64の変換
 base64への変換は下記の規則で行う。
・エンコードしたいファイルのバイナリデータを6bitずつ取り出す(足りない分は0を追加する)。
・6ビット列をbase64の変換表(下表)で文字(英数字)に変換する。
・4文字ずつに分けて、4文字に満たない場合は=を追加する。

 変換したい文字列”ABCD”(ASCIIコード)をbase64に変換する例
(1)変更したい文字列をバイナリ(2進数)に変換する
 0x41, 0x42, 0x43, 0x44(16進数)
 0100 0001, 0100 0010, 0100 0011, 0100 0100(2進数)
(2)バイナリを6ビットづつに分割する
 010000, 010100, 001001, 000011, 010001, 00
(3)最後の2ビットが余るので、6ビットになるように0を追加する
 010000, 010100, 001001, 000011, 010001, 000000
(4)base64変換表よりビット列を文字に変換する
 Q U J D R A
(5)4文字に分けた時に、2文字分足りないので=を追加する
 QUJD RA==

2.base64ライブラリ(C++)
(1)base64ライブラリのダウンロード
 https://ja.osdn.net/projects/clxcpp/releases/51423
clx_0.18.2.tar.bz2

(2)ライブラリの解凍
# tar xvfj clx_0.18.2.tar.bz2
clx配下に下記のファイルが展開される

(3)エンコードとデコードのサンプルアプリ作成
ex01.cpp

#include <iostream>
#include <string>
#include </home/clx/base64.h>
using namespace std;

int main()
{
    cout << "ABCDのエンコードとレコード\n";
    string src = "ABCD";
    string code = clx::base64::encode(src);
    cout << "encode:" << code << std::endl;
    cout << "decode:" <<         clx::base64::decode(code) << endl;
    return 0;
}

(4)サンプルのコンパイルと実行
# g++ -o ex01 ex01.cpp
# ./ex01

The end

シェルスクリプトの作成

実行環境
 Windows10上のVirtualBox仮想マシン
 ゲストOS Centos8

1.シェルスクリプトとは
 シェルスクリプトは主にオペレーティングシステムのコマンドラインインタプリタから実行可能なコマンドの一連の流れをファイルにして再利用できるようにしたもの。UNIXでは特にコマンドインタプリタのことをシェルと呼ぶ。
 大別すると以下のシェルにはいくつかの種類があり、主に下記のものに分けられる。
・Bシェル系:sh, bash, ksh
・Cシェル系 :csh, tcsh
・zshは両者の中間
 Bシェル系のshはUnix標準のシェルで、Linuxではbashが標準搭載されている。
 現在使っているシェルの確認
 $ echo $0
または、
 $ echo $SHELL(こちらはログインシェル)
 シェルのプロセスの情報を表示
 $ ps -p $$
 利用可能なシェルの一覧の確認
 $ cat /etc/shells

2.シェルスクリプトの作成と実行
(1)シェルスクリプトの作成
・#!/bin/bash
 #はhash、!はbangで、#!をshebang(シェバン)と言い、シェバンからスクリプトを実行するインタープリタが呼ばれ、/bin/bash のファイルが実行されます。
・# コメント
・改行(Enter)でコマンドの終わりを表し、また;(セミコロン)で区切ることも可能。
・複数行のコマンドは、(バックスラッシュ)で行を続ける。

 test.sh

#!/bin/bash
echo "Hello World" > ./hello.txt
ls -l ./hello.txt
cat ./hello.txt

(2)実行
 $ bash ./シェルスクリプト名
 また、シェルスクリプトファイルに実行権限を与えて、コマンドとして実行
 $ chmod +x test.sh
 $ ./test.sh

3.スクリプトファイル
(1)変数
・変数の名前には半角英数字(a-z、A-Z、0-9)とアンダーバー(_)が使える。
・変数に値を代入するときは、=を前後空白なしで書く。
・文字列の場合は”を付けて囲む。
・変数にアクセスする時は変数名の前に$を付ける、または$付けて変数を{}で囲む。

#!/bin/bash
a1="Hello"
echo $a1
echo ${a1}

 $ bash ./test1.sh
 Hello
 Hello

(3)if文
 if [ 条件1 ]; then
 コマンド1
 fi

 test1.sh

#!/bin/bash
if [ "a" = "a" ]
then
 echo "a文字列とa文字列は等しい"
fi

 $ bash ./test1.sh
 a文字列とa文字列は等しい

(4)for文
 for 変数 in 値リスト
 do
 コマンド
 done

 test2.sh

#!/bin/bash
for i in W o r l d
do
 echo $i
done

 $ bash ./test2.sh
 W
 o
 r
 l
 d

(5)while文
 while 条件式
 do
 コマンド
 done

 test3.sh

#!/bin/bash
a=1
while [ $a -lt 3 ]  # aの値が3より小さいか(**l**ess **t**han)
do
 echo $a
 a=`expr $a + 1` # a+1を実行し、その結果を再度aに格納
done

 $ bash ./test3.sh
 1
 2

(6)case文
 case 値 in
 条件・値1 ) コマンド1 ;;
 条件・値2 ) コマンド2 ;;
 条件・値3 ) コマンド3 ;;
 …
 条件・値n ) コマンドn ;;
 esac

 test4.sh

#!/bin/bash
text="a"
case $text in
A) echo "A" ;;
a) echo "a" ;;
x) echo "z" ;;
*) echo "?" ;; #ワイルドカード
esac

 $ bash ./test4.sh
 a

The end

Wireshark(ネットワークアナライザ

 Wireshark(ワイヤシャーク)はネットワークアナライザのソフトウェアです。

1.インストール環境
 Windows10pro

2.ダウンロード

https://forest.watch.impress.co.jp/library/software/wireshark/

3.インストール
(1)互換モードに変更
 ダウンロードしたプログラムを選択して、マウス右ボタンでプロパティを表示し、Windows7互換モードにします。

(2)インストール
 ダウンロードしたプログラムを「管理者として実行」を選択して、インストールします。

(3)WinCapをダウンロード
 上記のWiresharkのソフトからWinCapをインストールするとインタフェースが表示されない問題があったため、別にインストールします。
 下記のサイトからWinCapをダウンロードします。

 https://www.winpcap.org/install/default.htm

WinCapもWindows7互換モードでインストールします。

 ダウンロードしたプログラムを「管理者として実行」を選択して、インストールします。

4.Wiresharkの起動
 Wiresharkを起動すると「Wiresharkへようこそ」画面が表示され、本PCで利用できるネットワークインターフェイスが表示されます。
 キャプチャ対象のネットワークインターフェイスを選択し、ツールバーの「パケットキャプチャを開始します」ボタンをクリックするとキャプチャが開始されます。

5.使い方
(1)表示フイルタ
 Wiresharkのデフォルト設定ではキャプチャされたすべてのパケットの情報を表示します。必要なパケットのみ表示したいときは表示フィルタを使用します。

 フィルタは複数の条件を「and」、「or」でつなぐことができます。
(例)
 tcp and (ip.src==202.172.25.30 or ip.src==192.168.0.6)

(2)キャプチャフイルタ
 前記の表示フィルタは、キャプチャされたデータから必要な情報のみに絞って表示するための機能ですが、キャプチャフィルタはキャプチャ開始時にあらかじめ指定しておいた条件に合致するパケットだけを取得します。このため、長時間のキャプチャを行う場合などはキャプチャフィルタを利用することでファイルサイズを小さくできます。

The end

インターネットプロトコルについて

目次

1.前書き
 1980年代に異なる種類のコンピュータ間で通信を行うための約束事としてISO(国際標準化機構)とITU(国際電気通信連合)によって通信機能を7階層に分けて定義したOSI参照モデルが出来ました。
 各階層毎に機能や通信の約束事が決まっていて、2つのコンピュータ間の同じ階層間で論理的な接続が成立するようになっています。また、各階層は下位の機能を使って自身の機能を実現し、上位に機能を使ってもらう関係にあります。
 一方、TCP/IPはコンピュータネットワークの通信プロトコルとして1970年代に仕様が策定され、TCP/IP v4までの4つのバージョンが作られました。TCP/IPは1980年代に標準化され、インターネットを含む多くのコンピュータネットワークで標準的に利用される通信プロトコルのセット(インターネット・プロトコル・スイート)となっています。
 TCP/IPは、前記の7階層に分けて定義されたOSI参照モデルに対して4層から成ります。

 通信に関する標準規格を定める団体として以下のものがあります。
(1)ISO(International Organization for Standardization、国際標準化機構)
(2)ITU-T(International Telecommunication Union Telecommunication Standardization Sector、国際電気通信連合電気通信標準化部門)
(3)ANSI(American National Standards Institute、アメリカ規格協会)
(4)IEEE(Institute of Electrical and Electronics Engineers、電気・情報工学分野の学術研究団体)
 LAN(Local Area Network)に関する規格策定は、IEEE 802委員会(LAN/MAN Standards Committee, 略称LMSC)で行われている。
 策定された規格として、
・IEEE 802.1 Higher layer LAN protocols; High Level Interface (HILI) アーキテクチャ
・IEEE 802.3 Ethernet (旧: CSMA/CD) イーサネット
・IEEE 802.11 Wireless LAN (WLAN) 無線LAN
などがある。
(5)IETF(The Internet Engineering Task Force)
 インターネット・プロトコル・スイートの技術面に関するNPOで標準化団体で、IETFにおける技術仕様は RFC(Request For Comments)という名前で文書化される。

2.TCP/IPプロトコル
 送信側では上位層から受けたパケットを自身の層の機能を果たすためのヘッダでカプセル化して下位の層、または装置(ノード)にデータを渡します。受信側では下位層、または装置(ノード)からカプセルの中身のデータを受けて上位層、または装置(ノード)に渡します。
 この様にプロトコル階層に応じてカプセル化されたデータがノード間でバケツリレー式に送られます。下記にイメージ図を示します。

各プロトコル層における仕組みを以下に示します。

2.1 ネットワークインターフェース層
 ネットワークインターフェース層では隣接するネットワーク間、又は同じネットワーク内のノード間でデータ転送するための伝送媒体と、パケットをビットストリームに分解/再構築して隣接するノード間でデータを送るための誤り検出と誤り訂正方法を提供します。ネットワークインターフェース層が扱うデータを「フレーム」といいます。
 
 イーサネット (Ethernet)はネットワークインターフェース層の代表的なプロトコルで下記に概要を示します。
(1)伝送媒体
・同軸ケーブル:
 バス型のイーサネット
・光ファイバー:
 主にインターネット回線に使用されるスター型のイーサネットで、「1000BASE-LX」や「1000BASE-SX」といった光ファイバーケーブルなどがあります。
・LANケーブル:
 インターネット接続に使われるスター型のイーサネットで、「1000BASE-T」や「1000BASE-CX」というツイストペアケーブルなどがあります。

(2)データリンク層
 IEEEによりIEEE 802.3としてその技術仕様が公開されていて、MACアドレス(ネットワークインターフェイスが持っている端末を識別するための固有値)で隣接するノードにフレームデータを送るための誤り検出、誤り訂正方法が決められています。
 各ノードは自身や宛先のMACアドレス情報をフレームに含めて送信し、スイッチングハブや端末ノードはそのアドレス情報に基づいて受信や中継処理を行います。
 全てのビットが1のMACアドレス(FF.FF.FF.FF.FF.FF)はブロードキャストアドレスでこの宛先のフレームは全てのイーサネットカードが取り込みます。ARP(Address Resolution Protocolの略でIPアドレスからMACアドレスを動的に知る)プロトコルで使われます。
 イーサネットフレームにはいくつかの種類があり、Ethernet IIフレーム(イーサネットバージョン2またはDIXフレームともいう)は現在最も使用されているものです(下図)。

(3)データリンク層で動作する機器
①ブリッジ
 ネットワーク間(バス型とスター型など異なる方式間)を接続するための機器でイーサネットフレームの中継を行います。
②スイッチ(スイッチングハブ、L2スイッチ)
 ネットワークインターフェース層で中継する機能を持つ機器で、MACアドレスを使って中継動作を行います。
 ポートに接続されている機器のMACアドレスを学習して記憶しているため、宛先の機器が繋がっているポート以外のポートから無駄なデータが送信されることがなく、ネットワークを有効に使えます。

2.2 インターネット層
 インターネット層は、前記のネットワークインターフェース層で論理的に接続されたノード間の1つ、または複数にまたがってIPアドレスによって論理的に接続します。
 この層ではパケットの送受信を行うインターネットプロトコル (IP)の他に、アドレス解決プロトコル (ARP)、インターネット制御メッセージプロトコル (ICMP) が組み込まれています。
 インターネットプロトコルのパケットの構造を下図に示します。

(1)IPアドレス(IPv4)
 IPアドレスはネットワークIDとホストIDに区切って使い、区切る位置によって、A、B、Cの3つのネットワーククラスに分けられます。
 ホストIDフィールドが全て0のアドレスはネットワーク自身を表わすため、全て1のアドレスはネットワーク中の全てのホストへのブロードキャスト用に予約されています。

①グローバルIPアドレス
 グローバルIPアドレスはインターネットで相手と通信するための他と重複しない一意のIPアドレスです。
②プライベートIPアドレス
 プライベートIPアドレスはLANなどの企業内ネットワークで自由に使用できるIPアドレスです。IPアドレスの範囲はRFC 1918で規定されています。

③DNS (Domain Name System)
 各ドメイン名にはIPアドレスが割り振られています。ドメイン名からDNSによって対応するIPアドレスを求め、その宛先IPアドレスを利用してネットワーク上の機器と通信を行ないます。

(2)IPアドレスを調べる(Linux)
①自分のホストのIPアドレス
 $ ifconfig または$ /sbin/ifconfig

②ドメイン名のホストのIPアドレス
 $ nslookup ドメイン名
 又は
 $ dig ドメイン名
(例)
 $ nslookup www.yahoo.co.jp

$ dig www.yahoo.co.jp

2.3 トランスポート層
 トランスポート層のプロトコルにはTCPとUDPの2つがあります。
2.3.1 TCP
 インターネットプロトコル(IP)ではパケット(最大長1500バイト程度)単位で転送する仕組みしかなく、大きなサイズのファイルを送ることができません。またIPでは途中でパケットが破棄されることもありパケットが相手に届くことが保証されていません。
 TCPでは大きなデータをIPで送れるサイズのパケットへの分割を行なったり、途中で破棄されたパケットを再送して信頼性の高い通信を行います。
 また、TCPでは接続するアプリケーションを識別するためにポート番号を利用し、1つのIPアドレスをもつホストでポート番号の異なる複数のアプリケーションと接続することができます。

(1)TCPヘッダのフォーマット

①送信元ポート番号:相手からの応答を受付するポート番号
②宛先ポート番号:相手のアプリケーションを識別するポート番号
③シーケンス番号:データの到着順を確認するための番号で相手からの確認応答番号を設定する
④確認応答番号:シーケンス番号に受信したデータサイズ(byte数)を足して確認応答番号として応答する
⑤データオフセット:ヘッダー長を表し、データ開始位置を示す
⑥予約:将来用で常に0
⑦コントロールフラグ:6つのフラグ情報(URG,ACK,PSH,RST,SYN,FIN)の各々1ビットで0:無効、1:有効を表わす
⑧ウィンドウサイズ:応答確認を待たずにまとめて送ることのできるデータ量を示す
⑨チェックサム:TCPセグメント(TCPヘッダ+データ)のチェックに使用
⑩緊急ポインタ:URGフラグが有効のとき、緊急データの位置を示す
⑪オプション:MSS値などを設定するときに使用
⑫パディング:TCPヘッダが32bit単位になるように0で埋める

(2)TCPプロトコルの仕組み
 コントロールフラグの[SYN]、[ACK]で2点間の同期を取り、シーケンス番号(SEQ)と確認応答番号(ACK)でTCPセグメント(TCPヘッダ+データ)の到着順とデータ欠落の検出を行います。[SYN]、[ACK]で2点間の同期を取る仕組みを「3ウェイハンドシェイク」と言います。
 TCPプロトコルではデータ転送の信頼性を確保するため、送信データに対して[ACK]を監視して、一定時間[ACK]が返ってこないときは再送します。一定回数再送しても[ACK]が返ってこないときは[RST]フラグを送信してコネクションを強制終了し、初期状態とします。

2.3.2 UDP
 UDPは相手とのコネクションを確立する手順はなく、単純に相手をアドレスで特定して送受信します。
 UDPではデータ部の内容も利用者が自由に決められるため、通信の相手をIPアドレスだけでなくポート番号によってアドレスを拡張することで、ポート番号ごとに機能を割り振った送受信が可能になります。
(1)UDPヘッダのフォーマット

①送信元ポート番号:相手からの応答を受付するポート番号。
返信不要の場合は0。
②宛先ポート番号:相手のアプリケーションを識別するポート番号
③パケット長:UDPヘッダとデータを合わせたサイズ(btye)
④チェックサム:UDPデータグラム(UDPヘッダ+データ)をチェックするために使用。チェック不要の場合は0。

2.4 アプリケーション層
2.4.1 HTTPプロトコル
(1)プロトコルの概要
 Webサーバ(HTTPのサーバ)とWebブラウザ(HTTPのクライアント)の2つがTCPの80番ポートを使い通信します。
 最初にクライアント側からTCPの80番ポートで待ち受け(リッスン)しているサーバに対して接続します。
 TCPの接続が完了すると、クライアント側からHTTPのGETメソッドでファイルを要求します。クライアント側からの要求に対してサーバ側から応答を返し、続いて要求したファイルのデータが送信されます。

(2)実験
①試験環境準備 
 VirtualBoxの仮想マシンCentOS8にApatch(HTTPサーバ)をインストールします。
・インストール対象(httpd)のパッケージを確認します。
 $ yum list | grep httpd

・ここではhttpd、httpd-tools、httpd-devel、httpd-manualをインストールします。
 # yum install -y httpd httpd-tools httpd-devel httpd-manual
・Apache(httpd)サーバの起動
 # systemctl start httpd
・Apache(httpd)サーバの状態確認
 # systemctl status httpd

②ゲストOS(CentOS8)のファイアウォールの設定
・ファイアウォールが起動しているか確認します。
 $ systemctl status firewalld
 Active: active (running)となっていれば起動しています。
・HTTPが許可されていることを確認します。

・「services:」に「http」がないときは下記のコマンドで追加します。
 $ sudo firewall-cmd –permanent –add-service=http
 $ sudo firewall-cmd –reload
・httpが追加されたことを確認します。 
 $ systemctl status firewalld

③HTTPプロトコルの実験
・index.html(トップページ)の配置
 /var/www/htmlにindex.html(”テストページ”の記載のみ)を置きます。
・ホストOS(Windows10)のコマンドプロンプトから
 telnetでWebサーバにTCP接続してページを取得します。
 $ telnet localhost 80
 Webサーバに接続できたら
 GET / HTTP/1.1(改行)
 host:localhost(改行)
 (改行)
 サーバからページのレスポンスが返ります。

 アプリケーション層のプロトコルには、「HTTP」の他に
電子メールで使う「SMTP」・「POP3」・「IMAP4」、自動的にIPアドレスを割り振る「DHCP」、インターネット上でドメイン名を管理する「DNS」、ファイル転送「FTP」、ネットワーク・タイム・プロトコルの「NTP」、ネットワークの機器の監視・制御の「SNMP」、、暗号や認証を使ってリモートコンピュータと通信するための「SSH」などがあります。

3.ソケット
3.1 ソケットの概要
 TCP/IPアプリケーションからTCP/IPを使って通信するときにIPアドレスとポート番号の組み合わせによって識別されるリンクをソケットといい、ソケットを使ってTCP/IPプロトコルを操作するAPIがシステムコールとして提供されています。
 ソケットにはTCPとUDPのどちらを使うかによって2つのタイプがあります。
 TCPを操作するソケットは「ストリーム型」(SOCK_STREAM)、UDPを操作するソケットは「データグラム型」(SOCK_DGRAM)と呼びます。また、IP層以下を操作する型として「RAW型」(SOCK_RAW)があります。

3.2 ソケットの動作
(1)ストリーム型ソケット 
 ストリーム型ソケットの動作概要を下図に示します。

(2)データグラム型ソケット
 データグラム型ソケットの動作概要を下図に示します。

3.3 ソケットの実装例
 CentOS8のAPI(システムコール)を使ったC言語のサンプルプログラムは下記を参照して下さい。

ソケット通信(Linux)(本サイト内のページ)

(参考文献)
・「はじめてのTCP/IP」
 著者:中西 隆 発行:株式会社技術評論社

The end

Linuxのコマンド

目次

1.top(システムの稼働情報表示)
 現在実行中のプロセスをCPU利用率の高い順にリアルタイムで表示します。「q」で終了します。
オプション(例):
 -U ユーザー又は-u ユーザー:監視するユーザーを名前またはIDで指定
(例)
マルチスレッドプロセスを実行中にtopコマンド実行

$ top

2.ps(実行中プロセス表示)
実行中のプロセスを表示
オプション(例):
 a:端末を持つ全てのプロセスを表示する
 u:ユーザー指向のフォーマット
 x:端末を持たない全てのプロセスを表示する
(例)
マルチスレッドプロセスを実行中にpsコマンド実行

$ ps aux | grep a.out

3.kill(プロセス終了)
 実行中のプロセスを終了する
 $ kill プロセス番号(PID)
(例)
マルチスレッドプロセスを実行中にkillコマンド実行

4.grep(文字列の検索)
 $ grep -ril “文字列” ディレクトリパス
 指定したディレクトリパス以下を再帰的に検索し、指定した文字列を含むファイル名を表示する。
 ディレクトリパスを指定しないときはカレントディレクトリ以下を検索する。
 オプション(例):
  -r:サブディレクトリ内も含めて検索する
  -i:大文字・小文字を区別しない
  -l:検索結果にファイル名のみ表示する

5.find(ファイル検索)
 $ find ファイル名(又はディレクトリ名)
 ファイルやディレクトリを検索する
 (例)

$ find ./c_work/a.out
$ find c_work

6.source(スクリプトファイルを実行)
 $ source ファイル名
 ファイルに記載されているコマンドを実行する。
 $ source ファイル名 引数1 引数2 ・・・
 ファイルに記載した”$番号”に値を渡すことができる。
 (例)
 sample.txt

index=$1
echo "input=$index"

7.scp(リモートコピー)
 ネットワークを通じて他のコンピュータとの間でファイルの複製を行う。
scp [オプション] コピー元のファイル名 コピー先のファイル名
 リモートホストのファイルは、ユーザー名@ホスト名:ファイルパスで指定
オプション:
 -p:パーミッションや作成日時などの属性情報もコピー
 -r:指定したディレクトリ配下のサブディレクトリとファイルもコピー
※コピー先のホストで常駐プログラム「sshd.service」が起動(標準ではTCP22番ポートで接続を受け付け)していること。
(例)Windows10からlocalhost(CentOS8)のホームディレクトリ配下のフォルダ(ここではc_work)にファイルコピー
Windowsのコマンドプロンプトで下記を入力

> scp ./test.txt (ユーザ)@localhost:~/c_work

8.ポート番号一覧の表示

$ view /etc/services

 ※ファイルに設定されているデフォルトの値であり、実際にサービスが動いているポートとは限りません。

実行中のポート番号はlsofコマンドで確認できます。

9.lsof(プロセスが開いているファイルの表示)
オープンしているファイルを一覧表示する

$ sudo lsof -i -P

オプション:
 -i:「-i」で全てのネットワークソケットを対象にする。「-i4」でIPv4、「-i6」でIPv6を指定できる。この他、「-iTCP」「-iUDP」「-i@ホスト」「-i:ポート番号」を指定可能
 -P:ポート名の換わりにポート番号を表示する

10.サービス一覧の表示

# systemctl list-unit-files -t service

11.which(コマンドの検索)
 検索パスから指定されたコマンドを検索する
 $ which -a コマンド名

12.コマンドパスの設定
(1)パスの確認
 $ echo $PATH
(2)パスの設定
 $ PATH=$PATH:(パス1) : (パス2) : ・・・
 $ export PATH
 ログインしたとき自動的に設定するためには、ホームディレクトリ配下の~/.bash_profileに上記のコマンドを追加する。

The end

マルチプロセスとマルチスレッド(Linux)

目次

1.マルチプロセス
(1)概要
 プロセスはOSによってCPUに割り当てられたプログラムの実行単位で、プロセスには連続した仮想メモリ空間が用意されます。マルチプロセスは複数のプロセスが並列に実行できるように、CPUリソースを時間などで分割して共用します。
 マルチプロセス(マルチタスクともいう)では各プロセスにコンテキストが紐づいていて、CPUのレジスタの情報をコンテキストとして保持し、プログラムの実行に応じてコンテキストを切り替えることで処理を継続します。

(2)サンプル

 multi_process.c

#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/types.h>	//getpid,fork
#include	<unistd.h>	//getpid,fork
#include	<sys/wait.h>	//wait

//プロトタイプ宣言
void counter();

int main(void)
{
	pid_t	p_pid,pid;	//int型
	int status1,status2;
	p_pid=getpid();	//プロセスID取得(※1)
	printf("[%d]start\n",p_pid);
	switch(pid=fork()){	//子プロセス生成(※2)
		case	0:	//子プロセス
			counter();
			exit(0);
		case    -1:	//エラー
			perror("fork");	//エラーメッセージの出力
			break;
		default:	//親プロセス
			printf("[%d]子プロセスID : %d\n",p_pid,pid);
			break;
	}

	switch(pid=fork()){	//子プロセス生成(※2)
		case 0:	//子プロセス
			counter();
			exit(0);
		case -1:	//エラー
			perror("fork");	//エラーメッセージの出力
			break;
		default:	//親プロセス
			printf("[%d]子プロセスID : %d\n",p_pid,pid);
			break;
	}

	pid=wait(&status1);	//子プロセスの状態変化を待つ(※3)
	printf("[%d]pid = %d end status:%d\n",p_pid,pid,status1);//	子プロセス終了
	pid=wait(&status2);	//子プロセスの状態変化を待つ(※3)
	printf("[%d]pid = %d end status:%d\n",p_pid,pid,status2);//	子プロセス終了
	printf("[%d]end\n",p_pid);	//親プロセス終了

	return 0;
}

void counter()
{
	int i;
	pid_t pid;
	pid=getpid();	//プロセスID取得
	for(i=0;i<10;i++){
		sleep(1);
		printf("[%d]%d\n",pid,i);
	}
return;
}

(※1)getpid()
getpid()は呼び出し元のプロセスのプロセスIDを返す。
getppid()は呼び出し元のプロセスの親プロセスのプロセスIDを返す。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);  

(※2)fork()
呼び出し元プロセスを複製して新しいプロセスを生成する。
新しいプロセスは「子」プロセスと呼ばれ、呼び出し元プロセスは「親」プロセスと呼ばれる。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
戻り値:
成功した場合、親プロセスには子プロセスの PID が返され、
子プロセスには0が返される。
失敗した場合、親プロセスに-1が返され、子プロセスは生成されず、errno が適切に設定される。

(※3)wait()
子プロセスの状態変化を待ち、子プロセスのいずれかが終了するまで呼び出し元のスレッドの実行を一時停止する。
(書式) 
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
戻り値:
wait():成功すると、終了した子プロセスのプロセスIDを返す。エラーの場合 -1 を返す。

(3)実行結果

2.マルチスレッド
(1)概要
 スレッドはプロセス内で命令を実行する部分においてCPUコアを利用する単位で、1つのプロセスは1つ以上のスレッドから成ります。
 最近のCPUはコアが複数あり、1つのプロセスを複数のスレッドに分けることでCPUコアの利用率を上げて、処理の高速化が図れます。
 SMT (Simultaneous Multi-Threading)機能を備えたCPUでは1つのコアで複数のスレッドを同時に実行することができます(4コア8スレッドなど)。
 また、1つのプロセスの仮想メモリ空間をスレッドで共用するため、メモリの不整合が生じないようにスレッド間で同期をとる仕組みが必要となります。

(2)サンプル

multi_thread.c

#include	<stdio.h>	
#include	<stdlib.h>	
#include	<sys/types.h>	//getpid
#include	<pthread.h>	//pthread_create,pthread_join,pthread_self
#include	<unistd.h>	//sleep,getpid
#include	<string.h>	//strerror

//プロトタイプ宣言
void *counter(void *arg);

void main()
{
	pid_t	p_pid;
	pthread_t	thread_id1,thread_id2;
	int	status;
	void 	*result;

	p_pid=getpid();

	printf("[%d]start\n",p_pid);
	//1つ目のスレッド生成
	status=pthread_create(&thread_id1,NULL,counter,(void *)NULL);	//(※1)
	if(status!=0){
		fprintf(stderr,"pthread_create : %s",strerror(status));	//(※2)
	}
	else{
		printf("[%d]thread_id1=%d\n",p_pid,thread_id1);
	}
	//2つ目のスレッド生成
	status=pthread_create(&thread_id2,NULL,counter,(void *)NULL);	//(※1)
	if(status!=0){
		fprintf(stderr,"pthread_create : %s",strerror(status));	//(※2)
	}
	else{
		printf("[%d]thread_id2=%d\n",p_pid,thread_id2);
	}

	pthread_join(thread_id1,&result);	//スレッドの終了待ち(※3)
	printf("[%d]thread_id1 = %d end\n",p_pid,thread_id1);
	pthread_join(thread_id2,&result);	//スレッドの終了待ち(※3)
	printf("[%d]thread_id2 = %d end\n",p_pid,thread_id2);
	printf("[%d]end\n",p_pid);	//プロセス終了
}

void *counter(void *arg)
{
	int     i;
	pid_t   pid;
	pthread_t	thread_id;
	pid=getpid();	//プロセスID取得
	thread_id=pthread_self();	//呼び出したスレッドのIDを取得する(※4)

	for(i=0;i<10;i++){
		sleep(1);	//指定の秒数休止(※5)
		printf("[%d][%d]%d\n",pid,thread_id,i);
	}

	return(arg);
}

(※1)pthread_create()
新しいスレッドを生成する
(書式)
#include <pthread.h>
int pthread_create(pthread_t * thread,
pthread_attr_t * attr,
void * (*start_routine)(void *),
void * arg);
引数:
thread:スレッド管理用
attr:スレッド属性、NULLのときはデフォルト
(*start_routine)(void *):スレッドから呼び出される関数へのポインタ
arg:start_routine()の引数で渡すデータのポインタ
戻り値:
成功すると新しく作成したスレッドの識別子が引数threadの指す領域へ格納され、0 が返る。エラーの場合、非 0 のエラーコードが返る。

(※2)strerror()
エラー番号を説明する文字列を返す。
(書式)
#include <string.h>
char *strerror(int errnum);
const char *strerrorname_np(int errnum);
const char *strerrordesc_np(int errnum);
int strerror_r(int errnum, char *buf, size_t buflen);
            /* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
            /* GNU-specific */
char *strerror_l(int errnum, locale_t locale);
関数 strerror(), strerror_l() と GNU 固有の関数 strerror_r()
はエラー内容を説明する文字列を返す。
エラー番号が未知の場合は “Unknown error nnn” という メッセージを返す。

(※3)pthread_join()
別のスレッドの終了を待つ
(書式)
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
引数:
th:待機するスレッドの指定
**thread_return:スレッドの戻り値を格納する領域
戻り値:
成功すると、thの返り値がthread_returnで指し示された領域に格納され、0が返る。エラーの場合、非 0 のエラーコードが返る。

(※4)pthread_self()
呼び出したスレッドのIDを取得する
(書式)
#include <pthread.h>
pthread_t pthread_self(void);

(※5)sleep()
指定の秒数の間だけ休止する
(書式)
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
戻り値:
要求された時間が過ぎた場合はゼロを返す。
呼び出しがシグナルハンドラーに割り込まれた場合は、
休止の残り時間を返す。

(3)実行結果
 コンパイルはリンカにリンクするライブラリを伝えるためのオプション-lpthreadまたは-pthreadを指定する必要があります。

The end

ソケット通信(Linux)

目次

1.ソケット通信とは
 ソケットAPIを用いた異なるホスト間の通信のアプリケーションです。ソケットAPIはトランスポート層以下の機能を提供するプログラミングインターフェースでクライアント/サーバモデルの通信を行うために用いられます。

2.ソケットを用いたサーバ/クライアント間通信
 ソケットの種類として、TCPプロトコルを使うコネクション型のストリームソケットと、UDPプロトコルを使うコネクションレス型のデータグラムソケットがあります。
(1)ストリームソケットを使用したコネクション型の通信

(2)データグラムソケットを使用したコネクションレス型の通信

3.サンプルプログラム


3.1 コネクション型
 ストリームソケットを使用したコネクション型の通信のサンプルを作成します。
(1)サンプルの概要
 サーバはクライアントからの接続を待ち、クライアントがサーバに接続すると、サーバはクライアントからの送信電文をコンソールに表示する。サーバはクライアントに電文を送信して通信を終了する。クライアントはサーバからの電文をコンソールに表示して通信を終了する。

(2)ソースプログラム
 server.c

#include<sys/types.h>	//socket,bind,listen,accept,send
#include<sys/socket.h>	//socket,bind,listen,accept,send
#include<netinet/in.h>	//sockaddr_in
#include<netdb.h>	//hostent
#include<unistd.h>	//close
#include<stdio.h>	//perror,fdopen,fgets,
#include<stdlib.h>	//atoi,exit,
#include<string.h>	//bzero,strcmp
#include<errno.h>

char *resmsg="Hello to client from Server\r\n";

int main(int argc, char* argv[])
{
	FILE *fp;	//ファイルポインタ
	int s; //サーバ待ち受けのソケットディスクリプタ
	int ns; //クライアントから受入れるソケットディスクリプタ
	int port; //ポート番号
	struct sockaddr_in sin;	// ホストのIPアドレスなど情報を保持する構造体(※1)
	struct sockaddr_in fsin;	// クライアントの		〃
	char buf[128];	//受信バッファ128バイト
	
	socklen_t fromlen = sizeof(struct sockaddr_in);	//socklen_t型:unsigned int型の別名
	//コマンド引数が足りないときエラー
	if((argc <= 1) || ((port = atoi(argv[1])) == 0)) {	//文字ストリングから整数に変換
	  perror("no port number");	//エラーメッセージの出力
	  exit(EXIT_FAILURE);
	}
	//ソケットの作成(※2)
	if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("client socket()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}
	//バインド(※3)
	bzero(&sin, sizeof(sin));	//構造体sinを0で初期化
	sin.sin_family = AF_INET;	//アドレスファミリ  AF_INET:IPv4インターネットプロトコル
	sin.sin_port = htons(port);	//ポート番号をTCP/IPネットワークバイトオーダーに変換
	sin.sin_addr.s_addr = INADDR_ANY;	//任意のIPアドレスからも受付
	
	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
		perror("server bind()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);	
	}
	//リッスン(※4)
	if (listen(s, 128) == -1) {	//新規コネクションの最大数128
		perror("server listen()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);	
	}
	//アクセプト(※5)
	//接続待ちソケットのキューから新規にソケットを生成
	if ((ns = accept(s, (struct sockaddr *)&fsin, &fromlen)) == -1) {
		perror("server accept()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}
	
	fp = fdopen(ns, "r");	//ファイルディスクリプターにストリームを結びつける(※6)
	
	//クライアントからデータを取得しコンソールに出力(※7)
	while (fgets(buf, sizeof(buf), fp) != NULL) {
		if(strcmp(buf,"\r\n") == 0) break;	//CR+LFで読み込み停止
		printf("%s", buf);
	}
    //クライアントに送信(※8)
	send(ns, resmsg, strlen(resmsg), 0);
	//ファイルディスクリプターをクローズ(※9)
	close(ns); // クライアント接続用
	close(s); // サーバ待ち受け用
	return 0;
}

(※1)sockaddr_in構造体
struct sockaddr_in {
u_char sin_len;
u_char sin_family; (アドレスファミリ.今回はAF_INETで固定)
u_short sin_port; (ポート番号)
struct in_addr sin_addr; (IPアドレス)
char sin_zero[8];
};
ポート番号やIPアドレスはネットワークバイトオーダー (big endian) になっていないといけない。このため、整数をネットワークバイトオーダーに変換するhtons関数を用いる。

(※2)socket()
通信のための端点 (endpoint) を作成する
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
引数:
①domain
アドレスファミリーに指定できるプロトコル
AF_INET インターネットプロトコル
など
②type
通信方式 (semantics) を指定する。
定義されている型は現在以下
SOCK_STREAM:
順序性と信頼性があり、双方向の、接続された バイトストリーム (byte stream) を提供する。
帯域外 (out-of-band) データ転送メカニズムもサポートされる。
SOCK_DGRAM:
データグラム (コネクションレス、信頼性無し、固定最大長メッセージ) をサポートする。
など
③protocol
ソケットによって使用される固有のプロトコルを指定する。
与えられたプロトコルファミリーの種類ごとに一つのプロトコルのみをサポートする。その場合はprotocolに0を指定できる。
戻り値:ディスクリプタ
プログラムからファイルを操作する際、操作対象のファイルを識別・同定するために割り当てられる番号

(※3)bind()
ソケットが作成されたときは名前空間 (アドレスファミリー) に存在するがアドレスは割り当てられていない。bind() はファイルディスクリプターsockfdで参照されるソケットにaddrで指定されたアドレスを割り当てる。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
返り値:成功:0  失敗:-1
引数:
①int sockfd
②sockaddr *addr
③socklen_t addrlen

(※4)listen()
ソケットを待ち状態にする
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int listen(int s, int backlog);
引数:
①s:
ソケットディスクリプタ
②backlog:
新規コネクションの最大数を指定する

(※5)accept()
クライアントからの接続を受け入れコネクションを確立する。この関数は、接続待ちソケットsocket宛ての保留状態の接続要求が入っているキューから先頭の接続要求を取り出し、接続済みソケットを新規に生成し、そのソケットを参照するための新しいファイルディスクリプターを返す。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
引数:
①s
ソケットディスクリプタ
②addr
宛先の情報を戻すsockaddr構造体
③addrlen
sockaddr構造体の大きさを指定する

(※6)fdopen()
既存のファイルディスクリプターにストリームを結びつける。ストリームの mode (“r”, “r+”, “w”, “w+”, “a”, “a+” のいずれか) はファイルディスクリプターのモードと互換のものでなければならない。

(※7)fgets()
streamから最大でsize-1個の文字を読み込み、sが指すバッファーに格納する。読み込みは EOF または改行文字を読み込んだ後で停止する。読み込まれた改行文字はバッファーに格納される。終端のヌルバイト (‘\0’) が一つバッファーの中の最後の文字の後に書き込まれる。
(書式)
#include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);
char *fgets(char *s, int size, FILE *stream);

(※8)send(), sendto(), sendmsg()
ソケットが接続された (connected) 状態にある場合(送信相手が決まっている)にソケットへメッセージを送る 。
(書式)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
引数:
①int sockfd
②const void
③*buf
④size_t len
⑤int flags

(※9)close()
ファイルディスクリプターをクローズする
(書式)
#include <unistd.h>
int close(int fd);int close(int fd);

 client.c

#include<sys/types.h>	//socket,send
#include<sys/socket.h>	//socket,send
#include<netinet/in.h>	//sockaddr_in
#include<netdb.h>	//hostent
#include<unistd.h>	//close
#include<stdio.h>	//perror,fdopen,fgets
#include<stdlib.h>	//atoi,exit
#include<string.h>	//bzero,memcpy
#include<errno.h>

#define NUMSTR  3

char *reqmsg[NUMSTR] = {	//ポインタ配列
  "Request from client\r\n",
  "Hello to Server\r\n",
  "\r\n"
};

int main(int argc, char* argv[])
{
	FILE *fp;	//ファイルポインタ
	char hostname[128];	//受信バッファ
	int s;	//サーバ接続のソケットディスクリプタ
	int port;//	ポート番号
	struct hostent *hp;	//hostent構造体(ホスト名など)(※1)
	struct sockaddr_in sin;	//接続先のIPアドレスやポート番号の情報を保持するための構造体
	char buf[128];	//受信バッファ
	
	//コマンド引数が足りないときエラー
	if(argc <= 2) {
	  perror("host and port required");	//エラーメッセージの出力
	  exit(EXIT_FAILURE);		
	}
	if((port = atoi(argv[2])) == 0) {	//文字ストリングから整数に変換
	  perror("no port");	//エラーメッセージの出力
	  exit(EXIT_FAILURE);
	}
	
		if ((hp = gethostbyname(argv[1])) == NULL) {	//ホスト名からIPアドレスを取得(※2)
		fprintf(stderr, "%s: unknown host.\n", hostname);
		exit(EXIT_FAILURE);
	}

	/* IPアドレス表示(確認用) start */
	// IPv4は4バイト
    if(hp->h_length != 4){
        printf("IPv6 address.");
        exit(EXIT_FAILURE);
    }

	for(int i=0; hp->h_addr_list[i]; i++){
        printf("IP address(%d)    = %d.%d.%d.%d\n" , i, 
                (unsigned char)*((hp->h_addr_list[i])) ,
                (unsigned char)*((hp->h_addr_list[i]) + 1) ,
                (unsigned char)*((hp->h_addr_list[i]) + 2) ,
                (unsigned char)*((hp->h_addr_list[i]) + 3)
        );
    }
	/* IPアドレス表示 end */

	//ソケット
	if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("client socket()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);		
	}

	//コネクト(※3)
	bzero(&sin, sizeof(sin));	//構造体sinを0で初期化
	sin.sin_family = AF_INET;	//アドレスファミリ  AF_INET:IPv4インターネットプロトコル
	sin.sin_port = htons(port);	//ポート番号
	memcpy(&sin.sin_addr, hp->h_addr, hp-> h_length);	//IPアドレス(※4)
	
	if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
		perror("client connect()");	//エラーメッセージの出力
		exit(EXIT_FAILURE);	
	}
	
	fp = fdopen(s, "r");	//ファイルディスクリプターfdにストリームを結びつける

	// サーバにデータを送信
	for(int i = 0; i < NUMSTR; i++) {
	  send(s, reqmsg[i], strlen(reqmsg[i]), 0);
	}

	// サーバからのデータ受信待ち
	while (fgets(buf, sizeof(buf), fp) != NULL) {
	  printf("%s", buf);
	}
	
	close(s);	//ソケットディスクリプターをクローズ
	return 0;
}

(※1)hostent構造体
struct hostent {
char h_name; / ホストの正式名称 */
char *h_aliases; / 別名リスト / int h_addrtype; / ホストアドレスのタイプ (AF_INET6 など) / int h_length; / アドレスの長さ */
char *h_addr_list; / NULL で終わるアドレスのリスト */
};

(※2)gethostbyname()
ホスト名からIPアドレスを得る
(書式)
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);

(※3)connect()
ソケットの接続を行う
(書式)
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
ファイルディスクリプターsockfdが参照しているソケットをaddrで指定されたアドレスに接続する
戻り値:成功:0、失敗:-1

(※4)memcpy()
メモリー領域srcの先頭nバイトをメモリー領域 dest にコピーする。
(書式)
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);

(3)コンパイルと実行

$ gcc server.c -o server
$ gcc client.c -o client
先にサーバアプリを起動する
$ ./server 1050(1024未満のポート番号は非rootユーザーは使用不可)

他の端末を開き、接続先サーバの受付ポートに接続
$ ./client localhost 105

サーバ側にはクライアントが送信したメッセージを表示し、クライアント側ではサーバが送信したメッセージが表示される。

3.2 コネクションレス型
 データグラムソケットを使用したコネクションレス型の通信のサンプルを作成します。
(1)サンプルの概要
 サーバはクライアントからの接続を待ち、クライアントがサーバに検索を要求すると、サーバは検索結果をクライアントに返す。クライアントは検索結果をコンソールに表示して通信を終了する。

(2)ソースプログラム
 server_cl.c

/* コネクションレス型のソケット通信(サーバ) */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>	//socket,bind,recvfrom,sendto
#include <sys/socket.h>	//socket,bind,recvfrom,sendto
#include <netinet/in.h>	//sockaddr_in
#include <netdb.h>	// gethostbyname() 
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define  MAXHOSTNAME	64
#define  S_UDP_PORT	(u_short)5000  //本サーバのポート番号
#define  MAXKEYLEN	128
#define  MAXDATALEN	256

//プロトタイプ宣言
void db_search(int);//	検索

int main()
{
	int	s;	//ソケットディスクリプタ
	char	s_hostname[MAXHOSTNAME];	//ホスト名変数
	struct hostent	*hp;	//hostent構造体(ホスト名など)
	struct sockaddr_in	sin;	//ホストのIPアドレスなど情報を保持する構造体


	//IPアドレス求める
	gethostname(s_hostname, sizeof(s_hostname));
	hp = gethostbyname(s_hostname);
	
	// ソケットの作成
	if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		perror("socket");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}

	// バインド
	bzero((char *)&sin, sizeof(sin));	//構造体sinを0で初期化
	sin.sin_family = AF_INET;	//アドレスファミリ  AF_INET:IPv4インターネットプロトコル
	sin.sin_port = htons(S_UDP_PORT);	//ポート番号
	bcopy((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);

	// ソケットへアドレス割り当て
	if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
		perror("bind");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}
	
	// クライアントからの検索要求受付
	db_search(s);

	return 0;
}

void db_search(int s)	//クライアントからの検索要求
{
	struct sockaddr_in	c_address;	//接続先のIPアドレスやポート番号の情報を保持するための構造体
	int	c_addrlen;
	char	key[MAXKEYLEN+1], data[MAXDATALEN+1];
	int	keylen, datalen;
	//ポインタ配列に文字列データを入れる
	static char *db[] = {"aoyama","aoyama@test.com","suzuki","suzuki@test.com",
                             "ando","ando@test.com",NULL};
	char	**dbp;

	while(1) {
		/* キーをソケッから読み込む */
		c_addrlen = sizeof(c_address);
		//ソケットから受信(※1)		
		if((keylen = recvfrom(s, key, MAXKEYLEN, 0, (struct sockaddr *)&c_address, &c_addrlen)) == -1) {
			perror("recvfrom");
			exit(EXIT_FAILURE);
		}
		key[keylen] = '\0';
		printf("Received key> %s\n",key);
		// キー検索
		dbp = db;
		while(*dbp) {	//ポインタ配列の繰り返し
			if(strcmp(key, *dbp) == 0) {
				strcpy(data, *(++dbp));	//keyが一致したらdataを取得
				break;
			}
			dbp += 2;//ポインタ配列を2つ(keyとdataの分)増やす
		}
		if(*dbp == NULL) strcpy(data, "No entry");
	
		// 検索したデータをソケットに書き込む(※2)
		datalen = strlen(data);
		if(sendto(s, data, datalen, 0, (struct sockaddr *)&c_address, c_addrlen) != datalen) {
			fprintf(stderr, "datagram error\n"); 
			exit(EXIT_FAILURE);
		}
		printf("Sent data> %s\n", data);
	}
}

(※1)recvfrom()
ソケットからメッセージを受け取る
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

(※2)sendto()
ソケットへメッセージを送る
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

 client_cl.c

/* コネクションレス型のソケット通信(クライアント)*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>	//socket,bind,recvfrom,sendto
#include <sys/socket.h> //socket,bind,recvfrom,sendto
#include <netinet/in.h> //sockaddr_in
#include <netdb.h>      //gethostbyname()
#include <errno.h>
#include <string.h>
#include <unistd.h>	//close
#define  MAXHOSTNAME    64
#define	 S_UDP_PORT	(u_short)5000 //サーバのポート番号
#define  MAXKEYLEN	128
#define	 MAXDATALEN	256

//プロトタイプ宣言
void remote_dbsearch(int, struct sockaddr_in*, int);//	リモート検索

int main()
{
	int	s;	//ソケットディスクリプタ
	char	s_hostname[MAXHOSTNAME];
	struct hostent	*hp;	//ホスト名変数
	struct sockaddr_in s_address;	//ホストのIPアドレスなど情報を保持する構造体
	struct sockaddr_in c_address;	//クライアントのIPアドレスなど情報を保持する構造体
	int	s_addrlen;

	// ホスト名入力
	printf("server host name?: "); scanf("%s",s_hostname);
	//IPアドレス求める
	if((hp = gethostbyname(s_hostname)) == NULL) {
		fprintf(stderr, "server host does not exists\n");
		exit(EXIT_FAILURE);
	}
	// ソケットの作成
    if((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
 	  	perror("socket");	//エラーメッセージの出力
       	exit(EXIT_FAILURE);
    }
	// サーバホストのアドレスなどの情報設定
    bzero((char *)&s_address, sizeof(s_address));	//構造体sinを0で初期化
    s_address.sin_family = AF_INET;	//アドレスファミリ  AF_INET:IPv4インターネットプロトコル
    s_address.sin_port = htons(S_UDP_PORT);	//ポート番号
    bcopy((char *)hp->h_addr, (char *)&s_address.sin_addr, hp->h_length);
	s_addrlen = sizeof(s_address);
	// クライアントのアドレスなどの情報設定
    bzero((char *)&c_address, sizeof(c_address));	//構造体sinを0で初期化
    c_address.sin_family = AF_INET;	//アドレスファミリ  AF_INET:IPv4インターネットプロトコル
    c_address.sin_port = htons(0);	//ポート番号
	c_address.sin_addr.s_addr = htonl(INADDR_ANY); // Internet
	// クライアントアドレスのソケッへの割り当て
	if(bind(s, (struct sockaddr *)&c_address, sizeof(c_address)) == -1) {
		perror("bind");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}
	
	// データベース検索
	remote_dbsearch(s, &s_address, s_addrlen);
	close(s);
	return 0;
}	
//リモート検索
void remote_dbsearch(int s, struct sockaddr_in *s_addressp, int s_addrlen) // 検索
{
	char	key[MAXKEYLEN+1], data[MAXDATALEN+1];
	int	keylen, datalen;

	// キー値入力
	printf("key?: ");scanf("%s",key);
	// キー値をソケットに書き込む
	keylen = strlen(key);
	if(sendto(s, key, keylen, 0, (struct sockaddr *)s_addressp, s_addrlen) != keylen) {
		fprintf(stderr, "datagram error\n");
		exit(EXIT_FAILURE);
	}
	// 検索結果をソケットから読み込む
	if((datalen = recvfrom(s, data, MAXDATALEN, 0, NULL, &s_addrlen)) == -1) { 
		perror("recvfrom");	//エラーメッセージの出力
		exit(EXIT_FAILURE);
	}
	// データ表示
	data[datalen] = '\0';
	fputs("data: ",stdout);puts(data);
}

(3)コンパイルと実行

$ gcc server_cl.c -o server_cl
$ gcc client_cl.c -o client_cl
先にサーバアプリを起動する
$ ./server_cl 

他の端末を開き、接続先サーバの受付ポートに接続
$ ./client_cl
server host name?: localhost
key?: suzuki

サーバ側にはクライアントから受信したkey値と検索結果を表示し、クライアント側ではサーバから受信した検索結果を表示する。

The end

WSL(Windows Subsystem for Linux)の導入

1.WSLとは
 Windowsの中でLinux環境を動かす仕組みであり、Microsoft Store から好みの GNU/Linux ディストリビューションを選択してインストールすることができます。
 Windowsのアプリケーションのインストールと同じようにアンインストールもできます。
 
 下記のドキュメントに沿ってインストールを行います。
(参考文献)
・Windows/開発環境/WSL/インストール/WSL のインストール
 https://docs.microsoft.com/ja-jp/windows/wsl/install

2.前提条件
 Windows 10 バージョン 2004 以降 (ビルド 19041 以降)
 Windows ロゴ キー + R キーを押して、「winver」と入力し、 [OK] を選択。名前に「winver」を入力して[OK]押す。

3.WSLのインストール
(1)仮想化環境の設定
 WSL2は仮想化技術を利用するため、下記の設定を行う。
 「設定」から「アプリ」-「オプション機能」-「Windowsのその他の機能」

(2)インストール

 ダウンロードした更新プログラムパッケージ「wsl_update_x64.msi」を実行します。

(3)手順 5 – WSL 2 を既定のバージョンとして設定する
WindowsPowerShellを起動し下記のコマンドを実行

> wsl --set-default-version 2

(4)手順 6 – 選択した Linux ディストリビューションをインストールする
 Microsoft Store を開き、希望する Linux ディストリビューションを選択します。
 ここでは、”Ubuntu”で検索し、Ubuntu 18.04 LTSを選択し、「インストール」を選択。

「開く」を選択する

username:を入力する。
password:を入力する。

プロンプトが表示されるので、コマンドを入力してlinuxシェル(デフォルトのシェルbash)を実行する。

 exitで終了します。
 再度起動するときはWindowsのスタートメニューで
「Ubuntu18.04 TLS」を選択します。
 コマンドプロンプトから”wsl”コマンドを実行することでもlinuxのコマンド入力を行うことができます。

(5)アンインストール
 他のアプリケーションとの同じ方法でUbuntuのアンインストールを行えます。

The end

オブジェクト指向について

 ソフトウェア開発のオブジェクト指向について、初めて学んだときは抽象的で理解し難ったものですが、実際にオブジェクト指向のプログラミング言語を使ってみることでメリットがわかったように思います。大まかにオブジェクト指向のイメージがつかめるようにまとめて見ました。

1.オブジェクト指向とは
 オブジェクト指向は、ソフトウェア開発とコンピュータプログラミングのために用いられる考え方である。
(途中省略)
 オブジェクトとは、プログラミング視点ではデータ構造とその専属手続きを一つにまとめたものを指しており、分析/設計視点では情報資源とその処理手順を一つにまとめたものを指している。データとプロセスを個別に扱わずに、双方を一体化したオブジェクトを基礎要素にし、メッセージと形容されるオブジェクト間の相互作用を重視して、ソフトウェア全体を構築しようとする考え方がオブジェクト指向である。(引用:wikipedia)

2.オブジェクト指向を使って見る
 実際にオブジェクト指向が活かされているところをプログラムで使って見ます。下記ではスクリプトで簡単で試せるPython言語を使っています。
(1)動作環境 
 CentOS8(VirtualBoxの仮想マシン上)のPython用開発環境(IDLE:Integrated DeveLopment Environment)

(2)実行
 CentOS8の端末からidle3でIDLEを起動します。

 ここでは、変数aに文字列”Hello”を代入し、a.count(“l”)とすることで、”l”の文字の数をカウントすることができました。これがオブジェクト指向の一面です。
 Pythonは動的型付け言語のため、変数aに文字列を入力すると、”str”クラスと認識され、変数a(strクラスのオブジェクト)に対して、”str”クラスにあるメソッド(ここでは文字を数えるcount(‘文字’)メソッド)が利用できました。
 ”str”クラスはPythonに予め組み込まれているクラスですが、オブジェクト指向の言語ではクラスを定義して使うことができます。

3.クラスの作成
 文中のプログラム命令名はjavaの場合について記載しています。
(1)クラスの構成要素
 オブジェクト指向のプログラミング言語には、C#、java、Python、Cにオブジェクト指向を取り入れたC++などがあり、言語によって差異はありますがクラスの構成要素には以下のものがあります。(javaを例に記載)
①変数(フィールド変数)
 クラスの中で扱う変数で”private”修飾子でカプセル化※(外部のクラスに非公開)などができます。
②コンストラクタ
 オブジェクトを作るときにフィールド変数の初期化などを行うメソッドですがクラスのメンバではない(オブジェクトを作るときにのみ使う)。
 また、引数の数などによって、複数のコンストラクタを定義することができます(コンストラクタのオーバーロード)。
③アクセサメソッド
 ①で変数を”private”修飾子でカプセル化したとき、外部のクラスから変数にアクセスする方法として、必要に応じて変数に対して値をセット、又はゲットするメソッドを定義します。
④その他メソッド
 クラスのメンバとして、処理を行うメソッドを定義します。

(2)継承※
 親クラスを”extends”宣言で継承して、親クラスのメンバ(フィールド変数、メソッド)を使うことができます。
 また、親クラスのメソッドを書き換える(オーバーライド)して、親クラスのメソッド名を使ってオーバーライドしたメソッドを使うことができます(ポリモーフィズム※)。
 
 上記で※印の機能をオブジェクト指向の3大要素といい、これらの要素を活かしたクラスの設計を行ってオブジェクト指向のソフトウェア開発を行います。

The end

Linuxの環境変数について

1.環境変数とは
 環境変数(environment variable)はオペレーティングシステム (OS) が提供するデータ共有機能の一つ。OS上で動作するタスク(プロセス)がデータを共有するための仕組みである。特にタスクに対して外部からデータを与え、タスクの挙動・設定を変更するために用いる。
(引用:wikipedia)

2.環境変数の確認・変更・削除
(1)設定値の確認

$ env

環境変数名を指定して確認するためには

$ echo $環境変数
例:$ echo $DISPLAY

(2)設定
 シェルがbashの場合

$ export 環境変数名=値
例:$ export DISPLAY=0

(3)環境変数の削除

$ unset 環境変数名

3.環境変数の設定ファイル
(1).bash_profile
 ログインしたときに読み込まれる設定ファイルで、ログインした直後のディレクトリ(/root、/home/ユーザなど)にあります。
 
(2).bashrc
 ログインしたあと、bashを起動したときに読み込まれる設定ファイルです。ログインシェルがbashのときは、.bashrcは読み込まれませんが、.bash_profileの中で読み込まれている場合があります。

(3).bash_logout
 ログインシェルがbashのときにログインして、そこからログアウトしたときに読み込まれる設定ファイルです。

The end