Я работаю над клиент-серверным приложением для своего класса операционных систем, которое должно имитировать продажи авиабилетов.Мы должны сделать так, чтобы основной поток на TCP-сервере прослушивал входящие соединения, а затем, как только мы получили клиентское соединение, создал новый поток для обработки этого соединения.После большого начального замешательства, я считаю, что у меня есть программа в в основном -функционирующем состоянии.
Проблема, с которой я сейчас сталкиваюсь, заключается в том, что когда я запускаю все клиенты из отдельных терминалов(будь то 2 или 5 или любое другое число), все выходные данные сервера поступают в самый последний терминал, на котором я его запустил.Само по себе это не так уж сложно, но это также означает, что когда я использую Ctrl + C, чтобы закрыть процесс, запущенный на этом последнем терминале, он выходит из всех клиентов с сервера (что является проблемой).
Итак, мои вопросы: 1. Почему все выходные данные сервера направляются на один терминал, а не отправляют ответы на терминал, с которого был запущен каждый клиентский процесс?2. Почему все клиенты выходят, как только я заканчиваю процесс в терминале 5?
Изображение терминалов для всех клиентов и сервера (возможно, придется открыть в новой вкладке, чтобы увидеть все).![Client-server terminals](https://i.imgur.com/muJIbIA.jpg)
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;
}
Любая помощь приветствуется.