AI対戦サーバープログラム作成のヒント/強制終了
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
開始行:
[[AI対戦サーバープログラム作成のヒント]]
*その4 複数回通信と強制終了 [#uf1ce53f]
今までは、親子間で1回だけメッセージを送受信するだけでし...
今回は、それを何回でもやり取りをできるようにします。~
また、子プロセスの無限ループ対策に、子プロセスを強制終了...
***作るプログラムの条件 [#sc944058]
前回まで、親プロセスでは、子プロセスの終了を待ってからメ...
ややこしい話ですので詳細は省きますが、子プロセスを終了さ...
これでは複数回の通信を可能にするために子プロセスを生存さ...
具体的には、以下のプログラムを作ります。~
-親プロセスから子プロセスの標準入力にメッセージ(整数)を...
-子プロセスは整数を受け取って、その10倍した値を親プロセス...
-一定回これを繰り返したら、親は子を強制終了する
やっぱり言っている意味がわからないでしょうから、まずは先...
**子プロセスを作成する [#h6649a22]
まず、子プロセスとなるプログラムを作ります。~
#include <stdio.h>
#include <stdlib.h>
int input();
int main() {
int l=0;
fprintf(stderr, "child start.\n");
while(l<10) {
int d = input();
printf("%d\n", d*10);
fflush(stdout);
l++;
}
fprintf(stderr, "child end.\n");
return 0;
}
int input() {
char buf[16];
int d;
if(fgets(buf, 16-1, stdin) == NULL){
fprintf(stderr, "C:fgets return NULL\n");
exit(1);
}
d = atoi(buf);
return d;
}
今回は、入力部分をinput関数に分割しました。~
標準入力から一度fgets関数でchar配列に受け取り、atoi関数で...
また、fgets関数は失敗するとNULLを返すので、そこで強制終了...
このプログラムで一番大事なのは、main関数中の''fflush(stdo...
これを行わないと、出力が正しく行われず、このあと説明する...
これをコンパイルして出来た実行ファイルを「child.exe」とし...
**親プロセスを作成する [#x5cef40d]
親プロセスとなるプログラムを作成していきます。~
ですが、[[その3>AI対戦サーバープログラム作成のヒント/プ...
***自作ライブラリpipelib [#t359fd14]
今後のプログラム作成を楽にするために、一部の関数をライブ...
記事執筆が終わったら、まとめてUPする予定です。~
このライブラリは、「pipelib.h」と「pipelib.c」の2つのフ...
中身を見ていきましょう。まずはpipelib.hからです。~
***pipelib.h [#u4125be5]
#define R 0
#define W 1
int createPipe(HANDLE *readPipe, HANDLE *writePipe, BOOL...
int easyCreateProcess(LPTSTR commandLine, STARTUPINFO *s...
これだけです。その3で作った定数とcreatePipe関数、そして...
この関数の定義をpipelib.cの方でしています。~
***pipelib.c [#i2531355]
#include <windows.h>
#include <stdio.h>
int createPipe(HANDLE *readPipe, HANDLE *writePipe, BOOL...
{
HANDLE readTemp, writeTemp;
if(!CreatePipe(&readTemp, &writeTemp, NULL, 0)) {
fprintf(stderr, "CreatePipe\n");
return 0;
}
if(!DuplicateHandle(
GetCurrentProcess(), readTemp,
GetCurrentProcess(), readPipe,
0, readInherit, DUPLICATE_SAME_ACCESS)) {
fprintf(stderr, "DuplicacteHandle\n");
if(!CloseHandle(readTemp))
fprintf(stderr, "CloseHandle(readTemp)\n");
return 0;
}
if(!CloseHandle(readTemp)) {
fprintf(stderr, "CloseHandle(readTemp)\n");
return 0;
}
if(!DuplicateHandle(
GetCurrentProcess(), writeTemp,
GetCurrentProcess(), writePipe,
0, writeInherit, DUPLICATE_SAME_ACCESS)) {
fprintf(stderr, "DuplicacteHandle\n");
if(!CloseHandle(writeTemp))
fprintf(stderr, "CloseHandle(writeTemp)\n");
return 0;
}
if(!CloseHandle(writeTemp)) {
fprintf(stderr, "CloseHandle(writeTemp)\n");
return 0;
}
return 1;
}
int easyCreateProcess(LPTSTR commandLine, STARTUPINFO *s...
{
BOOL bInheritHandles = TRUE;
DWORD creationFlags = 0;
return CreateProcess(
NULL,
commandLine,
NULL, //プロセスのセキュリティー記述子
NULL, //スレッドのセキュリティー記述子
bInheritHandles,
creationFlags,
NULL, //環境変数は引き継ぐ
NULL, //カレントディレクトリーは同じ
si,
pi);
}
その3から、createPipeの返り値を変更しました。~
その3では、成功で0、失敗で0以外を返していましたが、元のC...
その3のサンプルプログラムと、結果の真偽値が逆になります。~
easyCreateHandle関数は、その名の通り、CreateHandle関数を...
プロセス名と2つの構造体だけを指定すればハンドルが作成で...
返り値はCreateHandle関数と同じです。~
***main関数 [#vfaae437]
これらのpipelibを使って、親プログラムを実装します。~
例によってまたmain関数からです。~
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "pipelib.h"
int execute();
int main(void) {
int r;
printf("parent start.\n");
r = execute();
printf("parent end.\n");
return r;
}
pipelib.hやstring.hをインクルードしています。~
string.hは、execute関数内でstrlen関数を使用しているからで...
***execute関数 [#f38c397c]
execute関数を確認していきます。~
やはりある程度は長いので、前後半に分けて見ていくことにし...
int execute() {
HANDLE stdoutPipe[2];
HANDLE stdinPipe[2];
HANDLE childProcess;
STARTUPINFO si = {};
PROCESS_INFORMATION pi = {};
LPTSTR commandLine = TEXT("child.exe");
int loop = 0;
int ans = 0;
char str[16];
char buf[128];
DWORD numberOfBytesWritten;
DWORD numberOfBytesRead;
if(!createPipe(&stdoutPipe[R], &stdoutPipe[W], FALSE, ...
fprintf(stderr, "createPipe(stdoutPipe)\n");
return -1;
}
if(!createPipe(&stdinPipe[R], &stdinPipe[W], TRUE, FAL...
fprintf(stderr, "createPipe(stdinPipe)\n");
return -1;
}
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = stdinPipe[R];
si.hStdOutput = stdoutPipe[W];
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(si.hStdError == INVALID_HANDLE_VALUE) {
fprintf(stderr, "GetStdHandle(STD_ERROR_HANDLE)");
return -1;
}
if(!easyCreateProcess(commandLine, &si, &pi)) {
fprintf(stderr, "easyCreateProcess\n");
return -1;
}
childProcess = pi.hProcess;
if(!CloseHandle(pi.hThread))
fprintf(stderr, "CloseHandle(pi.hThread)\n");
if(!CloseHandle(stdoutPipe[W]))
fprintf(stderr, "CloseHandle(stdoutPipe[W])\n");
stdoutPipe[W] = NULL;
if(!CloseHandle(stdinPipe[R]))
fprintf(stderr, "CloseHandle(stdoutPipe[W])\n");
stdoutPipe[W] = NULL;
この部分は通信の前準備の部分です。~
今までも同じことをずっとしてきているので、特に言うことも...
その3と比べて、createPipe関数の成功判定に「!」が付いて...
一応、していることを箇条書きすると、
-必要な変数等の用意
-パイプの生成
-STARTUPINFOの設定
-子プロセス(chidl.exe)の起動
-親が使わないパイプのクローズ
となります。~
続いて、後半部分を見ていきます。~
while(loop<5) {
int d = (loop+3)*2;
sprintf(str, "%d\n", d);
if(!WriteFile(stdinPipe[W], str, strlen(str), &numbe...
fprintf(stderr, "WriteFile\n");
return -1;
}
printf("\nP:send child\n");
ans = 0;
if(!ReadFile(stdoutPipe[R], buf, sizeof(buf)-1, &num...
if (GetLastError() == ERROR_BROKEN_PIPE) {
printf("broken pipe.\n");
break;
}else
fprintf(stderr, "ReadFile\n");
}
buf[numberOfBytesRead] = '\0';
printf("\nP:%s\n", buf);
loop++;
}
if(!TerminateProcess(childProcess, 0)) {
fprintf(stderr, "TerminateProcess(chileProcess)\n");
return -1;
}
if(!CloseHandle(stdoutPipe[R])) {
fprintf(stderr, "CloseHandle(stdoutPipe[R])\n");
return -1;
}
stdoutPipe[R] = NULL;
if(!CloseHandle(stdinPipe[W])) {
fprintf(stderr, "CloseHandle(stdinPipe[W])\n");
return -1;
}
stdinPipe[W] = NULL;
return 0;
}
whileループの中が通信を行っている本体部分です。このループ...
このループでは次のような処理を行っています。~
+ループ回数で決まる値をWriteFile関数で子プロセスに渡す
+ReadFile関数で子プロセスから値が返ってくるまで待つ
+送られてきたら、返ってきた値を出力して1に戻る
これを5回繰り返したらループを抜けます。~
ループを抜けたら、TerminateProcess関数で、子プロセスを強...
子プロセスを終わらせるにはこの関数を使います。~
最後に、使用したパイプをクローズして終了です。~
**実行結果 [#laa9b794]
最後にこのプログラムの実行結果を確認しましょう。
2つの実行ファイルを同じフォルダに入れて親側のプログラムを...
parent start.
P:send child 6
child start.
P:recieve 60
P:send child 8
P:recieve 80
P:send child 10
P:recieve 100
P:send child 12
P:recieve 120
P:send child 14
P:recieve 140
parent end.
ちゃんと親子間通信で、やりたかったこと(子は親からもらっ...
また、子プロセスが強制終了されているので、「child end.」...
**補足 [#id3ba9a8]
**次回 [#x6703464]
次回は、子プロセスからの応答の調べ方と、受信文字列の操作...
前回:[[その2 パイプを使ってみる>AI対戦サーバープログラ...
次回:[[その5 タイムアウトと文字列区切り]](予定)
*コメント [#pec0c234]
- 実は子の出力が出てこない不具合の対処に丸1日かかりました...
#comment
終了行:
[[AI対戦サーバープログラム作成のヒント]]
*その4 複数回通信と強制終了 [#uf1ce53f]
今までは、親子間で1回だけメッセージを送受信するだけでし...
今回は、それを何回でもやり取りをできるようにします。~
また、子プロセスの無限ループ対策に、子プロセスを強制終了...
***作るプログラムの条件 [#sc944058]
前回まで、親プロセスでは、子プロセスの終了を待ってからメ...
ややこしい話ですので詳細は省きますが、子プロセスを終了さ...
これでは複数回の通信を可能にするために子プロセスを生存さ...
具体的には、以下のプログラムを作ります。~
-親プロセスから子プロセスの標準入力にメッセージ(整数)を...
-子プロセスは整数を受け取って、その10倍した値を親プロセス...
-一定回これを繰り返したら、親は子を強制終了する
やっぱり言っている意味がわからないでしょうから、まずは先...
**子プロセスを作成する [#h6649a22]
まず、子プロセスとなるプログラムを作ります。~
#include <stdio.h>
#include <stdlib.h>
int input();
int main() {
int l=0;
fprintf(stderr, "child start.\n");
while(l<10) {
int d = input();
printf("%d\n", d*10);
fflush(stdout);
l++;
}
fprintf(stderr, "child end.\n");
return 0;
}
int input() {
char buf[16];
int d;
if(fgets(buf, 16-1, stdin) == NULL){
fprintf(stderr, "C:fgets return NULL\n");
exit(1);
}
d = atoi(buf);
return d;
}
今回は、入力部分をinput関数に分割しました。~
標準入力から一度fgets関数でchar配列に受け取り、atoi関数で...
また、fgets関数は失敗するとNULLを返すので、そこで強制終了...
このプログラムで一番大事なのは、main関数中の''fflush(stdo...
これを行わないと、出力が正しく行われず、このあと説明する...
これをコンパイルして出来た実行ファイルを「child.exe」とし...
**親プロセスを作成する [#x5cef40d]
親プロセスとなるプログラムを作成していきます。~
ですが、[[その3>AI対戦サーバープログラム作成のヒント/プ...
***自作ライブラリpipelib [#t359fd14]
今後のプログラム作成を楽にするために、一部の関数をライブ...
記事執筆が終わったら、まとめてUPする予定です。~
このライブラリは、「pipelib.h」と「pipelib.c」の2つのフ...
中身を見ていきましょう。まずはpipelib.hからです。~
***pipelib.h [#u4125be5]
#define R 0
#define W 1
int createPipe(HANDLE *readPipe, HANDLE *writePipe, BOOL...
int easyCreateProcess(LPTSTR commandLine, STARTUPINFO *s...
これだけです。その3で作った定数とcreatePipe関数、そして...
この関数の定義をpipelib.cの方でしています。~
***pipelib.c [#i2531355]
#include <windows.h>
#include <stdio.h>
int createPipe(HANDLE *readPipe, HANDLE *writePipe, BOOL...
{
HANDLE readTemp, writeTemp;
if(!CreatePipe(&readTemp, &writeTemp, NULL, 0)) {
fprintf(stderr, "CreatePipe\n");
return 0;
}
if(!DuplicateHandle(
GetCurrentProcess(), readTemp,
GetCurrentProcess(), readPipe,
0, readInherit, DUPLICATE_SAME_ACCESS)) {
fprintf(stderr, "DuplicacteHandle\n");
if(!CloseHandle(readTemp))
fprintf(stderr, "CloseHandle(readTemp)\n");
return 0;
}
if(!CloseHandle(readTemp)) {
fprintf(stderr, "CloseHandle(readTemp)\n");
return 0;
}
if(!DuplicateHandle(
GetCurrentProcess(), writeTemp,
GetCurrentProcess(), writePipe,
0, writeInherit, DUPLICATE_SAME_ACCESS)) {
fprintf(stderr, "DuplicacteHandle\n");
if(!CloseHandle(writeTemp))
fprintf(stderr, "CloseHandle(writeTemp)\n");
return 0;
}
if(!CloseHandle(writeTemp)) {
fprintf(stderr, "CloseHandle(writeTemp)\n");
return 0;
}
return 1;
}
int easyCreateProcess(LPTSTR commandLine, STARTUPINFO *s...
{
BOOL bInheritHandles = TRUE;
DWORD creationFlags = 0;
return CreateProcess(
NULL,
commandLine,
NULL, //プロセスのセキュリティー記述子
NULL, //スレッドのセキュリティー記述子
bInheritHandles,
creationFlags,
NULL, //環境変数は引き継ぐ
NULL, //カレントディレクトリーは同じ
si,
pi);
}
その3から、createPipeの返り値を変更しました。~
その3では、成功で0、失敗で0以外を返していましたが、元のC...
その3のサンプルプログラムと、結果の真偽値が逆になります。~
easyCreateHandle関数は、その名の通り、CreateHandle関数を...
プロセス名と2つの構造体だけを指定すればハンドルが作成で...
返り値はCreateHandle関数と同じです。~
***main関数 [#vfaae437]
これらのpipelibを使って、親プログラムを実装します。~
例によってまたmain関数からです。~
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "pipelib.h"
int execute();
int main(void) {
int r;
printf("parent start.\n");
r = execute();
printf("parent end.\n");
return r;
}
pipelib.hやstring.hをインクルードしています。~
string.hは、execute関数内でstrlen関数を使用しているからで...
***execute関数 [#f38c397c]
execute関数を確認していきます。~
やはりある程度は長いので、前後半に分けて見ていくことにし...
int execute() {
HANDLE stdoutPipe[2];
HANDLE stdinPipe[2];
HANDLE childProcess;
STARTUPINFO si = {};
PROCESS_INFORMATION pi = {};
LPTSTR commandLine = TEXT("child.exe");
int loop = 0;
int ans = 0;
char str[16];
char buf[128];
DWORD numberOfBytesWritten;
DWORD numberOfBytesRead;
if(!createPipe(&stdoutPipe[R], &stdoutPipe[W], FALSE, ...
fprintf(stderr, "createPipe(stdoutPipe)\n");
return -1;
}
if(!createPipe(&stdinPipe[R], &stdinPipe[W], TRUE, FAL...
fprintf(stderr, "createPipe(stdinPipe)\n");
return -1;
}
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = stdinPipe[R];
si.hStdOutput = stdoutPipe[W];
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(si.hStdError == INVALID_HANDLE_VALUE) {
fprintf(stderr, "GetStdHandle(STD_ERROR_HANDLE)");
return -1;
}
if(!easyCreateProcess(commandLine, &si, &pi)) {
fprintf(stderr, "easyCreateProcess\n");
return -1;
}
childProcess = pi.hProcess;
if(!CloseHandle(pi.hThread))
fprintf(stderr, "CloseHandle(pi.hThread)\n");
if(!CloseHandle(stdoutPipe[W]))
fprintf(stderr, "CloseHandle(stdoutPipe[W])\n");
stdoutPipe[W] = NULL;
if(!CloseHandle(stdinPipe[R]))
fprintf(stderr, "CloseHandle(stdoutPipe[W])\n");
stdoutPipe[W] = NULL;
この部分は通信の前準備の部分です。~
今までも同じことをずっとしてきているので、特に言うことも...
その3と比べて、createPipe関数の成功判定に「!」が付いて...
一応、していることを箇条書きすると、
-必要な変数等の用意
-パイプの生成
-STARTUPINFOの設定
-子プロセス(chidl.exe)の起動
-親が使わないパイプのクローズ
となります。~
続いて、後半部分を見ていきます。~
while(loop<5) {
int d = (loop+3)*2;
sprintf(str, "%d\n", d);
if(!WriteFile(stdinPipe[W], str, strlen(str), &numbe...
fprintf(stderr, "WriteFile\n");
return -1;
}
printf("\nP:send child\n");
ans = 0;
if(!ReadFile(stdoutPipe[R], buf, sizeof(buf)-1, &num...
if (GetLastError() == ERROR_BROKEN_PIPE) {
printf("broken pipe.\n");
break;
}else
fprintf(stderr, "ReadFile\n");
}
buf[numberOfBytesRead] = '\0';
printf("\nP:%s\n", buf);
loop++;
}
if(!TerminateProcess(childProcess, 0)) {
fprintf(stderr, "TerminateProcess(chileProcess)\n");
return -1;
}
if(!CloseHandle(stdoutPipe[R])) {
fprintf(stderr, "CloseHandle(stdoutPipe[R])\n");
return -1;
}
stdoutPipe[R] = NULL;
if(!CloseHandle(stdinPipe[W])) {
fprintf(stderr, "CloseHandle(stdinPipe[W])\n");
return -1;
}
stdinPipe[W] = NULL;
return 0;
}
whileループの中が通信を行っている本体部分です。このループ...
このループでは次のような処理を行っています。~
+ループ回数で決まる値をWriteFile関数で子プロセスに渡す
+ReadFile関数で子プロセスから値が返ってくるまで待つ
+送られてきたら、返ってきた値を出力して1に戻る
これを5回繰り返したらループを抜けます。~
ループを抜けたら、TerminateProcess関数で、子プロセスを強...
子プロセスを終わらせるにはこの関数を使います。~
最後に、使用したパイプをクローズして終了です。~
**実行結果 [#laa9b794]
最後にこのプログラムの実行結果を確認しましょう。
2つの実行ファイルを同じフォルダに入れて親側のプログラムを...
parent start.
P:send child 6
child start.
P:recieve 60
P:send child 8
P:recieve 80
P:send child 10
P:recieve 100
P:send child 12
P:recieve 120
P:send child 14
P:recieve 140
parent end.
ちゃんと親子間通信で、やりたかったこと(子は親からもらっ...
また、子プロセスが強制終了されているので、「child end.」...
**補足 [#id3ba9a8]
**次回 [#x6703464]
次回は、子プロセスからの応答の調べ方と、受信文字列の操作...
前回:[[その2 パイプを使ってみる>AI対戦サーバープログラ...
次回:[[その5 タイムアウトと文字列区切り]](予定)
*コメント [#pec0c234]
- 実は子の出力が出てこない不具合の対処に丸1日かかりました...
#comment
ページ名: