サーバプログラミングって何?


サーバは簡単に言うと、次のような作法で作ることができます。

接続要求受信用ソケットの作成

接続要求受信用ソケットの設定

接続待ち準備

接続要求受付

送受信

切断

接続要求受信用ソケットの作成

ソケットとは、通信路とアプリケーションの繋ぎ目という概念を表現した言葉です。(電球のソケットは発光する部分と電気コードの橋渡し役をしていますが、同じようなイメージです。)
ここではまず荒削りなソケットを作成します。

接続要求受信用ソケットの設定

荒削りに作成したソケットに詳細な設定を行います。どのアドレスのどのポートにくる接続要求を受信するのか設定します。「どのアドレス」って「自分のアドレス」に決まっているではないか、と思った方がいると思います。実は「自分のアドレス」は一つとは限りません。ネットワークインターフェースカード(NIC)が二枚以上あれば、その数だけ異なるネットワークに異なるIPアドレスで参加することができます。また、そもそもNICが一枚であっても、一般的なIPアドレスの他にループバックアドレス(例、127.0.0.1など)も「自分のアドレス」とする端末があります。
このような背景を考慮して、次のようなアドレス設定ができるようになっています。

  • 割り当てられたあるIPアドレスを指定した場合
そのIPアドレスに対応したネットワークとの通信に限定。(NICが複数あり、他のネットワークとつながっていても、それらのネットワーク上の端末とは通信できない)

  • ループバックアドレスを指定した場合
自分自身だけでの通信に限定。(同じ端末で動いているクライアントとしか通信できないということ)

  • 0.0.0.0(INADDR_ANYというマクロで定義されている)を指定した場合
全ネットワーク上の端末との通信が可能。(NICが複数ある場合、それら全てに対応するネットワーク上の端末と通信が可能)

接続待ち準備

クライアントからサーバに対して接続要求が届いてから実際に接続されるまでには(人間の感覚では一瞬ですが、)時間を要します。この間にもし別のクライアントから接続要求が届いたらどうしましょう。しばらく待たせる、という方法があります。
この準備では、最大いくつの要求を待たせるか、という設定を行います。
(この最大数を超えた要求が行われたとき、クライアント側は待たされず、即刻「接続拒否」を示すエラーを受け取ることとなります。)

接続要求受付

ここまで準備してきた設定で接続要求がクライアントからやってくるのを待ちます。接続要求があった場合、そのクライアントと通信するための専用ソケットを用意してくれます。(サーバの相手となるクライアントは一つとは限りません。そのため、専用のソケットが用意されます。最初に作成したソケットはあくまで接続要求を受信するためのソケットです。)

送受信

クライアントと通信するための専用ソケットを使って、送受信します。

切断

通信をやめる際に明示的にその旨を伝えるために行います。
そうしないとメモリなどの資源が使用されたままの状態になり、無駄が発生します。無駄が積み重なると有限である資源が枯渇することになります。

これらを実際にコードで書くと以下のようになります。

#include <stdio.h>
#include <winsock2.h>

int main(int argc, char** argv)
{
  WSADATA wsaData;
  SOCKET listenSock;
  struct sockaddr_in addr;
  struct sockaddr_in client;
  int len;
  SOCKET commSock;
  char recvBuf[256];
  int recvLen;

  printf("サーバプログラムスタート\n");

  // Winsock2 DLL 初期化
  WSAStartup(MAKEWORD(2,0), &wsaData);

  printf("接続要求受信用ソケットの作成\n");
  listenSock = socket(AF_INET, SOCK_STREAM, 0);

  addr.sin_family = AF_INET;
  addr.sin_port = htons(12345);           //任意のポート番号
  addr.sin_addr.S_un.S_addr = INADDR_ANY; // 全てのアドレス
  printf("接続要求受信用ソケットの設定\n");
  bind(listenSock, (struct sockaddr *)&addr, sizeof(addr));

  printf("クライアントからの接続待ち準備\n");
  listen(listenSock, 5);

  // クライアントからの接続要求受付
  len = sizeof(client);
  printf("クライアントからの接続要求受付\n");
  commSock = accept(listenSock, (struct sockaddr *)&client, &len);

  // 送信
  printf("\"おはよう\"送信\n");
  send(commSock, "おはよう", strlen("おはよう"), 0);

  recvLen = recv(commSock, recvBuf, sizeof(recvBuf), 0);
  recvBuf[recvLen] = '\0';
  printf("\"%s\"受信\n", recvBuf);
	
  printf("切断\n");
  closesocket(commSock);
  closesocket(listenSock);

  // Winsock2 DLL 終了
  WSACleanup();

  return 0;
}

上記プログラムは、

  • 接続要求受信用ソケットを作成(socket())し、アドレスやポート番号などを設定(bind())します。
  • クライアントからの接続待ち準備(listen())を行い、クライアントからの接続受け付ける(accept())とそのクライアントに対して「おはよう」というメッセージを送信(send())します。
  • その後、クライアントからのなんらかのメッセージが来るのを待ち、受信(recv())するとそのメッセージを表示(printf())します。
  • 表示後、通信を切断(closesocket())し、プログラムは終了します。


WSAStartupとWSACleanupはWinsock2を使用するために必要です。Winsock2利用区間で呼び出すようにしてください。

さらにWinsock2を利用するためには「Winsock2を使いますよ(ライブラリをリンクしますよ)」という設定をしておく必要があります。

  1. ソリューションエクスプローラーで「server」を選択してください。この状態で、「プロジェクト」>「プロパティ」を選択してください。

  2. プロパティページが表示されます。「構成プロパティ」>「リンカー」>「入力」を選択してください。
  3. 右のフレーム内、「追加の依存ファイル」に対する値の末尾に「;ws2_32.lib」を追加し、「OK」をクリックしてください。


これでWinsock2を利用できるようになりました。


さらにMicrosoft Visual C++ Expressの高度な機能を利用するために、「ツール」>「設定」>「上級者用の設定」にします。


では、プログラムをビルドしてみましょう。

  1. 「ビルド」>「server ビルド」を選択してください。

  2. ビルドが正常に動作した場合は、
    「1 正常終了」
    となります。もし失敗したならば、もう一度各種設定、コードを確認してください。

では、プログラムを実行してみましょう。


  1. 「デバッグ」>「デバッグなしで開始」を選択してください。(「デバッグなしで開始」がないっ!という方は、先に説明した「上級者用の設定」をしてください。「デバッグなしで開始」でプログラムを動作させるのはプログラム終了時にコンソールを閉じないためです。)

  2. 下図のような結果になれば意図通り動作しています。

accept()処理でクライアントからの接続を待っている状態になっています。

さて、accept()処理が戻る、つまり、クライアントからの接続を行わなければ、処理を進めることができません。クライアントを作る必要があるのですが、ここでちょっと近道をして既存のツールを利用してクライアントを肩代わりさせる方法を紹介します。(クライアントも後で作成します。)

そのツールは「SocketDebugger」というものです。>>SocketDebuggerっ
て何?


comment

メールアドレスが公開されることはありません。

© 2024 BLuE AND PuRE