Непрерывная отправка данных с сервера на приемник с использованием c ++ - PullRequest
0 голосов
/ 14 января 2020

Я пишу в C ++, используя Winsock API. Я делаю чат с несколькими клиентами. Проблема, с которой я сталкиваюсь в своем коде, заключается в том, что мой сервер может отправить сообщение клиенту только один раз. Но я хочу, чтобы это происходило несколько раз. Я не могу поставить функцию accept () из бесконечного l oop в коде сервера. Я использовал select () для нескольких клиентов. Я делаю это без потоков.

Сервер:

#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");
     }

Код клиента:

  #include<iostream>
  #include<ws2tcpip.h>
  #include<string>
  using namespace std;
   #pragma comment(lib,"ws2_32.lib")
   void main() {
string ipAddress = "127.0.0.1";     //IP Address of the server
int port = 54000;                   //Listening port on the sever
//Initialize Winsock 
WSADATA data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
    cerr << " Can't initialize winsock " << endl;
    return;
}
//Create socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
    cerr << "Can't create a socket " << WSAGetLastError() << endl;
    closesocket(sock);
    WSACleanup();
    return;
}
//Fill in a hint structure
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);
//Connect to the server
int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
if (connResult == SOCKET_ERROR) {
    cerr << " Can't connect to the server " << WSAGetLastError() << endl;
    closesocket(sock);
    WSACleanup();
    return;
}
//Do-While loop to send and receive data
//char b[4096];
//int bytes = recv(sock,b,4096, 0);

//cout << string(b, 0, bytes) << endl;

char buff[4096];
string userInput;


do {
    //Prompt the user
    //cout << ">";
    //getline(cin, userInput);
    //Send the result
    //int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
    //if (sendResult != SOCKET_ERROR) {
    //ZeroMemory(buff, 0);
    int bytesrecieved = recv(sock, buff, 4096, 0);
    if (bytesrecieved > 0) {
    //Echo response to console
        cout << "SERVER> " << string(buff, 0, bytesrecieved) << endl;
    }
    //}
} while (true);
//Shut down everything
closesocket(sock);
WSACleanup();
  }

1 Ответ

0 голосов
/ 14 января 2020

РЕДАКТИРОВАТЬ:

Вы должны сделать некоторые изменения:

  1. Используйте timeval для выбора, чтобы избежать блокировки выбора (подождите, пока не будет установлено новое соединение или есть что почитать) .
  2. Переместить раздел чтения / отправки сообщения из для l oop.
  3. Отдельная обработка ввода ключа в другом потоке.
  4. Использование безопасной очереди для разделения ввода между потоком ввода и коммуникационным потоком (основной поток).

Вот пример:

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