MACフレームの送信について
これからMACフレームの送受信の動きを確認します。
まず記事に添付されているサンプルコードをダウンロードしてから、自分の好きな方の言語のソリューションを開いてください。そして、SedMacFreamTest
プロジェクトを開始プロジェクトにしてから実行します。
すると、端末AからBへデータを送信した場合、端末Bは受け取って、他の端末はデータを受信してから破棄したことがコンソール画面に表示されます。この動きがEthernetにおけるデータ送受信の基礎です。
Ethernetでは基本的に、データを送信した端末以外のHUBに繋がれている全ての端末がデータを受信します。そして受信した端末は自分宛かどうかを調べて、自分宛なら受信処理を行い、違うのならば受信したデータを破棄します(注1)。
本当はこの動作に加えてFCSをチェックする処理があるのですが、この計算は難しい上にプログラマ自身が計算する必要がないので、この連載では省略しています。
これが正常時の状態です。一方、同時に端末がデータを送信してしまう時があります。この状態について体験するために、サンプルコードのCollisionTest
プロジェクトをスタートプロジェクトに設定してから実行してください。そうすれば、端末Aと端末Dが何度もデータの送信を試みていることが確認できます(※注2) この時に使用するルールがCSMA/CD(Carrier Sense Multiple Access with Colision Detection)方式です。次項ではこのCSMA/CD方式について解説します。
実際はこれほど頻繁に衝突が発生する事はありませんが、衝突を確認するために衝突が起こりやすいように実装しています
CSMA/CD方式について
LANに接続される端末は、1つの伝送媒体を共有してデータのやり取りを行っているといえます(先ほどHUBで複数のLANケーブルを接続しましたが、論理的には1つの伝送媒体とみなされています)。この状況で考えなくてはならないのは「どうやってアクセスするか」という点です。
1つの伝送媒体を共有している以上、好き勝手にデータの送受信を行えず、あらかじめ定められたルールに従って制御する必要があります。これを「アクセス制御方式」と呼びます。このアクセス制御方式の1つがCSMA/CD方式です。
CSMA/CD方式では、ある瞬間にデータを伝送するのは1つの端末になるように制御します。この方式は以下の3つの特徴があります。
キャリア感知で伝送媒体の空きを判定
伝送媒体にデータが流れると、搬送波(キャリア)が流れます。データを送信する端末はこのキャリアを感知して、伝送路が空いているか確認します。
早い者勝ち
伝送媒体のデータ送信の権利は基本的に早い者勝ちです。伝送媒体が空いていれば、どの端末でもデータを伝送できます。こうすることにより、たくさんのコンピュータが平等に処理ができますし、伝送媒体が有効活用できます。
衝突処理
キャリア感知で伝送媒体の空きをチェックしても、複数のコンピュータが伝送媒体を空いていると判断して送信してしまうことがあります。これを衝突(コリジョン)と呼びます。コリジョンは、1つのデータが流れる以上の電圧が発生するので全てのコンピュータが分かります。コリジョンが発生した時は、データを送信しようとしたコンピュータがランダムに待って、再び送信を試みます。この待ち時間のことを「バックオフ時間」と呼びます。 この処理についてはサンプルコードのCsmaCdControler
のNic_Collision
メソッドが参考になると思います。ぜひ目を通してください。
Private Sub Nic_Collision(ByVal sender As Object, ByVal e As EventArgs) Dim sends As List(Of CsmaCd) = New List(Of CsmaCd)() 'データを送信したPCにバックオフ時間を決定させる For Each obj As Pc In Me.targets If obj.ExpansionCard.IsSend = True Then obj.CsmaCdInfo.SetBackOffTime(Me.rand) sends.Add(obj.CsmaCdInfo) End If Next 'バックオフ時間に基づき送信するPCを決める sends.Sort() '衝突したデータ2つ分のシミュレート If sends(0).BackOffTime = sends(1).BackOffTime Then 'バックオフ時間が同じなのでまた衝突 sends(0).ManagementPC.ExpansionCard.SendRetry() sends(1).ManagementPC.ExpansionCard.SendRetry() Else 'もう一回データを送る(こっちは衝突しない) sends(0).ManagementPC.ExpansionCard.SendRetry() Me.sharingHub.SendEnter() sends(1).ManagementPC.ExpansionCard.SendRetry() Me.sharingHub.SendEnter() 'データの初期化 For Each obj As CsmaCd In sends obj.ManagementPC.ExpansionCard.IsSend = False obj.ResetTime() Next End If End Sub
private void Nic_Collision( object sender, EventArgs e ) { List sends = new List( ); //データを送信したPCにバックオフ時間を決定させる foreach ( Pc obj in this.targets ) { if ( obj.ExpansionCard.IsSend == true ) { obj.CsmaCd.SetBackOffTime( this.rand ); sends.Add( obj.CsmaCd ); } } //バックオフ時間に基づき送信するPCを決める sends.Sort( ); //衝突したデータ2つ分のシミュレート if ( sends[ 0 ].BackOffTime == sends[ 1 ].BackOffTime ) { //バックオフ時間が同じなのでまた衝突 sends[ 0 ].ManagementPC.ExpansionCard.SendRetry( ); sends[ 1 ].ManagementPC.ExpansionCard.SendRetry( ); } else { //もう一回データを送る(こっちは衝突しない) sends[ 0 ].ManagementPC.ExpansionCard.SendRetry( ); this.sharingHub.SendEnter( ); sends[ 1 ].ManagementPC.ExpansionCard.SendRetry( ); this.sharingHub.SendEnter( ); //データの初期化 foreach ( CsmaCd obj in sends ) { obj.ManagementPC.ExpansionCard.IsSend = false; obj.ResetTime( ); } } }
この方式は10ギガビットEthernet以降では使われていません。しかし、10ギガビット以上の規格はまだそれ程普及していませんし、この方式は無線LANのアクセス制御方式であるCSMA/CA(Carrier Sense Multiple Access with Colision Avoidance)方式とほぼ同じなので覚えておいて損はないでしょう。次項では10ギガビットEthernet以降で使用されるスイッチングハブについて説明します。