【问题标题】:C/C++ Multithreaded server/client on Linux crashesLinux 上的 C/C++ 多线程服务器/客户端崩溃
【发布时间】:2020-10-04 20:36:39
【问题描述】:

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

【问题讨论】:

  • 它是如何崩溃的?您是否收到错误消息或核心转储?请添加服务器的控制台输出。
  • 请显示完整的运行日志。调试打印对任何查看问题的人都有帮助。
  • 我更新了我的运行日志
  • 头文件中有函数定义的任何具体原因?
  • 不,不是,这是我此时的第一个头文件 :D --> 需要重做这部分

标签: c++ c linux sockets networking


【解决方案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++ 编译器实际上能够检测到这种常见错误。如果您在编译显示的代码时收到来自编译器的警告消息,这将是一个教训,永远不要忽略来自编译器的诊断消息,即使它仍然编译显示的代码。报告这些诊断消息总是有原因的,并且表明编译代码中可能存在缺陷和错误,即使它不违反 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 和请求的字节数之间,表示实际发送和接收的字节数,您必须相应地处理结果.如果写入的字节数少于您尝试写入的字节数,则必须重试并写入剩余的未写入字节,直到写入整个消息(假设这是您的意图)。

您必须从头开始完全重新设计和重新实现您的客户端和服务器,正确实施从/向套接字读取和写入所需的规则。这比您预期的要多得多,但必须这样做。

【讨论】:

  • 发送(sock, &message_from_client, sizeof(message_from_client), 0); &message_from_client 是数组存储的地址:它应该是 send(sock, message_from_client, sizeof(message_from_client), 0);
  • 首先,感谢您的反馈。我在服务器和客户端之间的通信方面遇到了困难。这是我第一次写这样的东西。我想我必须定义一个可以在服务器和客户端之间发送的缓冲区。我会尝试再做一次。我真的很感谢你的例子
  • var = recv(...) 中,var 的类型必须是 ssize_t,而不是 char。例如,请参阅beej.us/guide/bgnet
猜你喜欢
  • 2019-05-17
  • 1970-01-01
  • 2015-01-09
  • 2019-04-18
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
相关资源
最近更新 更多