2017-05-28 6 views
0

学校プロジェクトとして、私はIRCサーバーをコード化する必要がありますが、問題が残っています。 私がやろうとしているのは、ブロッキングせずにクライアントのコマンドを受信して​​実行することです(私はサービスするクライアントが多いため)。Cソケット: n分離されたコマンドを読むための非ブロッキング方法

編集:非ブロッキングソケットとフォーク()の使用は、このプロジェクトのコマンドについて

のために禁止されている:彼らはある

  1. "\ rはn個\" 彼らは512です
  2. を分離しましたchar max

私の最初の試みは、getlineでループすることでした。 (彼らは、次のクライアントに渡すのではなく、読むことをより多くのを注目しているときのgetlineブロックとして)それは私がこれのようなループからのgetlineを削除した場合、それはすべてのために働く1つのクライアントだけのために

bool  recv_cmd(t_hdl *hdl)            
{                        
    char   *raw;                   
    size_t  len;                   
    FILE   *input_stream;                 
    ssize_t  nread;                   

    len = 0;                      
    raw = NULL;                     
    if ((input_stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)        
    return (false);                   
    while ((nread = getline(&raw, &len, input_stream)) > 0)          
    {                       
     printf("%lu\n", nread);                 
     parse_cmd(hdl, raw);                  
     exec_cmd(hdl);                                     
    }                       
    fclose(input_stream);                  
    return (true);                    
} 

を完全に働いたが、クライアントだけが、クライアントからの最初のコマンドが実行される(例えば、クライアント送信する場合は「Command1を\ rを\ ncommand2 \ rを\ n」は、唯一のCommand1が実行される)

bool  recv_cmd(t_hdl *hdl)            
{                        
    char   *raw;                   
    size_t  len;                   
    FILE   *input_stream;                 

    len = 0;                      
    raw = NULL;                     
    if ((input_stream = fdopen(dup(hdl->sender->fd), "r")) == NULL)        
    return (false);                   
    if (getline(&raw, &len, input_stream) != -1)             
    {                       
     parse_cmd(hdl, raw);                  
     exec_cmd(hdl);                   
     //free(raw                    
    }                       
    fclose(input_stream);                  
    return (true);                    
}   

私もFCLOSEを削除しようとしました()を使用して、command1を読み込んだときにcommand2がストリームバッファに残るようにしましたが、どちらも機能しませんでした。

は、送受信されているさまざまなコマンドと応答を安全に最適化するために循環バッファを使用します。」とも言います。

どうすればよいですか?この場合、getline上で循環バッファを使用する利点は何ですか?

+1

*ブロッキングではないソケットを使用し、ポーリングを使用して、 '選択'? –

+1

あなたのクライアント/サーバーI/O **は、低レベルの読み書き操作(つまり、あなたのプロジェクトの要件)で**を非ブロッキングにするか、複数の同時クライアントを処理するだけで済むか高速で非連続的な方法でサーバーからの接続? – DevNull

+0

@Someprogrammerdude私は既に選択しているが、非ブロックソケットはこのプロジェクトには使用できません:/ –

答えて

0

getline()を使用していたので、私はあなたがPOSIX.1の機能に頼っていると仮定しています。この場合、接続されているすべてのクライアントからのメッセージを受信するための専用スレッドを使用することをお勧めします。

だけではなく、クライアントごとの動的バッファから追加データを読み込み、私はチェーンに受信メッセージを置く:

#define MAX_INCOMING_LEN 512 

struct incoming_message { 
    struct incoming_message *next; 
    size_t     len; 
    char      data[MAX_INCOMING_LEN]; 
} 

があるので、クライアントの構造は、少なくともMAX_INCOMING_LEN文字(の一時的なバッファを必要としますストリームソケットからのrecv()またはread()が完全なメッセージを提供すること、または単一のメッセージを提供することは保証されません)。

struct client { 
    int      socketfd; 
    char      received[MAX_INCOMING_LEN]; 
    size_t     received_len; 

    pthread_mutex_t   incoming_lock; 
    struct incoming_message *incoming_next; 
    struct incoming_message *incoming_last; 
}; 

新しいメッセージを受け取る関数が擬似コードでは、このようにリストに追加されます:

Construct and fill in struct incoming_message *msg 
Lock incoming_lock mutex 
Set msg->next = NULL 
If incoming_last != NULL: 
    Set incoming_last->next = msg 
    Set incoming_last = msg 
Else 
    Set incoming_next = msg 
    Set incoming_last = msg 
End If 
Unlock incoming_lock mutex 
を別のスレッドがメッセージを読んでいる場合は、また、同時アクセスからのメッセージ・チェーンを保護するためにロックする必要があります

2つのポインタincoming_nextincoming_lastを使用すると、追加するときにリストをスキャンする必要はありません。関数は、擬似コードで使用すると、通常にまったく同じメッセージを送信するため、送信メッセージのために、私は、完全に異なる構造を使用したいという

Function next_message(struct client *c) 
{ 
    Lock c->incoming_lock mutex 
    If c->incoming_next != NULL: 
     struct incoming_message *msg = c->incoming_next; 
     If msg->next != NULL 
      Set incoming_next = msg->next 
      Set msg->next = NULL 
     Else: 
      Set incoming_next = NULL 
      Set incoming_last = NULL 
     End If 
     Unlock c->incoming_lock mutex 
     Return msg 
    Else: 
     Unlock c->incoming_lock mutex 
     Return NULL 
    End If 
} 

ノートのようなものがあり、クライアントc与えられ、次の着信メッセージをつかむために多くのクライアント。これには少なくとも2つの全く異なるアプローチがありますが、OPはこれらについて尋ねなかったので、私はそれらについての反省を省略します。

クライアントごとにreceived[]バッファにアクセスするのは、着信データワーカーまたはソケットリーダースレッドだけなので、ロックは必要ありません。

あなたは、次のグローバル変数を持っていると仮定しましょう:擬似コードで

static pthread_mutex_t received_lock = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t received_more = PTHREAD_COND_INITIALIZER; 
static long    received_gen = 0L; 

、ソケットリーダースレッドがループで次の作業を行います。received_lock

Use select() or poll() to find out which clients' sockets have unread data 
Lock received_lock mutex 
Set have_received = 0 
For each client whose socket has unread data: 
    Try receiving as much as is free in received[] buffer 
    If new data received: 
     Increment received_len by the received amount 
     Increment have_received by 1 
     If a separator exists in received[0..received_len-1]: 
      Let N be the offset of the character following the separator 
      Grab or allocate a new incoming_message structure 
      Copy the first N chars of received[] to the new structure 
      Lock the incoming_lock mutex 
      Prepend the structure to the singly-linked list 
      Unlock the incoming_lock mutex 
      If N < received_len: 
       memmove(received, received + N, received_len - N) 
       received_len -= N 
      Else: 
       received_len = 0 
      End If 
     End If 
    End If 
End If 
If have_received > 0: 
    Increment received_gen by 1 
    Signal on received_more condition variable 
End If 
Unlock received_lock mutex 

目的は、received_waitreceived_genは、新しいメッセージが入力されていないときにビジーループを回避することです。

あなたのメインスレッド各着信メッセージを処理するためのEADは、このようなループに何かの本体と、ループを持っています:

Lock received_lock mutex 
before_gen = received_gen 
Unlock received_lock mutex 

Set msg_count = 0 
For each client: 
    Lock client->incoming_lock 
    If the list is not empty: 
     Increment msg_count by 1 
     Grab the last message in the list 
     Unlock client->incoming_lock 

     Process the message 

    Else: 
     Unlock client->incoming_lock 
    End If 
End For 

If msg_count == 0: 
    Lock received_lock mutex 
    after_gen = received_gen 
    If after_gen == before_gen: 
     pthread_cond_wait(received_more, received_lock) 
    End if 
    Unlock received_lock mutex 
End If 

ブロックは、新しいメッセージの送受信いるので、我々は、時間の任意の長さのためreceived_lockを保持する必要はありません。代わりに、received_genを世代カウンタとして使用します。実行する作業がない場合は、世代カウンタが変更されているかどうかを確認します。それがあれば、もっと多くの作業が必要になるかもしれないので、メインループの次の繰り返しを続けます。さもなければ、我々はまだmutexを保持していることに注意して、条件変数の信号を待ちます。

+0

これは本当にうれしいです。スレッドの使用が許可されているかどうか、これを実装する;) –

関連する問題