我是一个初学者,我的任务是用C/C++为两个玩家编写一个多线程服务器/客户端游戏。
问题是,我的服务器在第一次客户端连接后就崩溃了。 客户端正在向服务器发送播放器名称。 服务器接收到它,然后它就崩溃了。
错误:终止调用而没有活动异常中止(核心转储)
为什么会撞车? 我的while循环应该接受2个客户端,然后从循环中跳出来。
我用
g++ -std=c++17 -o server simple_server_tcp.cpp -lpthread
g++ -std=c++17 -o client client_tcp.cpp
最初的游戏代码是用德语写的,但这并不是问题的一部分,所以不要介意。
这是我关于堆栈溢出的第一个问题-->; 请告诉我是不是我做错了什么。
以下是服务器代码
#include "header_file.h"
int count_on_clients_connections = 0;
void handle(int newsock, char *hello_server); //function for interaction with the client
void recieve_message_from_client(int newsock);
void send_message_to_client_from_server(int newsock);
void player_thread(int client_socket);
void client_thread_player(int client_socket);
void recieve_player_name(int client_sock);
int main(void)
{
char buffer[1024] = {0};
char data_sent[256];
char data_received[256];
int sock, bytes_send, bytes_received;
struct addrinfo hints, *res;
int reuseaddr = 1; /* True */
//function for testing --> sending message all five seconds;
void send_message_to_clients_im_still_here(int client_sock_first, int client_sock_second);
//socket_list_need_for connecting max 2 clients
int socket_list[2] = {0, 0};
//all the server thingys
/* Get the address info */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(NULL, PORT, &hints, &res) != 0)
{
perror("getaddrinfo");
return 1;
}
/* Create the socket */
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1)
{
perror("socket");
return 1;
}
/* Enable the socket to reuse the address */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1)
{
perror("setsockopt");
return 1;
}
/* Bind to the address */
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1)
{
perror("bind");
return 1;
}
/* Listen */
if (listen(sock, BACKLOG) == -1)
{
perror("listen");
return 1;
}
freeaddrinfo(res);
// all the server thingy end
cout << "Server is running" << endl
<< "Server is waiting for connection...." << endl
<< endl;
/* Main loop for connecting new clients */
while (1)
{
do
{
cout << count_on_clients_connections << endl;
socklen_t size = sizeof(struct sockaddr_in);
struct sockaddr_in their_addr;
int player_sock = accept(sock, (struct sockaddr *)&their_addr, &size);
//hanndling the connection from incoming client and starting it in a thread = player one
client_thread_player(player_sock);
cout << "a client connected.." << endl;
printf("Got a connection from %s on port %d\n", inet_ntoa(their_addr.sin_addr), htons(their_addr.sin_port));
count_on_clients_connections++;
cout << count_on_clients_connections << endl;
} while (count_on_clients_connections <= 2);
cout << "2 player Connected --> no more connection is allowed" << endl
<< "Now can the game start" << endl;
}
close(sock);
return 0;
}
void recieve_message_from_client(int newsock)
{
char message_from_client[256];
message_from_client[256] = recv(newsock, &message_from_client, sizeof(message_from_client), 0);
cout << message_from_client << endl;
}
void send_message_to_client_from_server(int newsock)
{
//message send from server
char hello_server[256] = "Hello from server";
send(newsock, &hello_server, sizeof(hello_server), 0);
}
//recievieng the name of the player client for the thread function
void recieve_player_name(int client_sock)
{
char player_name[256];
player_name[256] = recv(client_sock, &player_name, sizeof(player_name), 0);
cout << "Player connected: " << player_name << endl;
cout << "recieve player name was okay" << endl;
}
void player_thread(int client_socket)
{
//message recieved from client
char message_from_client[256];
message_from_client[256] = recv(client_socket, &message_from_client, sizeof(message_from_client), 0);
cout << "recieve from client was okay " << endl;
cout << message_from_client << endl;
send_message_to_client_from_server(client_socket);
cout << " send was okay " << endl;
recieve_player_name(client_socket);
cout << "player thread was finished!" << endl;
}
void client_thread_player(int client_socket)
{
//sending my client to a thread
std::thread player_client_thread(player_thread, client_socket);
//waiting the thread/player to complete his task.
// kann raus weil keine paralität player_client_one_thread.join();
//showing the connection
cout << "client thread player was okay !!!" << endl;
player_client_thread.join();
}
//the function handles all the client thingy in a thread
}
这是我的委托人
#include "header_file.h"
void send_player_name(int sock);
void recieve_function(int sock);
//all the game functions
void board_game(char vier_gewinnt_spielfeld[8][8]);
void column();
//all the game functions ends
int main(int argc, char const *argv[])
{
int sock = 0, bytes_send, bytes_received;
char data_sent[256];
char data_received[256];
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
string play_client_name;
//game_needed_thingys
//game_needed_thingys_end
// All the Client socket thingy
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT_CLIENT);
// Convert IPv4 and IPv6 addresses from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
// all the client socket thingy _finished
bool repeat = true;
while (1)
{
//how to send a message to server
char message_from_client[256] = "Hello from player client";
send(sock, &message_from_client, sizeof(message_from_client), 0);
/*How to recieve a message from server*/
char message_from_server[256];
message_from_server[256] = recv(sock, &message_from_server, sizeof(message_from_server), 0);
cout << message_from_server << endl;
//Input of a string from console , turned to array and send to server
send_player_name(sock);
cout << "end" << endl;
cout << " wait....." << endl;
sleep(7);
}
//ending connection
close(sock);
return (0);
}
//input of a name as string, convert to char array so we can send it to server;
void send_player_name(int sock)
{
string name;
cout << "Hi,please tell me the Name of the player ?" << endl;
cin >> name;
int n = name.length();
char char_name[n + 1];
strcpy(char_name, name.c_str());
//converting to char array complete
send(sock, &char_name, sizeof(char_name), 0);
cout << "Name has been sent " << endl;
//all the game thingy starts here
cout << "Its time to start the game for the player " << name << endl;
char vier_gewinnt_spielfeld[8][8];
for (int i = 0; i <= 7; i++) //feels the array vier_gewinnt with 0 chars
for (int o = 0; o <= 7; o++)
{
vier_gewinnt_spielfeld[i][o] = '0';
}
board_game(vier_gewinnt_spielfeld);
cout << "Make your turn !!!" << endl;
}
void recieve_function(int sock)
{
char message_from_server[256];
message_from_server[256] = recv(sock, &message_from_server, sizeof(message_from_server), 0);
usleep(500000);
cout << message_from_server << endl;
}
这是我的标题
#pragma once //header guard so not the same thing is reused !!!
//include server
#include <stdio.h>
#include <string.h> /* memset() */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <iostream>
#include <thread>
#include <unistd.h> //usleep
//include the header file where the game is located
using namespace std;
#define PORT "32001" /* Port to listen on */
#define PORT_CLIENT 32001
#define BACKLOG 10 /* Passed to listen() */
//client
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <iostream>
#include <bits/stdc++.h>
#include <array>
#include <vector>
#include <unistd.h> //usleep
//all game needed defines
#define RED "\033[31m" /* Red */
#define BLUE "\033[34m" /* Blue */
#define YELLOW "\033[33m" /* Yellow */
#define RESET "\033[0m" /*Reset of the used color*/
#define OVERLINE "\u203e"
#define black_square "\u25A2"
using namespace std;
//include the game file
//#include "vier_gewinnt.cpp"
//double main --> exlude vier_gewinnt ath the moment :D
//game function
void player_name_creation(string &player_1, string &player_2);
void column();
void board_game(char vier_gewinnt_spielfeld[8][8]); //includes column()
int turn_player(string player, char vier_gewinnt_spielfeld[8][8], int &curr_row, int &curr_column);
void unvalid_player_turn(char vier_gewinnt_spielfeld[8][8], int temp, int zeile);
int check_user_input(int Value, char board[8][8]);
int get_row(int curr_column, char Board[8][8]);
int search_four(char gamer, char Grid[8][8]); //winning condition
//server function
void handle(int newsock, char *hello_server); //function for interaction with the client
void recieve_message_from_client(int newsock);
void send_message_to_client_from_server(int newsock);
void player_thread(int client_socket);
void client_thread_player(int client_socket);
void recieve_player_name(int client_sock);
//client_functions
void send_player_name(int sock);
void recieve_function(int sock);
//all the game functions
int search_four(char gamer, char Grid[8][8])
{
int i, j;
// Suche waagrecht
for (i = 0; i < 8; i++)
for (j = 0; j < 8 - 3; j++)
if (Grid[i][j] == gamer && Grid[i][j + 1] == gamer &&
Grid[i][j + 2] == gamer && Grid[i][j + 3] == gamer)
{
//mark_four(i, j, 0, +1);
return 1;
}
// Suche senkrecht
for (j = 0; j < 8; j++)
for (i = 0; i < 8 - 3; i++)
if (Grid[i][j] == gamer && Grid[i + 1][j] == gamer &&
Grid[i + 2][j] == gamer && Grid[i + 3][j] == gamer)
{
//mark_four(i, j, +1, 0);
return 1;
}
// Suche diagonal '\'
for (i = 0; i < 8 - 3; i++)
for (j = 0; j < 8 - 3; j++)
if (Grid[i][j] == gamer && Grid[i + 1][j + 1] == gamer &&
Grid[i + 2][j + 2] == gamer && Grid[i + 3][j + 3] == gamer)
{
//mark_four(i, j, +1, +1);
return 1;
}
// Suche diagonal '/'
for (i = 0; i < 8 - 3; i++)
for (j = 8 - 4; j < 8; j++)
if (Grid[i][j] == gamer && Grid[i + 1][j - 1] == gamer &&
Grid[i + 2][j - 2] == gamer && Grid[i + 3][j - 3] == gamer)
{
//mark_four(i, j, +1, -1);
return 1;
}
return 0;
}
void player_name_creation(string &player_1, string &player_2) // creates the name of player 1 and player 2
{
cout << " 4 Gewinnt wird zu 2 geschpielt" << endl;
cout << "Wie heißt der erste Spieler --> Bitte namen eingeben" << endl;
cin >> player_1;
cout << "Wie heißt der zweite Spieler --> Bitte namen eingeben" << endl;
cin >> player_2;
cout << "Der Spieler: " << player_1 << RED << " vs" << RESET << " " << player_2 << " den zweiten Spieler: " << endl;
return;
}
int turn_player(string player, char vier_gewinnt_spielfeld[8][8], int &curr_row, int &curr_column) // Ein Spielerzug mit der rückgabe der spalte
{
int input_player_column;
cout << "Der Spieler: " << player << " ist dran" << endl
<< "Bitte waehlen sie die Spalte[1-8] aus, wo sie den Stein reinwerfen wollen" << endl; // text abändern
//cin >> input_player_column;
while (!(cin >> input_player_column))
{
cin.clear();
cin.ignore(__INT_MAX__, '\n');
cout << "Invalid input! Try again: ";
}
input_player_column--;
curr_column = check_user_input(input_player_column, vier_gewinnt_spielfeld);
curr_row = get_row(curr_column, vier_gewinnt_spielfeld);
while (curr_row == 10)
{
cout << "WRONG --> Ungueltiger Spielerzug --> Die Spalte ist voll --> Waehle eine andere Spalte" << endl;
turn_player(player, vier_gewinnt_spielfeld, curr_row, curr_column);
}
}
int check_user_input(int Value, char board[8][8])
{
while ((Value < 0) || (Value > 7))
{
cout << RED << "Falsche eingabe, probieren sie es erneut --> es darf die zahl nur zwischen " << YELLOW << "[1-8]" << RED << " eingegeben werden " << RESET << endl;
board_game(board);
cin >> Value;
Value--;
}
return Value;
}
int get_row(int curr_column, char board[8][8])
{
for (int i = 7; i >= 0; i--)
{
if (board[i][curr_column] == '0')
{
return i;
}
}
return 10; // default value
}
void column()
{
int reihe_anzeige[8] = {1, 2, 3, 4, 5, 6, 7, 8}; //shows column number
cout << BLUE << "|" << RESET;
for (int i = 0; i <= 7; i++) //Gibt die Spalten an
{
cout << reihe_anzeige[i] << BLUE << "|" << RESET;
}
cout << endl;
for (int w = 0; w <= 16; w++)
cout << BLUE << "_" << RESET;
cout << endl;
return;
}
void board_game(char vier_gewinnt_spielfeld[8][8]) //creates the board table
{
column(); //displays the column of the game board
for (int i = 0; i <= 7; i++) //gibt das spielbrett aus
{
cout << BLUE << "|" << RESET;
for (int o = 0; o <= 7; o++) //colores the output of the array for a better visual option
{
if (vier_gewinnt_spielfeld[i][o] == 'x')
{
cout << RED << vier_gewinnt_spielfeld[i][o] << RESET << BLUE << "|" << RESET;
}
if (vier_gewinnt_spielfeld[i][o] == '0')
{
cout << vier_gewinnt_spielfeld[i][o] << BLUE << "|" << RESET;
}
if (vier_gewinnt_spielfeld[i][o] == 'v')
{
cout << YELLOW << vier_gewinnt_spielfeld[i][o] << RESET << BLUE << "|" << RESET;
}
if (o == 7)
{
cout << endl;
}
}
}
for (int w = 0; w <= 16; w++) // Gibt den Boden des Spielbrettes aus
cout << BLUE << OVERLINE << RESET;
cout << endl;
}
运行日志:服务器
./server
Server is running
Server is waiting for connection....
0
client thread player was okay !!!
recieve from client was okay
Hello from player client
send was okay
Player connected: Klaus
recieve player name was okay
player thread was finished!
Segmentation fault (core dumped)
客户
./client
Hello from server
Hi,please tell me the Name of the player ?
Klaus
Name has been sent
Its time to start the game for the player Klaus
|1|2|3|4|5|6|7|8|
_________________
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
|0|0|0|0|0|0|0|0|
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Make your turn !!!
end
wait.....
在所示代码中重复出现相同的错误集,典型的例子是:
char message_from_client[256];
message_from_client[256] = recv(newsock, &message_from_client,
sizeof(message_from_client), 0);
cout << message_from_client << endl;
没有“message_from_client[256]
”。 根本不存在。 message_from_client
是由256个值组成的数组,因此有效值是message_from_client[0]
到message_from_client[255]
。 给第256个不存在的值赋值会导致内存损坏,这可能是崩溃的原因。 现代C++编译器实际上能够检测到这种常见的bug。 如果您在编译所示代码时收到来自编译器的警告消息,这将是一个教训,即使编译器仍然编译所示代码,也不要忽略来自编译器的诊断消息。 报告这些诊断消息总是有原因的,并且指出编译代码中可能存在的缺陷和bug,即使它没有违反C++语法的正式规则。
但这不是唯一的问题。 这段代码显然假定它将始终从套接字读取256字节。 这不是真的。 流套接字不是这样工作的。 即使连接的另一端发送了256字节,也不能保证所有256字节都是recv()
ed。 第一个调用recv()
可能返回1,再次调用recv()
将读取剩余的255字节。 那只是个例子。 或者每次调用recv()
时,它读取1个字节,您必须反复调用它,直到接收到整个消息。 你不能做那样的假设。
类似地,当所示代码试图向套接字发送256字节时,所示代码假定所有256字节都被发送。 这也不是事实:
send(sock, &message_from_client, sizeof(message_from_client), 0);
这假定发送了整个消息。 那是不能保证的。 send()
和recv()
都返回发送或接收的实际字节数,可以介于0和请求的字节数之间,指示实际发送和接收的字节数,您必须相应地处理结果。 如果写入的字节少于您尝试写入的字节,则必须再次尝试并写入剩余的未写入字节,直到写入整个消息(假设这是您的意图)。
您将不得不完全重新设计和重新实现您的客户机和服务器,从头开始,正确地实现从套接字读写所需的规则。 这比你想象的要多得多,但这就是你必须做的。