使用c+连续地从服务器向接收器发送数据+; 我用Winsock API在C++中进行编码。我正在进行多客户端服务器聊天。我在代码中遇到的问题是,我的服务器只能向客户端发送一次消息。但我希望这种情况发生多次。我无法将accept()函数放在服务器代码的无限循环之外。我已将select()用于多客户端。我在做这件事时没有穿线

使用c+连续地从服务器向接收器发送数据+; 我用Winsock API在C++中进行编码。我正在进行多客户端服务器聊天。我在代码中遇到的问题是,我的服务器只能向客户端发送一次消息。但我希望这种情况发生多次。我无法将accept()函数放在服务器代码的无限循环之外。我已将select()用于多客户端。我在做这件事时没有穿线,c++,sockets,client-server,winsock2,C++,Sockets,Client Server,Winsock2,服务器: #include <iostream> #include <WS2tcpip.h> #include <string> #include <sstream> #pragma comment (lib, "ws2_32.lib") using namespace std; void main() { // Initialze winsock WSADATA wsData; WORD ver = MAKEWORD(2, 2)

服务器:

#include <iostream>
#include <WS2tcpip.h>
#include <string>
#include <sstream>

#pragma comment (lib, "ws2_32.lib")

using namespace std;

void main()
{
// Initialze winsock
   WSADATA wsData;
   WORD ver = MAKEWORD(2, 2);

   int wsOk = WSAStartup(ver, &wsData);
   if (wsOk != 0)
  {
    cerr << "Can't Initialize winsock! Quitting" << endl;
    return;
  }

// Create a socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
    cerr << "Can't create a socket! Quitting" << endl;
    return;
}

// Bind the ip address and port to a socket
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton .... 

bind(listening, (sockaddr*)&hint, sizeof(hint));

// Tell Winsock the socket is for listening 
listen(listening, SOMAXCONN);

// Create the master file descriptor set and zero it
fd_set master;
FD_ZERO(&master);

// Add our first socket that we're interested in interacting with; the listening socket!
// It's important that this socket is added for our server or else we won't 'hear' incoming
// connections 
FD_SET(listening, &master);

// this will be changed by the \quit command (see below, bonus not in video!)
bool running = true;

while (running)
{
    // Make a copy of the master file descriptor set, this is SUPER important because
    // the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that
    // are accepting inbound connection requests OR messages. 

    // E.g. You have a server and it's master file descriptor set contains 5 items;
    // the listening socket and four clients. When you pass this set into select(), 
    // only the sockets that are interacting with the server are returned. Let's say
    // only one client is sending a message at that time. The contents of 'copy' will
    // be one socket. You will have LOST all the other sockets.

    // SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!!

    fd_set copy = master;

    // See who's talking to us
    int socketCount = select(0, &copy, nullptr, nullptr, nullptr);

    for (int i = 0; i < socketCount; i++) {

        //Accept a new connection

        SOCKET sock = copy.fd_array[i];
        if (sock == listening) {
            //Accept a new connection

            SOCKET client = accept(listening, nullptr, nullptr);

            //Add a new connection

            FD_SET(client, &master);

            string mssg = "Welcome to the awesome chat server\n";
            //Send a welcome message to the connected client
            send(client, mssg.c_str(), mssg.size() + 1, 0);
        }

        //Send a new message

        string mssg;
        getline(cin, mssg);

        int bytes = send(sock, mssg.c_str(), mssg.size() + 1, 0);

        for (int i = 0; i < master.fd_count; i++) {
            SOCKET outsock = master.fd_array[i];
            if (outsock != listening && outsock != sock) {
                send(outsock, mssg.c_str(), mssg.size() + 1, 0);
            }
        }

    }

}   


// Remove the listening socket from the master file descriptor set and close it
// to prevent anyone else trying to connect.
FD_CLR(listening, &master);
closesocket(listening);

// Message to let users know what's happening.
string msg = "Server is shutting down. Goodbye\r\n";

while (master.fd_count > 0)
{
    // Get the socket number
    SOCKET sock = master.fd_array[0];

    // Send the goodbye message
    send(sock, msg.c_str(), msg.size() + 1, 0);

    // Remove it from the master file list and close the socket
    FD_CLR(sock, &master);
    closesocket(sock);
}

// Cleanup winsock
WSACleanup();

system("pause");
     }
#包括
#包括
#包括
#包括
#pragma注释(lib,“ws2_32.lib”)
使用名称空间std;
void main()
{
//初始化winsock
WSADATA-wsData;
单词ver=MAKEWORD(2,2);
int wsOk=WSAStartup(版本和wsData);
如果(wsOk!=0)
{
cerr”;
//getline(cin,userInput);
//发送结果
//int sendResult=send(sock,userInput.c_str(),userInput.size()+1,0);
//if(sendResult!=套接字错误){
//零内存(buff,0);
int bytesreceived=recv(短袜,浅黄色,4096,0);
如果(字节接收>0){
//对控制台的回显响应
无法编辑:

您应该做一些修改:

  • 使用timeval进行选择以避免阻塞选择(等待 已建立新连接或有内容可阅读)
  • 将读取/发送消息部分移出for循环
  • 在另一个线程中进行单独的键输入处理
  • 使用安全队列在输入线程和通信线程(主线程)之间共享输入
  • 以下是一个例子:

    #include <iostream>
    #include <WS2tcpip.h>
    #include <string>
    #include <sstream>
    
    #include <thread>
    #include <mutex>
    #include <list>
    
    
    #pragma comment (lib, "ws2_32.lib")
    
    using namespace std;
    
    class safe_queue {
        mutex m;
        list<string> str_queue;
    
    public:
        safe_queue() {};
        void add(const string &s) {
            const lock_guard<mutex> lock(m);
            str_queue.push_back(s);
        }
    
        bool pop( string &s ) {
            const lock_guard<mutex> lock(m);
            if (!str_queue.empty()) {
                s = str_queue.front();
                str_queue.pop_front();
                return true;
            }
    
            return false;
        }
    };
    
    
    
    int main()
    {
        // Initialze winsock
        WSADATA wsData;
        WORD ver = MAKEWORD(2, 2);
    
        int wsOk = WSAStartup(ver, &wsData);
        if (wsOk != 0)
        {
            cerr << "Can't Initialize winsock! Quitting" << endl;
            return 0;
        }
    
        // Create a socket
        SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
        if (listening == INVALID_SOCKET)
        {
            cerr << "Can't create a socket! Quitting" << endl;
            return 0;
        }
    
        // Bind the ip address and port to a socket
        sockaddr_in hint;
        hint.sin_family = AF_INET;
        hint.sin_port = htons(54000);
        hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton .... 
    
        bind(listening, (sockaddr*)&hint, sizeof(hint));
    
        // Tell Winsock the socket is for listening 
        listen(listening, SOMAXCONN);
    
        // Create the master file descriptor set and zero it
        fd_set master;
        FD_ZERO(&master);
    
        // Add our first socket that we're interested in interacting with; the listening socket!
        // It's important that this socket is added for our server or else we won't 'hear' incoming
        // connections 
        FD_SET(listening, &master);
    
        // this will be changed by the \quit command (see below, bonus not in video!)
        bool running = true;
    
        safe_queue sq;
    
        auto io_thread = thread([&] {
            string s;
            while (running && getline(std::cin, s, '\n')){
                sq.add(s);
            }
        });//thread.
    
        while (running)
        {
            // Make a copy of the master file descriptor set, this is SUPER important because
            // the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that
            // are accepting inbound connection requests OR messages. 
    
            // E.g. You have a server and it's master file descriptor set contains 5 items;
            // the listening socket and four clients. When you pass this set into select(), 
            // only the sockets that are interacting with the server are returned. Let's say
            // only one client is sending a message at that time. The contents of 'copy' will
            // be one socket. You will have LOST all the other sockets.
    
            // SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!!
    
            fd_set copy = master;
    
            timeval tv = {0,0};
    
            // See who's talking to us
            int socketCount = select(0, &copy, nullptr, nullptr, &tv);
    
            for (int i = 0; i < socketCount; i++) {
    
                //Accept a new connection
    
                SOCKET sock = copy.fd_array[i];
                if (sock == listening) {
                    //Accept a new connection
    
                    SOCKET client = accept(listening, nullptr, nullptr);
    
                    //Add a new connection
    
                    FD_SET(client, &master);
    
                    string mssg = "Welcome to the awesome chat server\n";
                    //Send a welcome message to the connected client
                    send(client, mssg.c_str(), mssg.size() + 1, 0);
                }
            }//for.
    
    
            string mssg;
            if (sq.pop(mssg) ) {
    
                std::cout << "Send :" << mssg << endl;
    
                for (u_int i = 0; i < master.fd_count; i++) {
                    SOCKET outsock = master.fd_array[i];
                    if (outsock != listening) {
                        send(outsock, mssg.c_str(), mssg.size() + 1, 0);
                    }
                }
            }
    
        }//while
    
    
        // Remove the listening socket from the master file descriptor set and close it
        // to prevent anyone else trying to connect.
        FD_CLR(listening, &master);
        closesocket(listening);
    
        // Message to let users know what's happening.
        string msg = "Server is shutting down. Goodbye\r\n";
    
        while (master.fd_count > 0)
        {
            // Get the socket number
            SOCKET sock = master.fd_array[0];
    
            // Send the goodbye message
            send(sock, msg.c_str(), msg.size() + 1, 0);
    
            // Remove it from the master file list and close the socket
            FD_CLR(sock, &master);
            closesocket(sock);
        }
    
        // Cleanup winsock
        WSACleanup();
    
        system("pause");
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #pragma注释(lib,“ws2_32.lib”)
    使用名称空间std;
    类安全队列{
    互斥m;
    列表stru队列;
    公众:
    安全队列(){};
    无效添加(常量字符串和s){
    常数锁和防护锁(m);
    str_队列。推回(s);
    }
    布尔流行音乐(弦乐和s){
    常数锁和防护锁(m);
    如果(!str_queue.empty()){
    s=str_queue.front();
    str_queue.pop_front();
    返回true;
    }
    返回false;
    }
    };
    int main()
    {
    //初始化winsock
    WSADATA-wsData;
    单词ver=MAKEWORD(2,2);
    int wsOk=WSAStartup(版本和wsData);
    如果(wsOk!=0)
    {
    cerr编辑:

    您应该做一些修改:

  • 使用timeval进行选择以避免阻塞选择(等待 已建立新连接或有内容可阅读)
  • 将读取/发送消息部分移出for循环
  • 在另一个线程中进行单独的键输入处理
  • 使用安全队列在输入线程和通信线程(主线程)之间共享输入
  • 以下是一个例子:

    #include <iostream>
    #include <WS2tcpip.h>
    #include <string>
    #include <sstream>
    
    #include <thread>
    #include <mutex>
    #include <list>
    
    
    #pragma comment (lib, "ws2_32.lib")
    
    using namespace std;
    
    class safe_queue {
        mutex m;
        list<string> str_queue;
    
    public:
        safe_queue() {};
        void add(const string &s) {
            const lock_guard<mutex> lock(m);
            str_queue.push_back(s);
        }
    
        bool pop( string &s ) {
            const lock_guard<mutex> lock(m);
            if (!str_queue.empty()) {
                s = str_queue.front();
                str_queue.pop_front();
                return true;
            }
    
            return false;
        }
    };
    
    
    
    int main()
    {
        // Initialze winsock
        WSADATA wsData;
        WORD ver = MAKEWORD(2, 2);
    
        int wsOk = WSAStartup(ver, &wsData);
        if (wsOk != 0)
        {
            cerr << "Can't Initialize winsock! Quitting" << endl;
            return 0;
        }
    
        // Create a socket
        SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
        if (listening == INVALID_SOCKET)
        {
            cerr << "Can't create a socket! Quitting" << endl;
            return 0;
        }
    
        // Bind the ip address and port to a socket
        sockaddr_in hint;
        hint.sin_family = AF_INET;
        hint.sin_port = htons(54000);
        hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton .... 
    
        bind(listening, (sockaddr*)&hint, sizeof(hint));
    
        // Tell Winsock the socket is for listening 
        listen(listening, SOMAXCONN);
    
        // Create the master file descriptor set and zero it
        fd_set master;
        FD_ZERO(&master);
    
        // Add our first socket that we're interested in interacting with; the listening socket!
        // It's important that this socket is added for our server or else we won't 'hear' incoming
        // connections 
        FD_SET(listening, &master);
    
        // this will be changed by the \quit command (see below, bonus not in video!)
        bool running = true;
    
        safe_queue sq;
    
        auto io_thread = thread([&] {
            string s;
            while (running && getline(std::cin, s, '\n')){
                sq.add(s);
            }
        });//thread.
    
        while (running)
        {
            // Make a copy of the master file descriptor set, this is SUPER important because
            // the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that
            // are accepting inbound connection requests OR messages. 
    
            // E.g. You have a server and it's master file descriptor set contains 5 items;
            // the listening socket and four clients. When you pass this set into select(), 
            // only the sockets that are interacting with the server are returned. Let's say
            // only one client is sending a message at that time. The contents of 'copy' will
            // be one socket. You will have LOST all the other sockets.
    
            // SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!!
    
            fd_set copy = master;
    
            timeval tv = {0,0};
    
            // See who's talking to us
            int socketCount = select(0, &copy, nullptr, nullptr, &tv);
    
            for (int i = 0; i < socketCount; i++) {
    
                //Accept a new connection
    
                SOCKET sock = copy.fd_array[i];
                if (sock == listening) {
                    //Accept a new connection
    
                    SOCKET client = accept(listening, nullptr, nullptr);
    
                    //Add a new connection
    
                    FD_SET(client, &master);
    
                    string mssg = "Welcome to the awesome chat server\n";
                    //Send a welcome message to the connected client
                    send(client, mssg.c_str(), mssg.size() + 1, 0);
                }
            }//for.
    
    
            string mssg;
            if (sq.pop(mssg) ) {
    
                std::cout << "Send :" << mssg << endl;
    
                for (u_int i = 0; i < master.fd_count; i++) {
                    SOCKET outsock = master.fd_array[i];
                    if (outsock != listening) {
                        send(outsock, mssg.c_str(), mssg.size() + 1, 0);
                    }
                }
            }
    
        }//while
    
    
        // Remove the listening socket from the master file descriptor set and close it
        // to prevent anyone else trying to connect.
        FD_CLR(listening, &master);
        closesocket(listening);
    
        // Message to let users know what's happening.
        string msg = "Server is shutting down. Goodbye\r\n";
    
        while (master.fd_count > 0)
        {
            // Get the socket number
            SOCKET sock = master.fd_array[0];
    
            // Send the goodbye message
            send(sock, msg.c_str(), msg.size() + 1, 0);
    
            // Remove it from the master file list and close the socket
            FD_CLR(sock, &master);
            closesocket(sock);
        }
    
        // Cleanup winsock
        WSACleanup();
    
        system("pause");
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #pragma注释(lib,“ws2_32.lib”)
    使用名称空间std;
    类安全队列{
    互斥m;
    列表stru队列;
    公众:
    安全队列(){};
    无效添加(常量字符串和s){
    常数锁和防护锁(m);
    str_队列。推回(s);
    }
    布尔流行音乐(弦乐和s){
    常数锁和防护锁(m);
    如果(!str_queue.empty()){
    s=str_queue.front();
    str_queue.pop_front();
    返回true;
    }
    返回false;
    }
    };
    int main()
    {
    //初始化winsock
    WSADATA-wsData;
    单词ver=MAKEWORD(2,2);
    int wsOk=WSAStartup(版本和wsData);
    如果(wsOk!=0)
    {
    
    cerr在我看来,你不能为
    sock
    写任何新的东西,因为它被锁定在循环中。你可能应该从在循环中实现一个等待期开始。我认为没有额外的线程,这是不可能完成的……难道你不能同时等待带有
    WaitForMultipleObjects
    的套接字吗然后可以等待任何超时为零的套接字(即,只要检查是否有内容可读取)。或者检查是否将套接字设置为非阻塞模式。您还必须以非阻塞方式检查用户输入。查看
    conio.h
    。如果有用户输入,您可以使用
    kbhit
    进行检查(getline本身也在阻塞!)。每当我包含conio时,它都会给出一个错误。在我看来,您无法向
    sock
    写入任何新内容,因为它被锁定在循环中。您可能应该从在循环中实现等待期开始。我认为,如果没有额外的线程,它是无法完成的……您是否可以使用
    等待来等待套接字也可以使用MultipleObjects
    吗?然后,您可以等待任何超时为零的套接字(也就是说,只需检查是否有内容可读取)。或签出将套接字设置为非阻塞模式。您还必须以非阻塞方式检查用户输入。查看
    conio.h
    。如果有用户输入,您可以使用
    kbhit
    进行检查(getline本身也是阻塞的!)。每当我包含conio时,它都会给出一个错误。那么解决方案是什么?我如何停止阻塞,或者如何在其中使用线程?@RishabhMalhotra:这取决于您的应用程序大小,如果您只想在控制台上使用一个简单的聊天客户端服务器,您可以使用kbhit()和getch()/getline()组合使用。如果你的应用程序将包含UI,你应该使用线程来分离通信和输入。如何使用线程来创建独立客户端?我没有在项目中使用线程的经验。那么解决方案是什么?如何停止阻塞或如何在其中使用线程?@RishabhMalhotra:这取决于你的应用程序如果你只想在控制台上使用一个简单的聊天客户端服务器,你可以使用kbhit()和getch()/getline()。如果你的应用程序将包含UI,你应该使用线程来分离通信和输入。如何使用线程来创建独立的客户端?我没有在项目中使用线程的经验。