0

チャット サーバーとクライアントで発生している問題の解決策を考えて、底を打っています。

何をすべきか、クライアントはユーザー名を要求し、次にユーザーへの接続要求を [Y/N] で応答します。

はいを押すと、クライアントはサーバーに接続する必要があります。接続する場合は、複数のクライアントを処理するために別のスレッドに入る必要があります(ただし、私の問題は、複数のユーザーが参加すると(現在ログインしているユーザーのユーザー名が変更されることです)チャットが行われている間 (サーバーにはユーザー名が表示されますが、クライアント画面ではユーザー名が表示されなくなり、奇妙な兆候は何も表示されないか、すべて表示されます)。

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

コード サーバー:

#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;

                }
            }

うまく書けなかったら申し訳ありません (ソケットのプログラミングを学んでいる最中です) が、これを理解することはできません。だから私に苦労しないでください、私はまだ学ぶ必要がありますが、必要なものを見つけることができません. だから、誰かがその方法を教えてくれれば、それがどのように行われたのか、そしてその理由を理解できると思います.

常に何かを学んでください (私は現在、beejee のネットワーク プログラミング チュートリアルにも取り組んでいます)。

4

2 に答える 2

0

sberichtとchatname-グローバル変数このグローバルバッファを同時に処理する2つのスレッドしたがって、1つのスレッドが別のスレッドのデータを書き換えます

于 2011-12-18T14:20:54.660 に答える
0

このコードにはいくつかの問題がありますが、開始時にソケットは厄介なものです。あなたのサーバー コードでは、マルチスレッドが真の野獣であるように見えます。スレッドとソケットはまったく異なる概念ですが、しばしば一緒に使用されることに注意してください。(Andrew が言ったように) 大きな問題は、競合状態があることです。複数のスレッドにまたがってグローバル変数に書き込む場合は、mutex を使用して相互排他を確保する必要があります。たとえばSocketHandler、変数を保護する必要がありますantwoord。さらに、newanddeleteではなく C++ でmalloc()andをfree()使用します (これらは C で使用されます)。また、新しい接続ごとに、csock変数の値を上書きすることに注意してください。各クライアントの開いている接続ソケットを保持するには、個別の変数が必要です。

サーバーソケットは、基本的に次のように機能します: socket()bind()、。ここで、接続先のポートを開いたままにして、より多くの接続を確立するために、クライアントを再ルーティングし、別のソケット ファイル記述子 (の戻り値) に配置して、通信を継続できるようにします。このクライアント。したがって、クライアントごとに一意の値を保持する必要があります。listen()accept()bind()listen()accept()accept()accept()

ただし、クライアント コードで期待どおりの結果が得られているかどうかについては懐疑的です。あなたのプロトコルは、ユーザー名とチャット名だけを送信することをどこかで保証していますか? recv()すべてのデータを取得するには、1 回の呼び出しが必要な場合があります。また、次の行の関連性は何ですか?

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

同じ作業を 2 回行っているだけです。1 回で十分です (おそらく、プログラムがこの時点でブロックされ、知らないうちにデータが上書きされる可能性があります)。単純なテキスト プロトコルの場合、512 バイトまでの変数を作成し、サーバーからすべての情報をrecv().

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

于 2011-12-18T14:41:27.693 に答える