3

私はtcpチャットプログラムを持っています:server.cそしてclient.c

サーバーはwhile(1)ループ状態にあり、selectそのソケットに接続したいクライアントを検出するために使用します。次に、受け入れられたクライアント用に新しいスレッドが作成され、そのソケット記述子がスレッドの引数として指定されます。pthread_create (&thread,NULL, do_something, (void *) &socket_descriptor);

クライアントからメッセージを受信する場合、サーバーはこのメッセージを接続されているすべてのクライアントに送信する必要があります。(これはまだ実装されていません)。

今、私はこれをどのように行うのか疑問に思っています。受け入れられた各接続をスレッドに含める必要があります。

select内部も使うことを考えてdo_somethingいました。selectデータがソケット記述子に着信しているかどうかを検出しますか?それとも別の方法でやりますか?

編集:追加されたコード私のコード:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "tcp_comm.h"
#include <sys/time.h>
#include <sys/types.h>

#define BUFSIZE 1024
#define PORT 1234

void *do_something(void *a);

int main (void){
    Socket server = tcp_passive_open( PORT );
    MySocket *s = (MySocket *)server;
    printf("Server socked_id (main): %i", s->sd);

    pthread_t thread;

    fd_set active_socketDescriptors,read_socketDescriptors;

    FD_ZERO(&active_socketDescriptors);         
    FD_SET(s->sd,&active_socketDescriptors);    

    while (1){
        read_socketDescriptors = active_socketDescriptors;
        if (select (FD_SETSIZE, &read_socketDescriptors, NULL, NULL, NULL) < 0){
            perror ("select"); 
            exit (EXIT_FAILURE);
        }

        int i;
        for (i = 0; i < FD_SETSIZE; ++i){
            if (FD_ISSET (i, &read_socketDescriptors)){
                if (i == s->sd){
                    Socket client = tcp_wait_for_connection( server ); 
                    pthread_create (&thread,NULL, do_something, (void *)client); 
                    FD_SET (s->sd, &active_socketDescriptors);          
                } else {
                }
            }
        }
    }

    tcp_close( server );
    return 0;

}
void *do_something(void *client){
    unsigned char input[BUFFER_SIZE]; 
    pthread_detach(pthread_self());

    MySocket *s = (MySocket *)client;
    printf("Client socked_id (thread): %i", s->sd);
    int j;
    while (1){
        int nbytes = tcp_receive(client, input, BUFSIZE );
        if (nbytes <= 0){
                if (nbytes ==0){
                    /* connection closed by client*/    
                    printf("Client closed connection");         
                } else {
                    /* other error*/
                    perror("tcp_receive");              
                }
                tcp_close(&client);
                /*remove the socket descriptor from set in the main BRAINSTORM ABOUT THIS */
        } else {
            /*data incoming */
            printf("\nMessage from client: %s",input);
        }
    }
    return 0;
}

編集2:問題の再定式化私はスレッドを使用する必要があります(システムのためではなく、Linuxのためです)が、割り当てではクライアントごとにスレッドを持つことが必須であるためです。

私が特に抱えている問題は、メインスレッドだけがソケット記述子を含むセットにアクセスできるため、各スレッドで受信したデータを各クライアントからすべてのクライアントに送信できることです。

edit3:各スレッドに追加する必要があるものですが、s.threadとs.mainが異なる場所にあり、スレッドがメインのセットを認識していないため、追加できません。

for (j=0; j<=FD_SETSIZE;j++){
    if(FD_ISSET(j,&active_socketDescriptors)){
        if (j != s.thead && j!=s.main){
            tcp_send(j, (void*)input,nbytes);
        }       
    }   
}

編集4:私はそれをこのように解決しました:私はそこにソケット記述子を持つ接続されたクライアントのリストを置く動的配列リストを持っています。サーバーのスレッド内で(何かを実行します)、入力を取得するまで受信をブロックします。その後、この入力は、ループするリストのソケット記述子を使用して、接続されているすべてのクライアントに送信されます。クライアントの内部には、スレッドのリスニングとスレッドの送信があります。

4

3 に答える 3

3

クライアント接続ソケットが非ブロッキングである場合、たとえばselect、ソケットがデータを受信するのを待つために使用することが可能な方法です。ただし、すでにスレッドに接続されたソケットがあるため、それらをブロックしたままにして、readそれらを呼び出すだけで済みます。toの呼び出しはread、データを受信するまでブロックされます。データを受信すると、他のスレッドに拡散できます。

編集

要件をよりよく理解した後、おそらくソケットを非ブロッキングにselectし、タイムアウトの短いループを使用する必要があります。selectタイムアウトする(つまり、戻る)とき0は、送信するデータがあるかどうかを確認します。ある場合は、データを送信して、select通話に戻ります。

于 2012-05-30T08:54:20.687 に答える
1

説明があれば、アプリケーションのアーキテクチャを再考する価値があるかもしれません。(これがシステムの制限によって指示されていない限り)。これについてもう少し説明させてください...

あなたの説明によると、私があなたを正しく理解していれば、クライアントがサーバーに接続した後、クライアント(クライアント)が送信するメッセージは(サーバーによって)他のすべてのクライアントに中継されます。したがって、新しいスレッドを作成するのではなく、新しく接続されたソケットをselectのFDSETに追加するだけではどうでしょうか。その後、メッセージが届いたら、他の人に簡単に中継できます。

1台のサーバーに多数のクライアントが必要な場合は、pollシステムコールがシステムで使用可能かどうかを確認する必要があります(selectと同じですが、より多くのクライアントの監視をサポートします)。優れたポーリング/選択バージョンは、スレッドバージョンよりもパフォーマンスが優れているはずです。

スレッドバージョンを本当に続行したい場合は、これがあなたがやろうとしていることを達成するための1つの方法です。受け入れられたクライアントごとにスレッドを作成するときは、サーバースレッドに戻るパイプも作成し(これをサーバーの選択/ポーリングセットに追加します)、それをクライアントスレッドに渡します。そのため、サーバースレッドは新しい接続を受信するだけでなく、メッセージも中継します。

絶対に別々のスレッドで各クライアントを処理する必要があると言いましたが、リアルタイムオペレーティングシステムを使用していない限り、実行する必要のあるスレッドのコンテキストスイッチ/同期がすぐにの多重化オーバーヘッドを支配することに気付くでしょう。私が提案した最初の解決策。(しかし、あなたはOSについて言及していなかったので、私は推測しています!)

于 2012-05-30T09:42:27.017 に答える
0

これはあなたのデザインに関連しています。

接続されているクライアントごとに1つまたは2つの機能のみを実行する必要がある場合は、サーバーの実装に1つのスレッドのみを使用することをお勧めします。

接続されているクライアントごとに多くの機能を実行する必要がある場合は、マルチスレッド設計で問題ありません。ただし、あなたが尋ねた質問は、受信スレッドから他のすべてのスレッドにデータをどのように渡すかということです。私からの提案された答えはエーテルです:

a)メッセージキューを使用してスレッド間データを渡します。各スレッドには1つのメッセージキューがあり、各スレッドは独自のソケットとこのメッセージキューをリッスンします。ソケットからデータを受信するとき、スレッドはデータを他のすべてのメッセージキューに送信します

b)単一のグローバルバッファを使用します。ソケットからの受信データがある場合は、このデータをこのグローバルバッファに入れ、このデータの出所を示すタグをこのデータに追加します。

私の2セント。

于 2012-05-30T09:50:56.553 に答える