2017-11-10 6 views
1

こんにちは初心者のCプログラマから。サーバーの親プロセスが受け入れ呼び出しでスタックしました。子から親をきれいに終了させる方法

シンプルなサーバークライアントのセットアップがあります。私は1つのクライアントをサーバーに接続したいだけですが、私は他のクライアントがサーバーが占有されているというメッセージを受け取ることができます。

1つのクライアントでサーバーに接続でき、接続しようとしている他のクライアントに空きがないことを知らせることができます。私の問題は、クライアントがサーバーにシャットダウンを指示したときに発生します。子プロセスはループから抜け出し、終了することができます。しかし親は、acceptに固執しているので、パイプを使って子からメッセージを受け取ることができません。

私はkill(2)を使用して親を終了することができますが、ソケットとファイルのクローズできれいに終了しますか?

私はまた、fcntl(sock_desc, F_SETFL, fcntl(sock_desc, F_GETFL, 0) | O_NONBLOCK);を使用して受け入れ時に親が止まらないようにしようとしましたが、これにより新しい問題が発生します。

私はどういうわけか子供にaccept行をスキップし、パイプメッセージを取得してループを終了するよう親に伝えさせたいと思っています。

これはサーバーを終了するための悪い方法である場合、私はそれについて学ぶために感謝します。

簡体サーバコード:

void termination_handler (int signum) 
{ 
    if(signum == SIGTERM){ 
     //Is this where the accept call is changed? 
    } 
} 


void main(){ 
struct sigaction sa = {0}; //2b) Initialise the struct sigaction variable to all 0s beforehand. 
sa.handler = termination_handler; //2a) Set the member for handler (to the signal-handler function) 

sigaction(SIGTERM, &sa, NULL);  

pid_t pid; 
int loop = 1; 
while(loop){ 
    int sock = accept(net_sock, NULL, NULL); //After connection 
              //parent is stuck here 

    if(kill(pid,0) == -1){ 
     pid = fork(); 
    } 
    else{ 
     //Tell second client there is no room and close socket 
    } 

    //Child 
    if(pid == 0){ 
     while(loop){ 
      //Read signal in from client to end child loop and terminate child 
      //Write with pipe to parent to end parent loop and terminate parent 
      kill(getppid(), SIGTERM) // Is this how I do it? 
     } 
    } 
    //Parent 
    else{ 
     close(sock); 
     //Read with pipe from child to end loop 
     //After first connection, the parent won't get this message 
    } 
} 
+1

関連:https://stackoverflow.com/a/23685817/694576 – alk

+0

私は私の答えに 'sigaction'ケースのためのコードを追加しました。初期化されたローカルの 'pid_t pid;'で 'kill 'を呼び出すので、あなたのコードは未定義ではありません。とにかく、それ以上の質問があれば、おそらくそれを修正するのではなく、別のものにするべきです。 – PSkocik

答えて

2

OSによってファイル記述子が閉じられます。他のクリーンアップ作業(ファイルへの書き込みやファイルの削除など)がない限り、処理されていない着信信号(例:またはSIGINT)を使用した強制終了で十分です。

他のクリーンアップ作業がある場合は、親に信号ハンドラが確立されている信号(親機にはsigactionでハンドラを確立する必要があります)を信号で伝えます。それは返信コード-1errno == EINTRacceptを破り、あなたが何をする必要があれば何でもできるようになります。

volatile sig_atomic_t usr1 = 0; 
void usr1_handler(int Sig) { usr1 = 1; } 
//... 
int main() { //... 
    sigaction(SIGUSR1, &(struct sigaction){.sa_handler=usr1_handler},0); 
    //... 
    usr1 = 0; 
    sock = accept(/*... */); 
    if (-1 == sock && EINTR == errno && usr1) //was interrupted by USR1 
     /* cleanup and exit */; 
+0

ありがとうございました!あなたは両方の提案されたsigactionを持っています。私が他の人に言ったように、私はすぐに信号を使うのは快適ではない。私はそれを見るためにしばらく時間を使いますが、それがあなたの言うとおりであれば、その殺害はそのトリックを行います、私は今それを固執しています。ありがとうございました:) – tore

+0

おっと、これは役に立ちます!ありがとうございました!私はあなたの例のようにあなたのコードを追加しました。私はそれをすべて正しく動作させるために、子プロセスの中で 'kill(getppid()、SIGTERM)'を呼び出すことを正しく理解しましたか?これは、usr1_handlerを呼び出し、usr = 1を実行します。したがって、例えば 'void1'と言うと' usr1_handler(int Sig){usr1 = 1; printf( "inside usr_handler \ n"); } '子供の中で' kill() 'を呼び出すと、その出力が見えますか?これを行うと、 'accept()'が-1を返すので、親が移動できるようになります。私はあなたが見る出力を得ていない。 – tore

+0

@toreはい、killとprintfsとEINTRでは、関数が非同期シグナルで安全でないため、技術的にはsigハンドラ内でprintfを呼び出すべきではありませんが(printfはスレッドセーフでロックをとり、別のprintfロックを取っていたときに、データの破損やデッドロックが発生する可能性があります)。 'man 7 signal 'は、非同期シグナルの安全性と' EINTR'に関する情報を提供します。 – PSkocik

1

は子供が終了する前に、それは親のシグナルましょう。正しく完了した場合は、accept()は信号受信で復帰し、-1を返し、errnoEINTRに設定します。

accept()'s documentationから:戻り値正常に終了する

は、(受け入れる)は受け入れられたソケットの非負のファイル・ディスクリプタを返します。そうでなければ、-1 [...]

[...]、errnoにエラーを示すように設定されなければならない、返さなければならない

ERRORS

受け入れる()関数条失敗した場合:

[...]

[EINTR] 受け入れる()関数が有効な接続の前にキャッチされたシグナルによって中断されました到着した。

+0

ありがとうございます!私は信号を使用するのが快適ではないことを認めなければならないので、この権利を得るためには試行錯誤が必要です。構造体を作成して引数を渡す必要があるようで、他の人の言うことが真実であれば、その殺害は適切な方法で終了します。私はこれで満足しています。もう一度お時間をありがとう:) – tore

+0

ちょうど2つのこと:1. 'sigaction()'を使ってください( 'signal()'は使わないでください)。 2.ハンドラのメンバを(シグナルハンドラ関数に対して)設定します。事前に 'struct sigaction'変数をすべて' 0 'に初期化してください。 – alk

+0

もう一度!私はこれを扱う手持ちの手持ちが必要だと思う。私は私のOPを編集しました。あなたの機能を正しく追加しましたか?私はこの1つの私の頭の上に方法だと思う。 – tore

関連する問題