gpt4 book ai didi

c++ - 为什么不同客户端的服务器输出会转到同一个终端?

转载 作者:行者123 更新时间:2023-11-28 01:24:10 25 4
gpt4 key购买 nike

我正在为我的操作系统类开发客户端-服务器应用程序,该应用程序应该模拟飞机票的销售。我们被指示让 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;
}

客户端.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;
}

非常感谢任何帮助。

最佳答案

问题是您使用了全局变量。

您的连接线程将响应写入clientSocket,您的main 随每次连接而改变。每个线程都将写入同一个套接字。

您需要创建一个类来保存特定于每个连接的数据,并将其中的一个新数据传递给每个线程。 不要使用共享的全局数据来保存特定于线程的值。

关于c++ - 为什么不同客户端的服务器输出会转到同一个终端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54729299/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com