今回から、名前付きパイプを作ります。
パイプそのものは大して面倒ではないのですが、読み書きの待ち受けとか、終了が少し面倒くさいです。
まずは、名前付きパイプの作り方を見てみましょう。
HANDLE CreateNamedPipe( LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes );lpNameには、パイプの名前を指定します。このとき名前は「\\.\pipe\name」のような感じで指定します。「pipe」はこのまま使います。「name」は自分で好きな名前を付けます。「.」はローカルマシンを表しますが、他のマシンを指定したいときは、その名前を指定します。実際のプログラムでは「\」は「\\」で表すので"\\\\.\\pipe\\name"となるので注意してください。
dwOpenModeには、アクセス・同期・一括書き込み・セキュリティモードを指定します。 アクセスモードは、次のいずれかを指定します。
PIPE_ACCESS_DUPLEXは、パイプ内のデータをクライアント・サーバー間で両方向に流すことができます。
PIPE_ACCESS_INBOUNDは、クライアントからサーバーに限定します。
PIPE_ACCESS_OUTBOUNDは、サーバーからクライアントに限定します。
一括書き込み・同期同期モードは次の組み合わせで指定します。
FILE_FLAG_WRITE_THROUGHは、一括書き込みモードを有効にします。パイプに書き込む関数は、書き込んだデータがリモートコンピュータ上にあるパイプのバッファに格納されるまで制御を返しません。サーバー・クライアントが同一マシン上に有ったり、バイトタイプのデータでないときは意味がありません。
FILE_FLAG_OVERLAPPEDは、非同期モードを有効にします。読み取り、書き込み関数などはすぐに制御を戻します。
セキュリティモードは次の任意の組み合わせで指定します。
WRITE_DACは、呼び出し側はパイプの任意のACL(アクセスコントロールリスト)への書き込みの権利を有します。
WRITE_OWNERは、呼び出し側は、名前付パイプのオーナーへの書き込み権を有します。 ACCESS_SYSTEM_SECURITYは、呼び出し側は名前付パイプのシステムACLへの書き込み権を有します。
dwPipeModeには、パイプの種類、読み取りモード、待機モードを指定します。
PIPE_TYPE_BYTEはパイプの情報が、通常のバイナリを扱う方法で読み書きされ、バイト列として理解されます。
PIPE_TYPE_MESSAGEは1度に1文を取得する必要があるときなどに用います。
読み取りモードは次のいずれかで指定します。
PIPE_READMODE_BYTEはバイトストリームとしてデータを読みます。
PIPE_READMODE_MESSAGEはメッセージストリームとしてデータを読みます。
待機モードは次のいずれかを指定します。
PIPE_WAITは、ReadFile関数が読み取るデータが発生するまで, WriteFile関数はデータがすべて書き込まれるまで, ConnectNamedPipe関数はクライアントに接続されるまで制御を返しません。
PIPE_NOWAITは上の関数はすぐに制御を返します。
nMaxInstancesはパイプに対して作成できるインスタンスの最大数を指定します。1以上PIPE_UNLIMITED_INSTANCES以下で指定します。
nOutBufferSizeに出力バッファのバイト数を指定します。
nInBufferSizeに入力バッファのバイト数を指定します。
nDefaultTimeOutには、タイムアウト値をミリ秒単位で指定します。
lpSecurityAttributesには、SECURITY_ATTRIBUTES構造体へのポインタを指定します。
関数が成功するとサーバー端点のハンドルが返されます。失敗するとINVALID_HANDLE_VALUEが返されます。
さて、パイプを作ったらクライアントからの接続を待つ仕組みを作る必要があります。通常スレッドを作って、ここで待ちます。この章で作るプログラムはクライアントからの書き込みを待って、これを表示するだけのものです。
スレッドでは、永久ループを作って、この中でReadFile関数で読み出します。
クライアントが何も書き込んでいないのにReadFile関数を実行してもエラーとなりますね。ここでは、待機関数を使ってイベントを待機することにします。クライアント側で書き込み直前に、イベントをシグナル状態にします。
ループ内で、読み取りが終わったらすぐに、イベントを非シグナル状態にして、次の書き込みが来るまで待機状態にします。
プログラムを終了する時は、クライアント側から「end」と書き込むようにします。サーバー側は、読み取りの結果、送信してきた文字列が「end」である場合、ループを抜けて、終了処理をします。
これで、何とかサーバー側のプログラムができそうですね。では、サンプルを見てみましょう。
/* namedp.c */
#define EVENTNAME "Nekodemowakaru"
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <process.h>
DWORD WINAPI MyThreadProc(LPVOID);
HANDLE hEvent, hThread;
int main()
{
DWORD dwThreadID;
static HANDLE hPipe;
hPipe = CreateNamedPipe("\\\\.\\pipe\\mypipe",
PIPE_ACCESS_INBOUND, //オープンモード
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
PIPE_WAIT, //パイプモード
1, //パイプに対する最大インスタンス
1024,//出力バッファ
1024,//入力バッファ
0,//タイムアウト
NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
printf( "パイプ作成失敗\n");
return -1;
} else {
printf("パイプを作成しました\n");
}
hEvent = CreateEvent(NULL, //セキュリティ属性
FALSE, //手動リセット
FALSE, //初期状態
EVENTNAME);//名前
hThread = (HANDLE)_beginthreadex(NULL,
0,
(LPTHREAD_START_ROUTINE)MyThreadProc,
(LPVOID)hPipe,
0,
&dwThreadID);
if (hThread == NULL) {
printf("スレッドの作成に失敗しました\n");
CloseHandle(hPipe);
return -3;
} else {
printf("スレッドを作成しました\n");
}
WaitForSingleObject(hThread, INFINITE);
if (CloseHandle(hThread)) {
printf("スレッドのハンドルをクローズしました\n");
}
printf("プログラムを終了します\n");
printf("何かキーを打ってください\n");
_getch();
return 0;
}
main関数です。名前付きパイプを作ったら、続いてイベントを作ります。イベントは初期状態で非シグナル状態です。
続いて、スレッドを作ります。そのままでは、すぐにmain関数を抜けてプログラムが終了してしまうので、待機関数でスレッドの終了を待ちます。
DWORD WINAPI MyThreadProc(LPVOID lpParam)
{
char szBuf[1024];
HANDLE hPipe;
DWORD dwRead;
BOOL bRead = FALSE;
printf("クライアントの送信を待っています...\n");
hPipe = (HANDLE)lpParam;
while (1) {
WaitForSingleObject(hEvent, INFINITE);
memset(szBuf, '\0', sizeof(szBuf));
bRead = ReadFile(hPipe, szBuf, sizeof(szBuf), &dwRead, NULL);
ResetEvent(hEvent);
if (bRead == FALSE) {
MessageBox(NULL, "読み出し失敗", "Error", MB_OK);
break;
}
printf("%s\n", szBuf);
if (strcmp(szBuf, "end") == 0) {
break;
}
}
printf("ループを抜けました\n");
SetEvent(hEvent);
if (CloseHandle(hPipe)) {
printf("hPipeをクローズしました\n");
printf("スレッドを抜けます\n");
}
return 0;
}
スレッド関数です。無限ループにはいったら、WaitForSingleObject関数で待機します。
クライアント側で書き込みがあったら、イベントをシグナル状態にするのでパイプから
読み出しを行います。読み出したらすぐに、イベントを非シグナル状態にします。もし、送られてきた文字列が「end」なら無限ループを抜けます。
ループを抜けたら、イベントをシグナル状態にします。(クライアント側の都合です)
次章では、クライアント側の作成を行います。
Update Feb/21/2006 By Y.Kumei