I'm handling incoming connections to a socket in separate std::thread for each client connection. So when trying to do a read() from the socket, the program crashes.
std::thread in_conn_th(handle_new_connection, in_socket); // <-- creating a new thread and passing the handle_new_connection function into the thread with the socket descriptor param
Here is the description of handle_new_connection()
waiterr::operation_codes waiterr::Waiter::handle_new_connection(int incoming_socket) {
std::cout << "Here comes " << incoming_socket << "\n";
char buffer[30000] = {0};
int val_read = read(incoming_socket, buffer, 30000); // <-- Error
std::cout << "Here comes 2\n";
std::cout << buffer << std::endl << std::endl;
write(incoming_socket, "Some response", 13);
std::cout << "* Msg sent *\n";
close(incoming_socket);
return operation_codes(OK);
}
Error
shantanu#Shantanus-MacBook-Pro webserver % ./test1.o
* Waiting for new connection *
libc++abi: terminating
Here comes 4
zsh: abort ./test1.o
If I'm just calling handle_new_connection() without spawning a new thread, the operation is successful and response is shown in the client.
So I'm pretty sure its about some thread thing that I'm unaware of.
Environment -
Apple M1 Silicon; running g++ natively on ARM.
Edit
function definition for handle_new_connection()
static enum operation_codes handle_new_connection(int incoming_socket);
I used pthread_t instead of std::thread and it worked just fine.
Instead of
std::thread in_conn_th(handle_new_connection, in_socket);
I used
pthread_t in_conn_th;
pthread_create(&in_conn_th, NULL, handle_new_connection, (void*)(&in_socket));
And changed the function definition to receive the void *
Do not forget to include pthread.h header.
Related
I work on a C++ server where I wait for an network connection. If I get one I put the socket into a new thread and listen for further inputs. But the problem is that as soon as I have the socket in a new thread the TCP connection is disconnected. I'm using the SFML library.
Here's some code:
main.cpp:
int main() {
std::list<std::thread> user_connections;
sf::TcpListener listener;
listener.listen(PORT);
while (true)
{
sf::TcpSocket client;
listener.accept(client);
Protocol user_connection;
std::thread new_con (&Protocol::connect, &user_connection, std::ref(client));
new_con.detach();
user_connections.push_back(std::move(new_con)); // user_connections is a list
}
protocol.cpp:
class Protocol {
public:
void connect(sf::TcpSocket& client)
{
std::cout << "Address: " << client.getRemoteAddress() << ":" << client.getRemotePort() << std::endl;
}
}
This prints out:
Address: 0.0.0.0:0
And if I try to send any kind of message I get the status 4 which is according to the documentation disconnected.
EDIT:
According to #Ted Lyngmo it's because I need to put client in a list, because otherwise it runs out of scope. Now if I try to put it in a list via:
std::list<sf::TcpSocket> clients; // executed before while loop
// [...]
clients.push_back(client); // in the while loop
I get the error: (pastebin).
This is something built on your current threaded code. It may be a good idea to use a single threaded design and use the sf::SocketSelector to wait for events on the listener and all the connected clients instead.
In this lazy solution disconnected clients will not be removed from the servers list of clients until a new client is connected.
I've tried to explain it with comments in the code which is an echoing kind of server, so you can telnet to it, send messages and get them back.
#include <SFML/Network.hpp>
#include <atomic>
#include <iostream>
#include <list>
#include <thread>
constexpr uint16_t PORT = 2048; // what you have in your code.
// A simple struct to keep a client and thread
struct client_thread {
sf::TcpSocket client{};
std::thread thread{};
// The main thread can check "done" to remove this client_thread from its list:
std::atomic<bool> done{false};
~client_thread() {
// instead of detaching, join()
if(thread.joinable()) thread.join();
}
};
// the connect function gets a reference to a client_thread instead
void connect(client_thread& clith) {
constexpr std::size_t BufSize = 1024;
auto& [client, thread, done] = clith; // for convenience
std::cout << "thread: Address: " << client.getRemoteAddress() << ":"
<< client.getRemotePort() << std::endl;
std::string buffer(BufSize, '\0');
std::size_t received;
while(client.receive(buffer.data(), buffer.size(), received) == sf::Socket::Done) {
// remove ASCII control chars (cr and newline etc.)
while(received && buffer[received - 1] < ' ') --received;
buffer.resize(received);
std::cout << buffer << std::endl;
// send something back
buffer = "You sent >" + buffer + "<\n";
client.send(buffer.c_str(), buffer.size());
// restore the size
buffer.resize(BufSize);
}
std::cout << "thread: client disconnected\n";
client.disconnect();
// set done to true so the main thread can remove the client_thread
done = true;
}
int main() {
sf::TcpListener listener;
// check that listening actually works
if(listener.listen(PORT) != sf::Socket::Done) return 1;
// now a list of client_thread instead:
std::list<client_thread> user_connections;
while(true) {
// create a client_thread to use when listening
auto& clith = user_connections.emplace_back();
auto& [client, thread, _] = clith; // for convenience
std::cout << "main: listening ...\n";
sf::Socket::Status status = listener.accept(client);
if(status == sf::Socket::Done) {
std::cout << "main: got connection\n";
thread = std::thread(connect, std::ref(clith));
} else {
std::cout << "main: accept not done\n";
}
// remove disconnected clients, pre C++20
for(auto it = user_connections.begin(); it != user_connections.end();) {
// check the atomic bool in all threads
if(it->done) {
std::cout << "main: removing old connection\n";
it = user_connections.erase(it);
} else {
++it;
}
}
// remove disconnected clients, >= C++20
//
// std::erase_if(user_connections,
// [](auto& clith) -> bool { return clith.done; });
}
}
Edit regarding your edited question where you're trying to put the client in a list:
You're trying to copy the sf::TcpSocket and it's not copyable. What's worse, it's not even moveable. The reason the code in my answer works is because it avoids both copying and moving by using std::list::emplace_back to construct the element in place in the list.
It is apparently both sf::TcpSocket client and Protocol user_connection are destroyed. It's no use to only keep the thread alive, your thread only holds references to client and user_connection, but both of them are destroyed soon after your thread is created (and maybe not even started running).
I read a little bit on the SMFL library and unfortunately, at least the client, which is an object of TCPSocket, is not copyable, nor movable. The SMFL library must be a very old library. Any modern socket library will design socket to be at least movable, meaning that you can move your socket into the thread, or move it to the std::list or std::vector you created.
So, to use SMFL library, which was written without modern C++11 support (the copy & move in C++ was introduced in C++ 2011), together with C++11 library (std::thread), will be quite painful.
You can probably use std::shared_ptr to hold a newly created protocol & client, and pass shared_ptr into thread or into the list you created.
I don't know what Protocol exactly does, a rough pseudo code is as follows,
std::shared_ptr<TcpSocket> client = std::make_shared<TcpSocket>();
listener.accept(*client);
std::shared_ptr<Protocol> protocol = std::make_shared<Protocol>();
// copy the pointer into thread, they will be deleted after the thread is done
std::thread new_con ( [client, protocol] () { protocol->connect(*client); } );
or, protocol can probably be defined in the thread,
std::shared_ptr<TcpSocket> client = std::make_shared<TcpSocket>();
listener.accept(*client);
std::thread new_con ( [client] () {
Protocol protocol;
protocol.connect(*client);
} );
I am using version 1.23.1 of the GRPC library.
I have an asynchronous RPC c++ Client class, which initiates each RPC with the following method:
void Client::SendTaskAsync(const Task& task) {
unique_lock<mutex> lock(mtx_);
cout << "Sending task with id " << task.id() << endl;
ClientContext context;
Status status;
unique_ptr<ClientAsyncResponseReader<Result>> rpc(
stub_->PrepareAsyncSendTask(&context, task, &queue_));
rpc->StartCall();
// Allocating memory to store result from RPC
Result* result = &results_.emplace_back();
int* tag = new int(results_.size() - 1);
rpc->Finish(result, &status, static_cast<void*>(tag));
}
In the main thread I call SendTaskAsync five times in a loop.
The Client class has a background thread informing when each RPC has returned a Result:
while (true) {
void* tag;
bool ok = false;
{
unique_lock<mutex> lock(mtx_);
cout << "Waiting the for next result" << endl;
const time_point<system_clock> deadline =
system_clock::now() + milliseconds(1000);
// SEGFAULT HERE, WHY?
GPR_ASSERT(queue_.AsyncNext(&tag, &ok, deadline));
}
if (ok) {
int index = *static_cast<int*>(tag);
cout << "Got result with tag " << index << endl;
} else {
cout << "Sleeping" << endl;
sleep_for(milliseconds(1000));
}
}
If I start my client, the following log is observed:
BACKGROUND: Waiting for the next result
MAIN THREAD: Sending task with id 0
BACKGROUND: Sleeping
MAIN THREAD: Sending task with id 1
MAIN THREAD: Sending task with id 2
MAIN THREAD: Sending task with id 3
MAIN THREAD: Sending task with id 4
BACKGROUND: Waiting for the next result
BACKGROUND: Segmentation fault (core dumped)
What happens is that
Background thread checks if a queue_ contains a result, there is none yet, so it goes to sleep;
Main thread makes 5 RPC that at the end should populate the queue_ with results;
Background thread wakes up and checks if a queue_ contains a result, AND CRASHES.
Any ideas why?
The code in the question is written according to this tutorial, which sends only one request and waits for a reply in the same thread.
If you want to use multiple threads, follow the client example here.
I use the grpc cpp example "helloworold" code to test limit handle thread. But I can't find any way to do it.
grpc version: 1.15
linux: ubuntu 16.04
I set the builder like this:
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::MIN_POLLERS, 1);
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::MAX_POLLERS, 1);
builder.SetSyncServerOption(ServerBuilder::SyncServerOption::NUM_CQS, 1);
set the handle like this:
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
std::cout << "start " << std::this_thread::get_id() << std::endl;
reply->set_message(prefix + request->name());
//**** sleep 5s, keep this thread block ****
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "end " << std::this_thread::get_id() << std::endl;
return Status::OK;
}
};
I use the example client and call SayHello in 100 threads, and server log show the thread is created by 100 times.
In this test, is my test way wrong? or somethings miss setup??
You can use SetMaxThread in this way:
grpc::ResourceQuota rq;
rq.SetMaxThreads(n);
builder.SetResourceQuota(rq);
It seems that a thread is needed for every completion queue. So if n=4 when you have 1 completion queue, 3 threads are remained for processing requests.
What you are using is the sync API, which will initiate a thread per call. You can look at the async API to reduce the number of threads.
I'm using the AMQ-CPP library (https://github.com/CopernicaMarketingSoftware/AMQP-CPP) to connect to an existing queue I've created but I'm unable to read anything. I've tested that the queue works using another library (https://github.com/alanxz/SimpleAmqpClient, it works and I consume messages), but it uses a polling approach and I need an event based one.
My code looks like (based on the provided example):
int main()
{
auto *poll = EV_DEFAULT;
// handler for libev (so we don't have to implement AMQP::TcpHandler!)
AMQP::LibEvHandler handler(poll);
// make a connection
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
// we need a channel too
AMQP::TcpChannel channel(&connection);
// Define callbacks and start
auto messageCb = [&channel](
const AMQP::Message &message, uint64_t deliveryTag,
bool redelivered)
{
std::cout << "message received" << std::endl;
// acknowledge the message
channel.ack(deliveryTag);
processMessage(message.routingKey(), message.body());
};
// callback function that is called when the consume operation starts
auto startCb = [](const std::string &consumertag) {
std::cout << "consume operation started: " << consumertag << std::endl;
};
// callback function that is called when the consume operation failed
auto errorCb = [](const char *message) {
std::cout << "consume operation failed" << std::endl;
};
channel.consume("domoqueue")
.onReceived(messageCb)
.onSuccess(startCb)
.onError(errorCb);
// run the poll
ev_run(poll, 0);
// done
return 0;
}
I'm running the code in a Raspberry Pi having :
Linux raspberrypi 4.4.26-v7+ #915 SMP Thu Oct 20 17:08:44 BST 2016 armv7l GNU/Linux
What can be the problem? Probably I'm missing some configuration parameters for the queue... I've placed some debug traces and the channel creation does not take place. It blocks in the connection statement:
AMQP::TcpConnection connection(&handler, AMQP::Address("amqp://localhost/"));
cout << "I never show up" << endl;
// we need a channel too
AMQP::TcpChannel channel(&connection)
I've found my problem: I wasn't using the declareQueue() method! In fact, I had to use it but specifying the following parameters (the same as I did when I created the queue manually):
AMQP::Table arguments;
arguments["x-message-ttl"] = 120 * 1000;
// declare the queue
channel.declareQueue("domoqueue", AMQP::durable + AMQP::passive, arguments).onSuccess(callback);
I tried to create simple server like in link 1.
Youtube tutorial to create multithreaded server
void Test_Server::incomingConnection(int socketDescriptor_)
{
qDebug() << socketDescriptor_ << "connecting...";
Test_Thread *thread_ = new Test_Thread(number_,socketDescriptor_,this);
connect(thread_,SIGNAL(finished()),thread_,SLOT(deleteLater()));
thread_->start();
number_++;
}
////
void Test_Thread::run()
{
qDebug() << this->Socket_Descriptor_ << "starting thread";
socket = new QTcpSocket();
if(!socket->setSocketDescriptor(Socket_Descriptor_))
{
qDebug() << "ERROR";
}
connect(socket,SIGNAL(readyRead()),this,SLOT(Ready_read_()),Qt::DirectConnection);
connect(socket,SIGNAL(disconnected()),this,SLOT(disconnected_()),Qt::DirectConnection);
qDebug() << this->Socket_Descriptor_ << "Client connected";
QByteArray name = QByteArray::number(number_);
server_->Socket_map_.insert(name,this);
server_->show_all_connected_sockets_();
exec();
}
My goal is to connect two clients to server(i use telnet), write from Client 1 to server something, and server should pass data to Client 2.
To do that I've made QMap to storage pointers to MyThreads. When data is received from Client 1, I'm calling method:
void Test_Server::write_to_client_(int number, QByteArray data)
{
QByteArray name = QByteArray::number(number);
Test_Thread *pointer;
pointer = client_socket_(name);
connect(this,SIGNAL(send_data_(QByteArray)),pointer,SLOT(write_data_(QByteArray)));
emit send_data_(data);
disconnect(this,SIGNAL(send_data_(QByteArray)),pointer,SLOT(write_data_(QByteArray)));
qDebug() << "void Test_Server::write_to_client_(int number, QByteArray data): data sent";
}
////
void Test_Thread::write_data_(QByteArray data) const
{
socket->write(data);
socket->waitForBytesWritten();
}
Generally passing information works, I write in Client 1 some data, and Client 2 shows it, however I'm geting:
TQObject: Cannot create children for a parent that is in a different
thread.
Parent Test_Thread is QNativeSocketEngine(Pointer 1), parent's thread is >(Pointer 2), current thread is (Pointer 3);
QsocketNotifier: Socket notifiers cannot be enabled or disabled from another thread.
My question is: how to correctly pass data from client 1, to server, and then to client 2? I've done reasearch and problem lies in proper use of Signals and Slots but I cannot find out how to do it in proper way.
Test_Thread::write_data is not running on the same thread where the socket was created, that is Test_Thread::run(). In the QThread class, only what runs on the run method runs on a separate thread.
I finally solved issue. To do that i followed similar issue solution described here: PROBLEM & SOLUTION
I've resigned to use class MyThread, instead created class Worker and moved it to thread like here below:
void Test_Server::incomingConnection(int socketDescriptor_)
{
qDebug() << "void Test_Server::incomingConnection current thread: " << QThread::currentThread();
qDebug() << socketDescriptor_ << "connecting...";
Socket_map_.insert(number_,QByteArray::number(socketDescriptor_));
QThread *thread_= new QThread;
qDebug() << "void Test_Server::incomingConnection new thread_: " << thread_->thread();
Test_Worker *worker = new Test_Worker(socketDescriptor_);
worker->moveToThread(thread_);
connect(thread_,SIGNAL(started()),worker,SLOT(create_socket_()));
connect(this,SIGNAL(pass_socket_descriptor_(int)),worker,SLOT(set_socket_descriptor_(int)));
connect(worker,SIGNAL(finished()),thread_,SLOT(quit()));
connect(worker,SIGNAL(finished()),worker,SLOT(deleteLater()));
connect(thread_,SIGNAL(finished()),thread_,SLOT(deleteLater()));
connect(worker,SIGNAL(pass_data_to_server_(QByteArray,QByteArray)),this,SLOT(data_from_socket_(QByteArray,QByteArray)));
connect(this,SIGNAL(pass_data_to_client_(QByteArray,QByteArray)),worker,SLOT(show_data_received_from_server_(QByteArray,QByteArray)));
number_++;
thread_->start();
}
HINT: When i created socket via test_server signal create_socket_(int)
and socket create_socket(int), program didn't work correctly. To fix
that connect signal from starting thread to socket - create_socket_
Program now succesfuly without errors receive data from client 1 and pass it to client 2.