2011-12-18 6 views
0

チャットサーバーとクライアントで問題が発生した場合の解決方法について考えています。C++マルチクライアント/サーバーチャット

クライアントはユーザー名を要求し、次に[Y/N]という回答を持つユーザーに接続要求を求めます。

yesを押すと、クライアントが別のスレッドに入る必要があるときにクライアントがサーバーに接続する必要があります(複数のクライアントを処理するために問題があります。 これは起きている間に(サーバはユーザ名を表示し、クライアント画面ではそれは消え、奇妙な兆候は全くまたは全く見えません)

私はまた助けが必要です接続されている他のクライアントへのメッセージの配信は(ユーザー自身を除く)

コードサーバー:

#include "stdafx.h" 


long antwoord; 
char chatname[100]; 
char bericht[498]; 
char sbericht[498]; 


using namespace std; 

DWORD WINAPI SocketHandler(void*); 

//our main function 
void main() 
{ 
    //here we set the Winsock-DLL to start 

    WSAData wsaData; 
    WORD DLLVERSION; 
    DLLVERSION = MAKEWORD(2,1); 

    //here the Winsock-DLL will be started with WSAStartup 
    //version of the DLL 
    antwoord = WSAStartup(DLLVERSION, &wsaData); 

    if(antwoord != 0) 
    { 
     WSACleanup(); 
     exit(1); 
    } 
    else 
    { 
     cout << "WSA started successfully" <<endl; 
     cout << "The status: \n" << wsaData.szSystemStatus <<endl; 
    } 
    //the DLL is started 

    //structure of our socket is being created 
    SOCKADDR_IN addr; 

    //addr is our struct 

    int addrlen = sizeof(addr); 

    //socket sListen - will listen to incoming connections 
    SOCKET sListen; 
    //socket sConnect - will be operating if a connection is found. 
    SOCKET sConnect; 

    //setup of our sockets 
    //opgezocht op internet - AF_INET bekend dat het lid is van de internet familie 
    //Sock_STREAM betekenend dat onze socket een verbinding georiënteerde socket is. 
    sConnect = socket(AF_INET,SOCK_STREAM,NULL); 

    //now we have setup our struct 

    //inet_addr is our IP adres of our socket(it will be the localhost ip 
    //that will be 127.0.0.1 

    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 

    //retype of the family 
    addr.sin_family = AF_INET; 

    //now the server has the ip(127.0.0.1) 
    //and the port number (4444) 
    addr.sin_port = htons(4444); 

    //here we will define the setup for the sListen-socket 
    sListen = socket(AF_INET,SOCK_STREAM,NULL); 

    if (sConnect == INVALID_SOCKET) 
    { 
     cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
     WSACleanup(); 
    } 
    else 
    { 
     cout << "Connect socket() is OK!" <<endl; 
    } 

    if(sListen == INVALID_SOCKET) 
    { 
     cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
     WSACleanup(); 
    } 
    else 
    { 
     cout << "Listen socket() is OK!" <<endl; 
    } 

    //here the sListen-socket will be bind 
    //we say that the socket has the IP adress of (127.0.0.1) and is on port (4444) 
    //we let the socket become the struct "addr" 
    if(bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) 
    { 
     cout << "bind() failed: \n" << WSAGetLastError() <<endl; 
     WSACleanup(); 
     exit(1); 
    } 
    else{ 
     cout << "bind() is OK!" <<endl; 
    } 

    if(listen(sListen, 10) == -1){ 
     cout << "Error listening %d\n" << WSAGetLastError() <<endl; 

    } 

    //here we will tell what the server must do when a connection is found 
    //therefor we will create an endless loop 
    cout << "Waiting for a incoming connection..." <<endl; 


    //now we let the socket listen for incoming connections 
    //SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet 
    int* csock; 

    while(true) 
    { 
     csock = (int*)malloc(sizeof(int)); 
     //if a connection is found: show the message! 
     if((*csock = accept(sListen, (SOCKADDR*)&addr, &addrlen))!= INVALID_SOCKET) 
     { 
      cout << "A Connection was found with :" << inet_ntoa(addr.sin_addr) <<endl; 

      antwoord = send(*csock, "Welcome to our chat:", 21,NULL); 
      CreateThread(0,0,&SocketHandler, (void*)csock , 0,0); 
      cout << *csock <<endl; 

     } 
    } 

} 
//sbericht is the message 
DWORD WINAPI SocketHandler(void* lp) 
{ 
    int *csock = (int*)lp; 

    for(;;) 
    { 
     antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL); 
     antwoord = recv(*csock, chatname, sizeof(chatname), NULL); 

     while(antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL))) 
     { 
      printf("%s\: \"%s\"\n", chatname, sbericht); 
      antwoord = send(*csock, sbericht, sizeof(sbericht), NULL); 
      antwoord = send(*csock, chatname, sizeof(chatname), NULL); 

     } 
     return 0; 


    } 
} 

クライアントコード:

#include "stdafx.h" 

using namespace std; 

//our main function 
int main() 
{ 
    //here we set the Winsock-DLL to start 
    string bevestiging; 

    char chatname[100]; 

    char bericht[250]; 
    char sbericht[250]; 

    string strbericht; 

    string strsbericht; 

    long antwoord; 
    //here the Winsock-DLL will be started with WSAStartup 
        //version of the DLL 
    WSAData wsaData; 
    WORD DLLVERSION; 
    DLLVERSION = MAKEWORD(2,1); 
    antwoord = WSAStartup(DLLVERSION, &wsaData); 
    if(antwoord != 0) 
    { 
     exit(1); 
    } 
    else 
    { 
     cout << "WSA started successfully" <<endl; 
     cout << "The status: \n" << wsaData.szSystemStatus <<endl; 
    } 

    SOCKADDR_IN addr; 

    int addrlen = sizeof(addr); 

    SOCKET sConnect; 

    sConnect = socket(AF_INET, SOCK_STREAM, NULL); 

    if (sConnect == INVALID_SOCKET) 
    { 
     cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
    } 
    else 
    { 
     cout << "socket() is OK!\n" <<endl; 
    } 


    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 

    addr.sin_family = AF_INET; 

    addr.sin_port = htons(4444); 

    cout << "What is your chat name?" <<endl; 

    cin.getline(chatname, 100); 


    cout << "Do you want to connect to the server? [Y/N]" <<endl; 

    cin >> bevestiging; 


    if (bevestiging == "N") 
    { 
     exit(1); 
    } 
    else 
    { 
     if(bevestiging == "Y") 
     { 

      connect(sConnect, (SOCKADDR*)&addr, sizeof(addr)); 

      antwoord = recv(sConnect, bericht, sizeof(bericht), NULL); 

      strbericht = bericht; 

      cout << strbericht << chatname <<endl; 

      while(true) 
      { 
       if(antwoord > 1) 
       { 

        cin.clear(); 
        cin.sync(); 
        cout << chatname << " :" <<endl; 
        cin.getline(sbericht, sizeof(sbericht)); 
        antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL); 
        antwoord = send(sConnect, chatname, sizeof(chatname), NULL); 

        while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL))) 
        { 
         antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL); 
         antwoord = recv(sConnect, chatname, sizeof(chatname), NULL); 
         cout << chatname << ":" <<endl; 
         cout << sbericht <<endl; 
         cin.getline(sbericht, 250); 

        } 

       } 

       else 
       { 
       cout << "The connection to the server has been lost... \n" << "please exit the client." <<endl; 

       } 
      } 

申し訳ありませんが、私は(私はソケットをプログラムするために学んでいる)だけでなく、それを書いていないが、私はこの1つを把握することはできません場合。だから私には難しくない、私はまだ学ぶ必要があるが、私は必要なものを見つけることができません。だから、もし誰かが私にそれをする方法を教えてもらえれば、私はそれがどのように、そして、なぜそれがどのように見えるかと思います。

いつも何かを学ぶ(私は現在、beejeeのネットワークプログラミングチュートリアルで忙しいです)。

+0

この質問は非常に幅広く、実際にはいくつかの質問があります。一度に一つのことを尋ね、特定の質問をカバーする最小限の例に与えられたコードを減らしてください。良いSOの質問を書く上でのヒントはhttp://tinyurl.com/sohintsで見つけることができます。 –

+0

私はこのプロジェクトを2つの部分に分割することをお勧めします。非スレッド型のサーバーなので、ネットワーキングの側面を完全に理解し、stdio/iostreamと完全に異なる 'スレッド化するサンドボックス'を置きます。あなたがそれらをうまく理解していなければ、あなたは悪夢を持ってそれらをまとめるでしょう。 – kfmfe04

+0

大丈夫ですので、今この問題に納得しています [link](http://i42.tinypic.com/awq81h.jpg) –

答えて

0

sberichtとchatname - あなたの2つのスレッドが同時に で、このグローバル・バッファでの作業 グローバル変数だから、1つのスレッドが別のスレッド

+0

バッファにスレッドを入れておくのもいいでしょうか? –

+0

それほど大きくない場合 - はい。各スレッドはそのスタックを持っています。スレッドのスタックサイズはデフォルト(Windowsの場合)1メガバイトです。あなたの状況では、スレッドにバッファを入れる必要があります – Andrew

0

のデータを書き換え、いくつかのこのコードの問題が、ソケットがあり、粘着性のものです始めるときにあなたの頭を包み込む。あなたのサーバーコードでは、マルチスレッドがあなたの本当の獣だと思われます。スレッドとソケットは非常に異なる概念ですが、しばしば一緒に使用されます。大きな問題(Andrewが言ったように)は、競争条件があるということです。複数のスレッドにわたってグローバル変数に書き込む場合、mutexを使用して相互排他を保証する必要があります。たとえば、SocketHandlerでは、変数antwoordを保護する必要があります。さらに、malloc()free()ではなく、C++でnewdeleteを使用してください(これらはC言語で使用されています)。また、新しい接続ごとにcsock変数の値を上書きすることに注意してください。各クライアントのオープン接続ソケットを保持するには、別々の変数が必要です。

サーバソケットは、基本的に次のように動作します。socket()bind()listen()accept()。今度は、ポート番号をbind()にして、listen()を開いてさらに接続すると、accept()はクライアントを再ルーティングし、別のソケットファイル記述子(戻り値はaccept())に入れます。このクライアントとの通信を続けます。したがって、クライアントごとに、accept()の値を一意に保持する必要があります。

私は懐疑的ですが、お客様のクライアントコードでは、あなたが期待していることを受け取っています。あなたのプロトコルはどこかでユーザー名とチャット名だけを送信することを保証していますか?すべてのデータを取得するには、単一のrecv()コールが必要です。また、次の行の妥当性は何ですか?

while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL))) 

あなたは、単に二度同じ仕事をしている - 1時間が十分なものでなければならない(そしておそらくプログラムは、この時点でブロックし、あなたは知らなくても、データが上書きされる場合があります)。単純なテキストプロトコルでは、〜512バイトの変数を作成し、recv()への1回の呼び出しでサーバーからのすべての情報を適切に受け取ることができます。

私はあなたのコードで大きな問題を選択しようとしましたが、これらの問題は多かれ少なかれあなたのコードの他の領域でも頻繁に発生します。マルチスレッドに慣れていない場合は、後でその問題を攻撃してください。シングルスレッドソケットのやり方を学び、マルチスレッドを学びましょう。一度に両方に取り組むことはあなたに噛み付くでしょう。がんばろう!

+0

私はSocketHandlerでチャットチャット名を定義していたのですが、問題は解決しました。 –

+0

OK(サーバー上で正常に動作している間に、クライアント) –