提问者:小点点

Linux上的C/C++多线程服务器/客户端崩溃


我是一个初学者,我的任务是用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.....


共1个答案

匿名用户

在所示代码中重复出现相同的错误集,典型的例子是:

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和请求的字节数之间,指示实际发送和接收的字节数,您必须相应地处理结果。 如果写入的字节少于您尝试写入的字节,则必须再次尝试并写入剩余的未写入字节,直到写入整个消息(假设这是您的意图)。

您将不得不完全重新设计和重新实现您的客户机和服务器,从头开始,正确地实现从套接字读写所需的规则。 这比你想象的要多得多,但这就是你必须做的。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(linux|c|c++|多线程|服务器|客户端|崩溃)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?