【问题标题】:c++ multi client/server chatc++ 多客户端/服务器聊天
【发布时间】:2011-12-18 14:03:56
【问题描述】:

我陷入了谷底,正在考虑解决我的聊天服务器和客户端遇到的问题。

应该做什么,客户端要求输入用户名,然后向用户提出连接请求并回答 [Y/N]。

当点击是时,客户端必须连接到服务器,当它需要进入单独的线程时(用于处理多个客户端(但我的问题是,当多个用户加入时(用户当前登录的用户名in 更改为最后一个加入聊天的人。 当这发生时(服务器显示用户名,而在客户端屏幕上它消失了,没有或所有奇怪的迹象出现)。

我还需要帮助将消息分发给连接的其他客户端(不包括用户本人)

代码服务器:

#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的网络编程教程)。

【问题讨论】:

  • 这个问题很宽泛——实际上是几个问题。请一次问一件事,并减少提供给涵盖特定问题的最小示例的代码。更多关于编写好的 SO 问题的提示可以在 tinyurl.com/sohints 找到。
  • 我建议您将此项目分解为两部分:一个non-threaded 服务器,以便您完全了解网络方面和一个完全不同的threading sandbox,其中您使用互斥 IO 的 stdio/iostream。如果您不能很好地单独理解它们,那么将它们组合在一起将会是一场噩梦。
  • 好的,所以我现在就解决这个问题link
  • 好的,所以我现在的工作方式如下:服务器显示聊天名称和消息,但似乎在收到来自客户端的 8 条消息时崩溃。它一遍又一遍地重复最后一条消息。

标签: c++ client chat stream-socket-client


【解决方案1】:

sbericht 和 chatname - 全局变量 您的 2 个线程同时使用此全局缓冲区 所以一个线程重写另一个线程的数据

【讨论】:

  • 如果它们不是很大 - 是的。每个线程都有其堆栈。默认情况下(在 Windows 中)线程的堆栈大小为 1 兆字节。在您的情况下,有必要在线程中放置缓冲区
【解决方案2】:

这段代码有一些问题,但是当你开始时,套接字是一个棘手的问题。在您的服务器代码中,多线程似乎是您真正的野兽。请注意,线程和套接字是非常不同的概念,但它们经常一起使用。一个大问题(正如安德鲁所说)是你有竞争条件。如果要跨多个线程写入全局变量,则需要使用互斥锁来确保互斥。例如,在SocketHandler 中,您需要保护您的变量antwoord。此外,在 C++ 中使用 newdelete 而不是 malloc()free()(这些在 C 中使用)。另外,请注意,对于每个新连接,您都会覆盖 csock 变量的值。您需要单独的变量来保存每个客户端的打开连接套接字。

服务器套接字基本上以这种方式工作:socket()bind()listen()accept()。现在,为了保持您的bind()'ed 端口和listen()'ing 的开放更多连接,accept() 重新路由您的客户端并将它们放在另一个套接字文件描述符上(这是返回值accept()) 以便您可以继续与此客户端通信。因此,对于每个客户端,您需要唯一地保存accept() 的值。

但是,我怀疑在您的客户端代码中,您是否收到了预期的结果。您的协议是否保证它只会发送用户名,然后是聊天名称?您可能只需要一个recv() 调用来检索您的所有数据。另外,以下行的相关性是什么?

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

您只是在做两次相同的工作 - 一次就足够了(也许程序此时可能会阻塞并在您不知情的情况下覆盖数据)。对于一个简单的文本协议,您很可能可以创建一个 ~512bytes 的变量,并在一次调用 recv() 时适当地从服务器接收所有信息。

我试图找出您代码中的大问题,而这些问题或多或少在您代码的其他区域也很常见。如果您不熟悉多线程,请稍后再解决该问题。学习如何做单线程套接字,然后去学习多线程。同时解决他们两个会咬你。祝你好运!

【讨论】:

  • 我修复了名称问题,我所做的是在 SocketHandler 中定义字符聊天名称
  • 好的,所以我改变了它(虽然它在服务器上工作正常,正确显示不同客户端的名称)
猜你喜欢
  • 2018-10-04
  • 1970-01-01
  • 2016-04-09
  • 2017-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-17
  • 2013-04-09
相关资源
最近更新 更多