Problem
I got a segmentation fault when I submitted the input box after I created the Interface class. Before creating the class, I was using several functions in my main.cpp
file (now the functions in the Interface class). After a lot of debugging, I traced the problem back to Client::Send
, but I cannot find the source of the problem. I have a lot of error catchers in my code, but none are triggered when I run the code.
Relevant Files
src/main.cpp
src/client.cpp
inc/client/client.h
Project Structure
bin/
obj/
inc/
interface/
interface.h
server/
server.h
client/
client.h
lib/
src/
main.cpp
interface.cpp
server.cpp
client.cpp
Makefile
inc/interface/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
inc/server/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
inc/client/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
src/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;
}
src/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_++;
}
src/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;
}
src/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] = '';
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)
I tried putting breakpoints in the Client::Send
function and the Client::~Client
function to see if the client was being deleted before the Interface could send a message, but this was not the case. I checked socket- and class-related variables to see if any were NULL
or -1
, but this was also not the case.
BitPigeon is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.