Почему вывод сервера для отдельных клиентов идет на один и тот же терминал? - PullRequest
0 голосов
/ 17 февраля 2019

Я работаю над клиент-серверным приложением для своего класса операционных систем, которое должно имитировать продажи авиабилетов.Мы должны сделать так, чтобы основной поток на TCP-сервере прослушивал входящие соединения, а затем, как только мы получили клиентское соединение, создал новый поток для обработки этого соединения.После большого начального замешательства, я считаю, что у меня есть программа в в основном -функционирующем состоянии.

Проблема, с которой я сейчас сталкиваюсь, заключается в том, что когда я запускаю все клиенты из отдельных терминалов(будь то 2 или 5 или любое другое число), все выходные данные сервера поступают в самый последний терминал, на котором я его запустил.Само по себе это не так уж сложно, но это также означает, что когда я использую Ctrl + C, чтобы закрыть процесс, запущенный на этом последнем терминале, он выходит из всех клиентов с сервера (что является проблемой).

Итак, мои вопросы: 1. Почему все выходные данные сервера направляются на один терминал, а не отправляют ответы на терминал, с которого был запущен каждый клиентский процесс?2. Почему все клиенты выходят, как только я заканчиваю процесс в терминале 5?

Изображение терминалов для всех клиентов и сервера (возможно, придется открыть в новой вкладке, чтобы увидеть все).Client-server terminals

Server.cpp (Необходим мой другой класс Plane.cpp для компиляции, который я могу предоставить при необходимости, но я не думаю, что какой-либо код там имеет отношение к проблеме, с которой я сталкиваюсь):

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <pthread.h>

#include "Plane.h"

using namespace std;

// default plane sizing
const int DEFAULT_ROWS = 26;
const int DEFAULT_COLUMNS = 6;
// Set up global variables for threads to access (yikes)
int rows, cols;
Plane* plane;
pthread_mutex_t mutexL = PTHREAD_MUTEX_INITIALIZER;
static int clientSocket;
int connections = 0;

void *connection_handler(void*);

struct argList {
    string arg;
    int row, col;
};

bool argParser(string input, argList &argL) {
    stringstream ss;

    ss << input;
    try {
        ss >> argL.arg >> argL.row >> argL.col;
    } catch (exception e) {
        cout << "Invalid arguments\n";
        return false;
    }
    return true;
}

string purchaseTicket(int row, int col) {
    string output;

    // lock this section before we use shared resource
    pthread_mutex_lock(&mutexL);
    cout << "Mutex locked\n";
    if (plane->isAvailable(row, col)) {
        plane->buyTicket(row, col);

        output = "Successfully purchased ticket for row: " + to_string(row) + ", column: " + to_string(col) + "\n";
    } else {
        if (row > plane->getNumRows() || row < 0 || col > plane->getNumCols() || col < 0) {
            output = "Invalid seat location!\n";
        } else {
            output = "Seat unavailable!\n";
        }
    }
    pthread_mutex_unlock(&mutexL);
    cout << "Mutex unlocked\n";
    // unlock when we're done
    return output;
}

string convertMatrix(Plane plane) {
    char** tempMatrix = plane.getSeatMatrix();

    string seats = "";
    for (int i = 0; i < plane.getNumRows(); i++) {
        seats += tempMatrix[i];
        seats += "\n";
    }

    return seats;
}

// arguments to run: column row
int main(int argc, char* argv[]) {
    // array of threads (thread pool)
    pthread_t threads[5];

    if (argc < 3) {
        rows = DEFAULT_ROWS;
        cols = DEFAULT_COLUMNS;
        plane = new Plane(rows, cols);
    } else if (argc == 3) {
        rows = atoi(argv[1]);
        cols = atoi(argv[2]);
        plane = new Plane(rows, cols);
    } else {
        cout << "Only 2 arguments allowed. You entered [" << argc << "]\n";
        return -1;
    }

    // Create socket
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listen_sock == -1) {
        cerr << "Failed to create socket\n";
        return -1;      
    }

    // Socket hint stuff
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(54000);
    inet_pton(AF_INET, "0.0.0.0", &hint.sin_addr);

    // Bind socket to IP and port
    if (bind(listen_sock, (sockaddr*)&hint, sizeof(hint)) < 0) {
        cerr << "Binding to IP/Port failed\n";
        return -2;
    }

    // Mark the socket for listening
    if (listen(listen_sock, SOMAXCONN) == -1) {
        cerr << "Can't listen";
        return -3;
    }

    char host[NI_MAXHOST];
    char service[NI_MAXSERV];

    int numThread = 0;
    while (numThread < 5) {
        cout << "Listening for connections...\n";

        sockaddr_in client;
        socklen_t clientSize = sizeof(client);
        // accept connections
        clientSocket = accept(listen_sock, (sockaddr*)&client, &clientSize);

        // if connection failed
        if (clientSocket == -1) {
            cerr << "Failed to connect with client";
            return -4;
        } else {
            cout << "Connection successful\n";
            connections++;
        }

        pthread_create(&threads[numThread], NULL, connection_handler, (void*) &clientSocket);

        // 0 out used memory
        memset(host, 0, NI_MAXHOST);
        memset(service, 0, NI_MAXSERV);
        int result = getnameinfo((sockaddr*)&client, 
                                sizeof(client), 
                                host, 
                                NI_MAXHOST, 
                                service,
                                NI_MAXSERV,
                                0);    
        if (result) {
            cout << host << " connected on " << service << endl;
        } else {
            inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
            cout << host << " connected on " << ntohs(client.sin_port) << endl;
        }

        numThread++;
    }

    // join threads together
    for (int i = 0; i < numThread; i++) {
        pthread_join(threads[i], NULL);
    }



    return 0;
}

void *connection_handler(void* listen_sock) {
    cout << "Thread No: " << pthread_self() << "\n-----\n";

    const int clientID = connections;
    // necessary variables for processing
    char buff[4096];
    string custMsg;

    custMsg += to_string(rows) + " " + to_string(cols) + "\n";
    int msgSize = strlen(custMsg.c_str())*sizeof(char);
    send(clientSocket, custMsg.c_str(), msgSize+1, 0);

    // Determine what we do when we receieve messages
    bool firstMsg = true;
    while (true) {
        memset(buff, 0, 4096);
        custMsg = "";


        int bytesRecv = recv(clientSocket, buff, 4096, 0);

        if (bytesRecv == -1) {
            pthread_mutex_lock(&mutexL);
            cerr << "There was a connection issue (client " << clientID << ")\n";
            pthread_mutex_unlock(&mutexL);
            break;
        } else if (bytesRecv == 0) {
            pthread_mutex_lock(&mutexL);
            cout << "Client " << clientID << " disconnected" << endl;
            pthread_mutex_unlock(&mutexL);
        }


        if (bytesRecv > 0)
            cout << "Received: " << string(buff, 0, bytesRecv) << " (client " << clientID << ")\n";

        // do things based on user input
        string inputStr(buff);
        argList args;
        if (argParser(inputStr, args)) {
            if (args.arg == "buy") {
                string purchResult = purchaseTicket(args.row, args.col);
                custMsg += purchResult;
                cout << purchResult << "------\n";
            } else {
                custMsg = "To buy a ticket, enter: 'buy <row> <col>'\n";
            }
        } else {
            custMsg = "Invalid argument list";
        }

        //custMsg += convertMatrix(*plane);
        int msgSize = strlen(custMsg.c_str())*sizeof(char);
        //cout << custMsg << "\n";
        cout << "Responding to client: " << clientID << "\n";
        send(clientSocket, custMsg.c_str(), msgSize+1, 0);
    }
    // Close socket
    close(clientSocket);
    return 0;
}

Client.cpp:

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <time.h>

using namespace std;

struct serverInfo {
    string ipAddr;
    int portNum;
    int timeout;
};

int getRand(int max) {

    return rand() % max;
}

bool getPlaneInfo(string line, int& rows, int& cols) {
    stringstream ss;
    ss << line;
    try {
        ss >> rows >> cols;
        return true;
    } catch (exception e) {
        cout << "Critical error\n";
        return false;
    }
}

void getServerInfo(ifstream &serverCfg, serverInfo &conn_serv) {
    // variables that we'll read into
    string label, val, eq;
    int i = 0;
    try { // for conversion errors
        while (serverCfg >> label >> eq >> val) {
            if (i == 0)
                conn_serv.ipAddr = val;
            else if (i == 1)
                conn_serv.portNum = stoi(val);
            else if (i == 2)
                conn_serv.timeout = stoi(val);
            else
                break;
            i++;
        }
    } catch (exception e) {
        e.what();
    }
}

// arguments being sent in should be 'automatic' or 'manual' for method of purchasing
// followed by the .ini file containing the server connection info.
int main(int argc, char* argv[]) {
    srand(time(NULL));
    // we get these int variables from the first server response
    int rows, cols;

    bool AUTOMATIC = false;

    // make sure arguments are present and valid
    if (argc != 3) {
        cout << "Invalid number of arguments. Exiting...\n";
    }
    if (strncmp(argv[1],"automatic", 9) != 0 && strncmp(argv[1],"manual", 6) != 0) {
        cout << "Invlaid arguments! Please use 'manual' or 'automatic'. Exiting...\n";
        return -1;
    }

    // check to see if they want automatic ticket purchasing
    if (strncmp(argv[1], "automatic", 9) == 0) {
        AUTOMATIC = true;
    }

    // Handle file processing in getServerInfo function
    string fileName = argv[2];
    ifstream SERVER_CFG;
    SERVER_CFG.open(fileName);

    // store values from file in conn_info
    serverInfo conn_info;
    if(SERVER_CFG) {
        getServerInfo(SERVER_CFG, conn_info);
    } else {
        cout << "Invalid filename. Exiting...\n";
        return -2;
    }
    SERVER_CFG.close();

    // create socket
    int conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (conn_sock < 0) {
        cout << "\nFailed to Create Socket. Exiting...\n";
        return -3;
    }

    // get port and ipAddr information that we read from file
    int port = conn_info.portNum;
    string ipAddr = conn_info.ipAddr;

    // make hint
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(port);
    inet_pton(AF_INET, ipAddr.c_str(), &hint.sin_addr);

    // try to connect to server socket i times where i is conn_info.timeout
    for (int i = 0; i < conn_info.timeout; i++) {
        int connectVal = connect(conn_sock, (sockaddr*) &hint, sizeof(sockaddr_in));
        if (connectVal < 0 && i >= conn_info.timeout-1) {
            cout << "Failed to connect (" << (i+1) << ")\n";
            cout << "Failed to connect after " << (i+1) << " attempts. Exiting.\n";
            return -4;
        } else if (connectVal == 0) {
            break;
        }
        cout << "Failed to connect (" << (i+1) << ")\n";
        sleep(1);
    }



    char buff[4096];
    string userInput;
    bool firstMsg = true;
    bool needGreet = true;
    do {
        userInput = "";

        int sendResult;

        // Send a greeting message to the server to get plane info
        if (needGreet) {
            userInput = "Greeting the server";
            send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);
            needGreet = false;
            continue;
        }

        if (AUTOMATIC && !firstMsg) {
            int row = getRand(20);
            int col = getRand(6);
            userInput = string("buy ") + to_string(row) + " " + to_string(col);
            cout << "Sending request to buy seat " << row << " " << col << "\n";
            sleep(1);
        } else { // get input if its manual
            if (!firstMsg) {
                cout << "> ";
                getline(cin, userInput);
            }
        }

        // send to server
        sendResult = send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);

        // check if sent successfully
        if (sendResult < 0) { // connection error
            cout << "Failed to send to server\r\n";
            continue;
        }
        // wait for response
        memset(buff, 0, 4096);
        int bytesReceived = recv(conn_sock, buff, 4096, 0);

        // print response
        cout << "Server> " << string(buff, bytesReceived) << "\r\n";
        if (firstMsg) {
            string planeInf(string(buff,bytesReceived));
            if (getPlaneInfo(planeInf, rows, cols)) {
                cout << "Rows: " << rows << ", Columns: " << cols << "\n";
            } else {
                return -5;
            }
            firstMsg = false;
        }

    } while (true);

    // closing socket
    close(conn_sock);
    return 0;
}

Любая помощь приветствуется.

1 Ответ

0 голосов
/ 17 февраля 2019

Проблема в том, что вы используете глобальные переменные.

Ваш поток соединений записывает ответ на clientSocket, который ваш main меняется при каждом соединении.Каждый поток будет записывать в один и тот же сокет.

Вам необходимо создать класс для хранения данных, специфичных для каждого соединения, и передать новый из них каждому потоку. не использовать общие глобальные данные для хранения значений, специфичных для потока.

...