c++服务器客户端聊天

发布于 2024-11-07 19:14:01 字数 7324 浏览 0 评论 0原文

我正在基于 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;

            }
        }
    }
}
    }

I'm making a server, client app in c++ console based.

What I did so far:

  • I can connect to the server.
  • I can send messages to the server.
  • The server can send the messages back.

But what I can't figure out, how can I let the server act also as a client to send messages to the client while he is processing received messages from the client?

People can use it as an example as well :D

Well I will post also some parts of the code:

server:

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


}
}

Client:

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

            }
        }
    }
}
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

朱染 2024-11-14 19:14:01

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

You would probably have to open another socket. The client would have to act as a server as well.

绝影如岚 2024-11-14 19:14:01

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

其次:如果别人想理解你的源代码,但不熟悉你的母语,他们就必须猜测。出于这个原因和其他各种原因,请尝试使用英语作为源代码语言。

现在回答您的问题:

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

First of all: putting a 20mb zip file in to the web for about 4 interesting source files is not a good option. Your object files and debug output is of no interest to us, since we want to help with your source code. Try uploading a zip file containing only the source files the next time.

Secondly: If others want to understand your source code and are not familiar with your native language, they have to guess. Try using english as source code language for this and a variety of other reasons.

Now to answer your question:

The answer is already in your code. Currently, the server is looping until a maximum number of connects, receives input and sends back an answer. So actually you have already implemented it. I guess if you want to send initiated messages in both ways you have to alter your software architecture a bit.

影子的影子 2024-11-14 19:14:01

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

  • 服务器一次只能处理一个客户端。如果您的服务器上将有多个用户(聊天服务器总是如此),您需要能够同时侦听多个连接。 selectWSAEventSelectWaitForMultipleObjects 在这里会有很大帮助。

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

  • 谈话是同步进行的。也就是说,客户端发送一条消息,等待响应,然后(并且)等待控制台输入。通过这种设计,每发送一条消息总是会收到一条消息。为了解决这个问题,我通常会为每个方向的数据设置一个线程——一个线程获取控制台输入并将其发送到服务器,而另一个线程监听服务器并打印收到的消息。 (注意,这意味着在您打字时可以接收消息。这就是重点。但是它使控制台输入有点烦人。)线程是一个半高级主题 - 一旦您开始创建新线程,您通常必须担心同步等问题。但在这种情况下,它通常比其他替代方案更清晰。

示例线程代码(非常粗略,因为我手边没有 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);
}

Your code has a few fundamental problems:

  • The server can only handle one client at a time. If your server will ever have more than a single user on it (as a chat server invariably will), you need to be able to listen for more than one connection at once. select, or WSAEventSelect and WaitForMultipleObjects, would help a lot here.

  • You assume that a whole fixed-size message will appear at a time. TCP can not guarantee that (as the "stream" concept considers the data as just a potentially infinite sequence of individual bytes), and a half-sent message could freeze up your server while it waits for the rest. Not a big deal if this is all on your LAN, but if you expose this service to the internet, you're asking for random lockups. In order to prevent that, get the data and put it in a buffer as it comes, processing it only when you have a whole message.

  • The conversation is done in lock-step. That is, the client sends a message, and waits for a response, and then (and only then) expects console input. With this design, there will always be one message received per message sent. In order to get around this, i'll often have a thread for the data going in each direction -- one that gets the console input and sends it to the server, while the other listens to the server and prints the message received. (Note, this means messages could be received while you're typing. That's kinda the point. But it makes console input a bit annoying.) Threading is a semi-advanced topic -- once you start creating new threads, you often have to worry about synchronization and such. But it's generally cleaner than the alternatives in this case.

Sample threaded code (very roughly, since i don't have a C++ compiler handy):

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文