c ++服务器客户端聊天

时间:2011-05-16 09:30:37

标签: c++ client client-server chat winsock

我正在基于c ++控制台制作服务器,客户端应用程序。

到目前为止我做了什么:

  • 我可以连接到服务器。
  • 我可以向服务器发送消息。
  • 服务器可以发回消息。

但是我无法弄清楚,在处理从客户端收到的消息时,我怎样才能让服务器充当客户端向客户端发送消息?

人们也可以用它作为例子:D

我还会发布代码的一些部分:

服务器:

  #include "stdafx.h"
  using namespace std;
 //our main function
 void main()
     {
int numClients;
long antwoord;
char chatname[100];
char bericht[250]; //messages
char sbericht[250]; //smessages
     //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("192.168.1.103");

//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;
}


//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;
for(;;)
{

        //now we let the socket listen for incoming connections
            //SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet
        listen(sListen, SOMAXCONN);
        while(numClients < SOMAXCONN)
        {
            //if a connection is found: show the message!
            if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
            {
                cout << "A Connection was found!" <<endl;

                antwoord = send(sConnect, "Welcome to our chat:", 21,NULL);

                if(antwoord > 1)
                {

                    antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
                    antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);

                        while(antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL)) )
                        {
                            antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
                            antwoord = send(sConnect, chatname, sizeof(chatname), NULL);    
                        }

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


}
}

客户端:

    // ChatServer.cpp : Defines the entry point for the console application.
    //
    //include of the stdafx.h file where importent files are being included

    #include "stdafx.h"

    using namespace std;

    void smessage()
    {

    }
   //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("192.168.1.103");

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, 250);
                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;

            }
        }
    }
}
    }

3 个答案:

答案 0 :(得分:0)

您可能需要打开另一个套接字。客户端也必须充当服务器。

答案 1 :(得分:0)

首先:将一个20mb的zip文件放入网上,获取大约4个有趣的源文件,这不是一个好选择。您的目标文件和调试输出对我们不感兴趣,因为我们希望帮助您的源代码。尝试下次上传仅包含源文件的zip文件。

其次:如果其他人想要了解您的源代码并且不熟悉您的母语,他们必须猜测。尝试使用英语作为源代码语言,以及其他各种原因。

现在回答你的问题:

答案已在您的代码中。目前,服务器正在循环,直到最大连接数,接收输入并发回答案。实际上你已经实现了它。我想如果你想以两种方式发送启动的消息,你必须稍微改变你的软件架构。

答案 2 :(得分:0)

您的代码存在一些基本问题:

  • 服务器一次只能处理一个客户端。如果您的服务器上只有一个用户(就像聊天服务器一样),您需要能够同时监听多个连接。 selectWSAEventSelectWaitForMultipleObjects会对此有所帮助。

  • 您假设一次会显示整个固定大小的消息。 TCP无法保证(因为“流”概念将数据视为可能无限的单个字节序列),半数发送的消息可能会在等待其余时间时冻结服务器。如果这一切都在你的局域网上,那就没什么大不了了,但是如果你将这项服务暴露在互联网上,你就会要求随机锁定。为了防止这种情况,请获取数据并将其放在缓冲区中,只有当您有完整的消息时才对其进行处理。

  • 对话以锁定步骤完成。也就是说,客户端发送消息,并等待响应,然后(并且然后)期望控制台输入。通过这种设计,每个发送的消息总会收到一条消息。为了解决这个问题,我经常会有一个线程来处理每个方向的数据 - 一个获取控制台输入并将其发送到服务器,另一个监听服务器并打印收到的消息。 (注意,这意味着你可以在输入时收到消息。这就是重点。但它会使控制台输入有点烦人。)线程是一个半高级主题 - 一旦你开始创建新线程,你经常需要担心同步等问题。但在这种情况下,它通常比替代品更清洁。

示例线程代码(非常粗略,因为我没有方便的C ++编译器):

const int MessageLength = 250;
const int NameLength = 250;

char myname[NameLength];

bool sendFully(SOCKET s, char* buffer, size_t buffer_len, int flags)
{
    char *end = buffer + buffer_len;
    while (buffer != buffer_len)
    {
        int sent = send(s, buffer, end - buffer, flags);
        if (sent == 0) return false;
        buffer += sent;
    }
    return true;
}

DWORD WINAPI watchConsoleInput(void*)
{
    char input[MessageLength];
    while (true)
    {
        std::cin.getline(input, MessageLength);
        if (!sendFully(sConnect, input, sizeof(input), 0))
            break;
        if (!sendFully(sConnect, myname, sizeof(myname), 0))
            break;
    }
    return 0;
}

int main()
{
    char chatname[NameLength];
    char sbericht[MessageLength];

    ... get our name in myname ...

    ...  do the connect stuff  ...

    HANDLE watcher = CreateThread(NULL, 0, watchConsoleInput, NULL, 0, NULL);

    while (true)
    {
        // Added MSG_WAITALL to work around the whole-message-at-a-time thing
        if (recv(sConnect, sbericht, sizeof(sbericht), MSG_WAITALL) != sizeof(sbericht))
            break;
        if (recv(sConnect, chatname, sizeof(chatname), MSG_WAITALL) != sizeof(sbericht))
            break;
    }

    // Don't care about errors; we're just being polite
    shutdown(sConnect, SD_BOTH);

    closesocket(sConnect);
    cout << "Connection lost\n";

    // ExitProcess rather than just 'return', so we know the watcher thread dies
    ExitProcess(0);
}