Ошибка сегментации в программе сокетов

Проблема

У меня возникла ошибка сегментации, когда я отправил поле ввода после создания класса интерфейса. Перед созданием класса я использовал несколько функций в своем файле main.cpp (теперь это функции класса Interface). После долгих отладок я проследил проблему до Client::Send, но не могу найти источник проблемы. В моем коде много ловушек ошибок, но ни один из них не срабатывает при запуске кода.

Соответствующие файлы

  • src/main.cpp
  • src/client.cpp
  • inc/client/client.h

Структура проекта

bin/
obj/
inc/
  interface/
    interface.h
  server/
    server.h
  client/
    client.h
lib/
src/
  main.cpp
  interface.cpp
  server.cpp
  client.cpp

Makefile

вкл/интерфейс/interface.h


#ifndef INTERFACE_H_
#define INTERFACE_H_

#include <thread>

#include <ncurses.h>

class Client;
class Server;

class Interface {
 public:
  Interface(Client *client) : client_(client) {
    initscr();
    cbreak();
    echo();
    output_ = newwin(LINES - 3, COLS, 0, 0);
    input_ = newwin(3, COLS, LINES - 3, 0);
    std::thread input_thread([this]() { this->Input(); });
    input_thread.join();
  }
  ~Interface() {
    endwin();
  }
  void Draw();
  void Input();
  void Output(const char *);
 private:
  WINDOW *output_;
  WINDOW *input_;
  int output_lines_;
  Client *client_;
};

#endif

вк/сервер/server.h

#ifndef SERVER_H_
#define SERVER_H_

#include <thread>
#include <vector>

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

class Server {
 public:
  Server(const in_addr_t addr, const in_port_t port) {
    sock_ = socket(AF_INET, SOCK_STREAM, 0);
    addr_.sin_family = AF_INET;
    addr_.sin_addr.s_addr = addr;
    addr_.sin_port = port;
    bind(sock_, (const sockaddr *) &addr_, sizeof(addr_));
    std::thread accept_thread([this]() { this->Accept(); });
    accept_thread.detach();
  }
  ~Server() {
    close(sock_);
  }
  void Accept();
  void Receive(const int) const;
  void Send(const int, const char *) const;
  int GetAddress() const;
  int GetPort() const;

 private:
  int sock_;
  sockaddr_in addr_;
  std::vector<int> clients_;
};

#endif

вк/клиент/client.h


#ifndef CLIENT_H_
#define CLIENT_H_

#include <thread>
#include <chrono>
#include <vector>
#include <iostream>

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ncurses.h>

class Interface;

class Client {
 public:
  Client(const in_addr_t addr, const in_port_t port, Interface *interface) : interface_(interface) {
    sock_ = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_ == -1) {
      std::cout << "Failed to initialize socket."<< "\n";
      exit(-1);
    }
    addr_.sin_family = AF_INET;
    addr_.sin_addr.s_addr = addr;
    addr_.sin_port = port;
    int attempt = 0;
    if (connect(sock_, (sockaddr *) &addr_, sizeof(addr_))== -1) {
      std::cerr << "Failed to connect to server." << "\n";
      exit(-1);
    }
    std::thread receive_thread(std::thread([this]() { this->Receive(); }));
    receive_thread.detach();
  }
  ~Client() {
    std::cout << "Client deleted." << "\n";
    close(sock_);
  }
  void Receive();
  void Send(const char *) const;

 private:
  int sock_;
  sockaddr_in addr_;
  Interface *interface_;
};

#endif

источник/main.cpp


#include <thread>
#include <chrono>
#include <iostream>
#include <string>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <ncurses.h>

#include "interface/interface.h"
#include "server/server.h"
#include "client/client.h"

void Create(in_addr_t addr, in_port_t port, Server*& server, Client*& client, Interface *interface) {
  server = new Server(addr, port);
  std::this_thread::sleep_for(std::chrono::seconds(1));
  client = new Client(addr, port, interface);
}

void Join(in_addr_t addr, in_port_t port, Client*& client, Interface *interface) {
  client = new Client(addr, port, interface);
}

int main(int argc, char **argv) {
  const in_addr_t kAddr = inet_addr("127.0.0.1");
  const in_port_t kPort = htons(8080);
  Server *server = nullptr;
  Client *client = nullptr;
  Interface *interface = new Interface(client);
  Create(kAddr, kPort, server, client, interface);
  return 0;
}

источник/interface.cpp


#include <ncurses.h>

#include "client/client.h"

#include "interface/interface.h"

void Interface::Draw() {
  wrefresh(input_);
  werase(input_);
  box(input_, 0, 0);
}

void Interface::Input() {
  while (true) {
    Draw();
    char message[1024];
    wgetstr(input_, message);
    client_->Send(message); // I do believe I've traced the error to here.
  }
}

void Interface::Output(const char *message) {
  box(output_, 0, 0);
  wmove(output_, output_lines_, 1);
  wprintw(output_, message);
  wrefresh(output_);
  wmove(input_, 1, 1);
  wrefresh(input_);
  output_lines_++;
}

источник/server.cpp|


#include <thread>
#include <unordered_map>
#include <cstring>

#include <sys/socket.h>

#include "server/server.h"

void Server::Accept()  {
  while(true) {
    listen(sock_, 5);
    int client_sock = accept(sock_, nullptr, nullptr);
    clients_.push_back(client_sock);
    std::thread receive_thread([this, client_sock]() { this->Receive(client_sock); });
    receive_thread.detach();
  }
}

void Server::Receive(const int client_sock) const {
  while (true) {
    char buffer[1024] = {0};
    int received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (received <= 0) {
      break;
    }
    for (int client : clients_) {
      Send(client, buffer);
    }
  }
}

void Server::Send(const int client_sock, const char *message) const {
  send(client_sock, message, strlen(message), 0);
}

int Server::GetAddress() const {
  return addr_.sin_addr.s_addr;
}

int Server::GetPort() const {
  return addr_.sin_port;
}

источник/client.cpp

#include <cstring>
#include <iostream>

#include <sys/socket.h>

#include "interface/interface.h"

#include "client/client.h"

void Client::Receive() {
  while (true) {
    char buffer[1024] = {0};
    int received = recv(sock_, buffer, sizeof(buffer), 0);
    if (received <= 0) {
      break;
    }
    buffer[received] = '\0';
    if (interface_ != nullptr) {
      interface_->Output(buffer);
    } else {
      std::cerr << "Interface is `NULL`." << "\n";
      exit(-1);
    }
  }
}

void Client::Send(const char *message) const {
  std::cout << "Message sent." << "\n";
  if (send(sock_, message, strlen(message), 0) == -1) {
    std::cerr << "Message failed to send." << "\n";
    exit(-1);
  }
}

Makefile

CC := g++
LIB := -lncurses
INC := -Iinc
SRC := $(wildcard src/*.cpp)
OBJ := $(patsubst src/%.cpp, obj/%.o, $(SRC))
BIN := bin/main

.PHONY: all clean

all: $(BIN)

$(BIN): $(OBJ)
    $(CC) $^ $(INC) $(LIB) -o $@

obj/%.o: src/%.cpp
    $(CC) -c $< $(INC) -o $@

clean:
    rm -rf $(OBJ) $(BIN)

Я попытался поставить точки останова в функции Client::Send и функции Client::~Client, чтобы увидеть, удаляется ли клиент до того, как интерфейс сможет отправить сообщение, но это не тот случай. Я проверил переменные, связанные с сокетами и классами, чтобы увидеть, есть ли среди них NULL или -1, но это тоже было не так.

Вы запускали это в отладчике, чтобы точно узнать, где возникает ошибка сегментации?

Tim Roberts 18.06.2024 06:51

В будущем не пишите большие куски кода и уж точно не целые программы без какого-либо тестирования. В основном начните с пустой main функции, создайте код с большим количеством дополнительных предупреждений, включенных и рассматриваемых как ошибки (я рекомендую как минимум -Wall -Wextra -Werror ). Когда код будет построен правильно, протестируйте программу всеми возможными способами. Только когда все работает, вы продолжаете добавлять совсем небольшой кусочек кода, возможно, даже одну-две строчки. Так станет намного проще находить ошибки, отлаживать и исправлять их.

Some programmer dude 18.06.2024 07:09

Мало или вообще ничего общего с классом , ой или системой. Не отмечайте без разбора.

user207421 18.06.2024 07:36

Я здесь новичок ~ следует ли мне воздерживаться от использования больших кусков кода, даже если я считаю, что это тоже уместно? Например, в моем примере ошибка сегментации на самом деле имела мало общего с функцией Client::Send(char const *), хотя мой отладчик сообщил, что она вызвала ее.

unstatistical 19.06.2024 01:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вероятная причина вашей проблемы - эти три строки:

Client *client = nullptr;
Interface *interface = new Interface(client);
Create(kAddr, kPort, server, client, interface);

Проблема в том, что конструктор Interface копирует указатель, который вы ему передаете. Это означает, что interface->client_ будет нулевым указателем, независимо от того, что делает функция Create.

Но поскольку объект Interfaceconstructor depends on aClientobject, and theClientobject also depends on theInterface существует, у вас возникает невозможный цикл зависимостей, который необходимо исправить.

Возможно, вы захотите переосмыслить свой дизайн, чтобы решить проблему зависимостей, а также проблему указателя и сбоя (моя рекомендация). Или измените свой код на какую-то двухфазную конструкцию, где объект Interface может быть создан без объекта Client, и вы инициализируете объект Interface после создания объекта Client.


Гораздо более общий и лучший способ решения проблем с указателями — вообще не использовать указатели.

В вашем коде я не вижу никакой необходимости в том, чтобы server, client или interface были указателями и создавались с помощью new.

Другие вопросы по теме