I am using QUdpSocket in order to receive data from a server. When i receive data With SFML its working, i can receive data throught SocketUdp but with qt it doesn't work.
void TheClass::Bind()
{
m_sock_receive = new QUdpSocket(this);
if (m_sock_receive->bind(QHostAddress::Any, port))
{
std::cout << "Bind: OK" << std::endl;
connect(m_sock_receive, SIGNAL(readyRead()), this, SLOT(QtReceive()));
}
else
std::cout << "Bind: NOK" << std::endl;
}
void TheClass::QtReceive()
{
std::cout << "Pending data !" << std::endl;
}
I would make the connect before the bind. It's possible that after binding, the readyRead fires before the connect call is completed. If you don't empty the pending datagrams, the readyRead will not fire again.
In order to connect SIGNAL(readyRead()) to any slot, the QUdpSocket must be in a QAbstractSocket::BoundState. Although you call bind before connect, the bind on QUdpSocket makes a non-blocking call, meaning, that the bind might be delayed. To ensure that you connect the SIGNAL(readyRead()) to SLOT(QtReceive()) after the bind has finished and the QUdpSocket is in a bound state, do the following:
void TheClass::Bind()
{
m_sock_receive = new QUdpSocket(this);
connect(m_sock_receive, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
if (m_sock_receive->bind(QHostAddress::Any, port))
{
std::cout << "Bind: OK" << std::endl;
}
else
std::cout << "Bind: NOK" << std::endl;
}
void TheClass::QtReceive()
{
std::cout << "Pending data !" << std::endl;
}
void TheClass::onSocketStateChange (QAbstractSocket::SocketState state) {
if ( state == QAbstractSocket::BoundState ) {
connect(m_sock_receive, SIGNAL(readyRead()), this, SLOT(QtReceive()));
}
}
Related
I'm trying to write a very simple client/server app with boost::socket. I need a server to run and a single client to connect, send data, disconnect and possibly reconnect later and repeat.
The code reduced to the minimum is here:
Server app:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class TheServer
{
public:
TheServer(int port) : m_port(port)
{
m_pIOService = new boost::asio::io_service;
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
listenForNewConnection();
}
~TheServer()
{
m_bContinueReading = false;
m_pIOService->stop();
m_pThread->join();
delete m_pThread;
delete m_pSocket;
delete m_pAcceptor;
delete m_pIOService;
}
void listenForNewConnection()
{
if (m_pSocket)
delete m_pSocket;
if (m_pAcceptor)
delete m_pAcceptor;
// start new acceptor operation
m_pSocket = new tcp::socket(*m_pIOService);
m_pAcceptor = new tcp::acceptor(*m_pIOService, tcp::endpoint(tcp::v4(), m_port));
std::cout << "Starting async_accept" << std::endl;
m_pAcceptor->async_accept(*m_pSocket,
boost::bind<void>(&TheServer::readSession, this, boost::asio::placeholders::error));
}
void readSession(boost::system::error_code error)
{
if (!error)
{
std::cout << "Connection established" << std::endl;
while ( m_bContinueReading )
{
static unsigned char buffer[1000];
boost::system::error_code error;
size_t length = m_pSocket->read_some(boost::asio::buffer(&buffer, 1000), error);
if (!error && length != 0)
{
std::cout << "Received " << buffer << std::endl;
}
else
{
std::cout << "Received error, connection likely closed by peer" << std::endl;
break;
}
}
std::cout << "Connection closed" << std::endl;
listenForNewConnection();
}
else
{
std::cout << "Connection error" << std::endl;
}
std::cout << "Ending readSession" << std::endl;
}
void run()
{
while (m_bContinueReading)
m_pIOService->run_one();
std::cout << "Exiting run thread" << std::endl;
}
bool m_bContinueReading = true;
boost::asio::io_service* m_pIOService = NULL;
tcp::socket* m_pSocket = NULL;
tcp::acceptor* m_pAcceptor = NULL;
boost::thread* m_pThread = NULL;
int m_port;
};
int main(int argc, char* argv[])
{
TheServer* server = new TheServer(1900);
std::cout << "Press Enter to quit" << std::endl;
std::string sGot;
getline(std::cin, sGot);
delete server;
return 0;
}
Client app:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
int main(int argc, char* argv[])
{
std::cout << std::endl;
std::cout << "Starting client" << std::endl;
using boost::asio::ip::tcp;
boost::asio::io_service* m_pIOService = NULL;
tcp::socket* m_pSocket = NULL;
try
{
m_pIOService = new boost::asio::io_service;
std::stringstream sPort;
sPort << 1900;
tcp::resolver resolver(*m_pIOService);
tcp::resolver::query query(tcp::v4(), "localhost", sPort.str());
tcp::resolver::iterator iterator = resolver.resolve(query);
m_pSocket = new tcp::socket(*m_pIOService);
m_pSocket->connect(*iterator);
std::cout << "Client conected" << std::endl;
std::string hello = "Hello World";
boost::asio::write( *m_pSocket, boost::asio::buffer(hello.data(), hello.size()) );
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
hello += "(2)";
boost::asio::write(*m_pSocket, boost::asio::buffer(hello.data(), hello.size()));
}
catch (std::exception& e)
{
delete m_pSocket;
m_pSocket = NULL;
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Note that I use non-blocking async_accept to be able to cleanly stop the server when Enter is pressed.
Under Windows, it works perfectly fine, I run the server, it outputs:
Starting async_accept
Press Enter to quit
For each client app run, it outpts:
Starting client
Client conected
and server app outputs:
Connection established
Received Hello World
Received Hello World(2)
Received error, connection likely closed by peer
Connection closed
Starting async_accept
Ending readSession
Then when I press Enter in server app console, it outputs Exiting run thread and cleanly stops.
Now, when I compile this same code under Linux, the client outputs the same as under Windows, but nothing happens on the server side...
Any idea what's wrong?
There are many questionable elements.
There is a classical data race on m_bContinueReading. You write from another thread, but the other thread may never see the change because of the data race.
The second race condition is likely your problem:
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
listenForNewConnection();
Here the run thread may complete before you ever post the first work. You can use a work-guard to prevent this. In your specific code you would already fix it by reordering the lines:
listenForNewConnection();
m_pThread = new boost::thread(boost::bind<void>(&TheServer::run, this));
I would not do this, because I would not have those statements in my constructor body. See below for the work guard solution
There is a lot of raw pointer handling and new/delete going on, which merely invites errors.
You use the buffer assuming that it is NUL-terminated. This is especially unwarranted because you use read_some which will read partial messages as they arrive on the wire.
You use a static buffer while the code may have different instances of the class. This is very false optimization. Instead, prevent all the allocations! Combining with the previous item:
char buffer[1000];
while (m_bContinueReading) {
size_t length = m_Socket.read_some(asio::buffer(&buffer, 1000), ec);
std::cout << "Received " << length << " (" << quoted(std::string(buffer, length)) << "), "
<< ec.message() << std::endl;
if (ec.failed())
break;
}
You start a new acceptor always, where there is no need: a single acceptor can accept as many connections as you wish. In fact, the method shown runs into the problems
that lingering connections can prevent the new acceptor from binding to the same port. You could also alleviate that with
m_Acceptor.set_option(tcp::acceptor::reuse_address(true));
the destroyed acceptor may have backlogged connections, which are discarded
Typically you want to support concurrent connection, so you can split of a "readSession" and immediately accept the next connection. Now, strangely your code seems to expect clients to be connected until the server is prompted to shutdown (from the console) but after that you somehow start listening to new connections (even though you know the service will be stopping, and m_bContinueReading will remain false).
In the grand scheme of things, you don't want to destroy the acceptor unless something invalidated it. In practice this is rare (e.g. on Linux the acceptor will happily survive disabling/re-enabling the network adaptor).
you have spurious explicit template arguments (bind<void>). This is an anti-pattern and may lead to subtle problems
similar with the buffer (just say asio::buffer(buffer) and no longer have correctness concerns. In fact, don't use C-style arrays:
std::array<char, 1000> buffer;
size_t n = m_Socket.read_some(asio::buffer(buffer), ec);
std::cout << "Received " << n << " " << quoted(std::string(buffer.data(), n))
<< " (" << ec.message() << ")" << std::endl;
Instead of running a manual run_one() loop (where you forget to handle exceptions), consider "just" letting the service run(). Then you can .cancel() the acceptor to let the service run out of work.
In fact, this subtlety isn't required in your code, since your code already forces "ungraceful" shutdown anyways:
m_IOService.stop(); // nuclear option
m_Thread.join();
More gentle would be e.g.
m_Acceptor.cancel();
m_Socket.cancel();
m_Thread.join();
In which case you can respond to the completion error_code == error::operation_aborted to stop the session/accept loop.
Technically, you may be able to do away with the boolean flag altogether.
I keep it because it allows us to handle multiple session-per-thread in
"fire-and-forget" manner.
In the client you have many of the same problems, and also a gotcha where
you only look at the first resolver result (assuming there was one),
ignoring the rest. You can use asio::connect instead of
m_Socket.connect to try all resolved entries
Addressing the majority of these issues, simplifying the code:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/optional.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using namespace std::chrono_literals;
using boost::system::error_code;
class TheServer {
public:
TheServer(int port) : m_port(port) {
m_Acceptor.set_option(tcp::acceptor::reuse_address(true));
do_accept();
}
~TheServer() {
m_shutdownRequested = true;
m_Work.reset(); // release the work-guard
m_Acceptor.cancel();
m_Thread.join();
}
private:
void do_accept() {
std::cout << "Starting async_accept" << std::endl;
m_Acceptor.async_accept( //
m_Socket, boost::bind(&TheServer::on_accept, this, asio::placeholders::error));
}
void on_accept(error_code ec) {
if (!ec) {
std::cout << "Connection established " << m_Socket.remote_endpoint() << std::endl;
// leave session running in the background:
std::thread(&TheServer::read_session_thread, this, std::move(m_Socket)).detach();
do_accept(); // and immediately accept new connection(s)
} else {
std::cout << "Connection error (" << ec.message() << ")" << std::endl;
std::cout << "Ending readSession" << std::endl;
}
}
void read_session_thread(tcp::socket sock) {
std::array<char, 1000> buffer;
for (error_code ec;;) {
size_t n = sock.read_some(asio::buffer(buffer), ec);
std::cout << "Received " << n << " " << quoted(std::string(buffer.data(), n)) << " ("
<< ec.message() << ")" << std::endl;
if (ec.failed() || m_shutdownRequested)
break;
}
std::cout << "Connection closed" << std::endl;
}
void thread_func() {
// http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.effect_of_exceptions_thrown_from_handlers
for (;;) {
try {
m_IOService.run();
break; // exited normally
} catch (std::exception const& e) {
std::cerr << "[eventloop] error: " << e.what();
} catch (...) {
std::cerr << "[eventloop] unexpected error";
}
}
std::cout << "Exiting service thread" << std::endl;
}
std::atomic_bool m_shutdownRequested{false};
uint16_t m_port;
asio::io_service m_IOService;
boost::optional<asio::io_service::work> m_Work{m_IOService};
tcp::socket m_Socket{m_IOService};
tcp::acceptor m_Acceptor{m_IOService, tcp::endpoint{tcp::v4(), m_port}};
std::thread m_Thread{boost::bind(&TheServer::thread_func, this)};
};
constexpr uint16_t s_port = 1900;
void run_server() {
TheServer server(s_port);
std::cout << "Press Enter to quit" << std::endl;
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
void run_client() {
std::cout << std::endl;
std::cout << "Starting client" << std::endl;
using asio::ip::tcp;
try {
asio::io_service m_IOService;
tcp::resolver resolver(m_IOService);
auto iterator = resolver.resolve("localhost", std::to_string(s_port));
tcp::socket m_Socket(m_IOService);
connect(m_Socket, iterator);
std::cout << "Client connected" << std::endl;
std::string hello = "Hello World";
write(m_Socket, asio::buffer(hello));
std::this_thread::sleep_for(100ms);
hello += "(2)";
write(m_Socket, asio::buffer(hello));
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
int main(int argc, char**) {
if (argc>1)
run_server();
else
run_client();
}
A proxy application which the client side redirect all connections to the server side application.
At the server side, QTcpServer::newConnection() signal is emitted and we connect QTcpSocket::readyRead() signal to our slot which receives the data from client and redirect the data to 3rd party application for handling HTTP/HTTPS requests. readyRead() signal emitted when client sends HTTP request but this does not happen with HTTPS requests.
Tried: Set QTcpServer::proxy() and QTcpSocket::proxy() to QNetworkProxy::NoProxy, use QSslSocket instead of QTcpSocket with ignoreSslErrors().
Code:
Client::Client(QObject *parent, QTcpSocket* clientCon)
: QObject(parent)
{
client = clientCon;
tunnel = new QSslSocket();
if (serverApp) // If application running as server
tunnel->connectToHost("127.0.0.1", 808); // Connect to CCProxy which handle the requests for us
else
{
tunnel->setProxy(QNetworkProxy::NoProxy);
tunnel->ignoreSslErrors();
tunnel->connectToHost(QHostAddress("serverip"), 9090); // Connect to the server side if running as client
}
tunnel->waitForConnected(5000); // 5 seconds timeout
if (tunnel->state() == tunnel->ConnectedState)
{
QObject::connect(client, SIGNAL(readyRead()), this, SLOT(ClientDataReceived()));
QObject::connect(tunnel, SIGNAL(readyRead()), this, SLOT(TunnelDataReceived()));
QObject::connect(client, SIGNAL(disconnected()), this, SLOT(ClientDisconnected()));
QObject::connect(tunnel, SIGNAL(disconnected()), this, SLOT(ClientDisconnected()));
}
}
Client::~Client()
{
}
bool Client::IsTunnelEstablished()
{
return tunnel->state() == tunnel->ConnectedState ? true : false;
}
QString Client::GetTunnelErrorString()
{
return tunnel->errorString();
}
void Client::ClientDataReceived()
{
QByteArray data = client->readAll();
qDebug() << "Client [" << client->peerAddress().toString().remove(" ") << ":" << client->peerPort() << "] Data Received: " << data.size();
qint64 wr = tunnel->write(data, data.size());
if (wr != data.size())
qDebug() << "QTcpSocket::write() failed!"; // Never happened
data.clear();
tunnel->flush();
}
void Client::TunnelDataReceived()
{
QByteArray data = tunnel->readAll();
qDebug() << "Client Tunnel [" << client->peerAddress().toString().remove(" ") << ":" << client->peerPort() << "] Data Received: " << data.size();
client->write(data);
data.clear();
client->flush();
}
void Client::ClientDisconnected()
{
/*if (tunnel->state() == tunnel->ConnectedState)
tunnel->close();*/
qDebug() << "[" << client->peerAddress().toString().remove(" ") << ":" << client->peerPort() << "] Disconnected";
QObject::disconnect(client, SIGNAL(readyRead()), this, SLOT(ClientDataReceived()));
QObject::disconnect(tunnel, SIGNAL(readyRead()), this, SLOT(TunnelDataReceived()));
QObject::disconnect(client, SIGNAL(disconnected()), this, SLOT(ClientDisconnected()));
QObject::disconnect(tunnel, SIGNAL(disconnected()), this, SLOT(ClientDisconnected()));
client->deleteLater();
tunnel->deleteLater();
delete this; // Self delete
}
Server Initialization:
void BaseProtocol::StartServer()
{
server = new QTcpServer();
server->setProxy(QNetworkProxy::NoProxy);
server->listen(QHostAddress("0.0.0.0"), serverApp ? 9090 : 5050); // 9090 for server, 5050 for client
qDebug() << QObject::connect(server, SIGNAL(newConnection()), this, SLOT(ClientConnecting()));
}
void BaseProtocol::ClientConnecting()
{
QTcpSocket *client = server->nextPendingConnection(); // Accept the connection
qDebug() << "Client Connected: " << client->peerAddress().toString() << ":" << client->peerPort();
Client* clientHandler = new Client(this, client);
if (!clientHandler->IsTunnelEstablished())
{
qDebug() << "Tunnel Connection Failed: " << clientHandler->GetTunnelErrorString() << "\n";
client->disconnect();
client->deleteLater();
delete clientHandler;
}
}
As you see the client and server application are the same but start with different ports. The reason why I use a client side application instead of connecting to the server as proxy directly is because I want to encrypt the data between client and server later. (same problem also exist when connecting directly to server side)
This is supposed to be a transparent proxy so there is no need for SSL/TLS at all.
"Internet Options" are used to set the proxy server to "127.0.0.1:5050" at the client side.
I'm having trouble sending data from a server to a client using Qt. Whenever I do QTcpSocket::waitForBytesWritten() after a call to QTcpSocket::write(...), it returns false.
I tried using the bytesWritten signal, but that never gets emitted, presumably because no data is able to be written, and no data is received on the client side.
The writeData method is what is being called in the MainWindow class, but to try and narrow down the cause of the problem, I moved the writing of data to the client into the newConnection method.
The message Connection received is printed to the output window. I'm sending the string Some random data in the newConnection method to the client for testing purposes, but this is not being received by the client (the code to output the received data on the client side is inside Character::readData() method).
The value of the returnValue variable is true, and the code returns from the call to the client->waitForBytesWritten(-1) method. client->errorString() gives Unknown error, and then the message Bytes written is printed (even though, evidently, nothing is written, but I'm just using it as a status message).
Server.cpp
#include "Server.h"
Server::Server(QObject *parent) : QObject(parent)
{
server = new QTcpServer(this);
qDebug() << connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
qDebug() << connect(server, SIGNAL(bytesWritten()), SLOT(bytesWritten()));
qDebug() << "Listening:" << server->listen(QHostAddress::Any, 1451);
server->waitForNewConnection(-1);
}
void Server::newConnection()
{
qDebug("Connection received");
client = server->nextPendingConnection();
client->write("Some random data\n");
bool returnValue = client->flush();
qDebug() << "Return value: " << returnValue;
qDebug() << client->waitForBytesWritten(-1);
qDebug() << "Error: " << client->errorString();
qDebug() << "Bytes written";
}
void Server::bytesWritten(qint64 bytes)
{
qDebug() << "Bytes written: " << QString::number(bytes);
}
void Server::writeData(std::string data)
{
QByteArray byteArray = QByteArray(data.c_str());
qDebug() << "Write data: " << QString::fromStdString(data);
client->write(byteArray);
}
Client.cpp
#include "Client.h"
#include "mainwindow.h"
Client::Client(QObject* parent) : QObject(parent)
{
socket = new QTcpSocket(this);
(void)QObject::connect(socket, SIGNAL(connected()), this, SLOT(connected()));
qDebug() << "Connect signal" << QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(readData()));
}
bool Client::connectToHost(QString host)
{
socket->connectToHost(host, 1451);
socket->waitForConnected();
qDebug() << "Error: " << QString::number(socket->error() == QAbstractSocket::UnknownSocketError);
return true;
}
void Client::connected()
{
qDebug("Socket is connected");
qDebug() << QString::number(socket->state() == QAbstractSocket::ConnectedState);
}
void Client::readData()
{
qDebug("Read data");
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
QByteArray data = sender->readAll();
std::string character = data.toStdString();
qDebug() << "Character received: " << QString::fromStdString(character);
MainWindow::characterReceived(character);
}
I want to create a server async with multi threads.
When I create a thread_group and waiting for some connections in asynchronous way. My program don't wait and termine immediatly.
void Server::configServer() {
_ip = boost::asio::ip::address_v4::from_string("127.0.0.1");
boost::asio::ip::tcp::resolver resolver(_io_service);
_endpoint = *resolver.resolve({tcp::v4(), _port});
std::cout << "Server address: " << _ip.to_string() << ":" << _port << std::endl;
_acceptor.close();
_acceptor.open(_endpoint.protocol());
_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
_acceptor.bind(_endpoint);
_acceptor.listen();
for (int i = 0; i < 8; ++i) {
_threads.create_thread(boost::bind(&boost::asio::io_service::run, &_io_service));
}
_threads.join_all();
std::cout << "Server is set up" << std::endl;
run();
}
void Server::run() {
Connection::pointer newConnection = Connection::create(_acceptor.get_io_service());
std::cout << "Server is running" << std::endl;
_acceptor.async_accept(newConnection->socket(),
boost::bind(&Server::handleAccept, this, newConnection,
boost::asio::placeholders::error));
}
void Server::handleAccept(Connection::pointer newConnection, const boost::system::error_code& error) {
if (!error) {
std::cout << "Reçu un client!" << std::endl;
newConnection->start();
run();
}
}
Can you tell me what am I doing wrong ?
run works as long as there are any pending tasks/handlers to be processed.
In your case you started run, then first async_ method was called. So run ends immediately due to no handlers to be called.
You should init some asynchronous task, then invoke run or use object called work guard. You didn't specify which version of Boost you use, but there are two options:
in olders io_service/io_context::work (ref)
current, executor_work_guard (ref)
In your class you can add executor_work_guard as additional member variable:
class Server {
boost::asio::io_context _io_service;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard;
Server() : ...., guard(boost::asio::make_work_guard(_io_service)) {
}
};
with this approach, run doesn't return even if there are no handlers to be processed.
I have some client application written using boost::asio.
I am trying to implement automatic reconnect when connection is lost. When connection can`t establishment
(for example, when the server is unavailable) or connection issues I trying establish connection again. However, on each attempt,
the application opens a new client port. This leads to a gradual usage of the all dynamic ports of the system.
class client
{
public:
client()
: socket_(nullptr)
, service_()
{
tcp::resolver r(service_);
auto iterator = r.resolve(tcp::resolver::query("127.0.0.1", "4572"));
if (iterator == tcp::resolver::iterator())
throw std::runtime_error("resolve");
endpoint_ = iterator->endpoint();
}
void start()
{
start_connect();
service_.run();
}
private:
void start_connect()
{
std::cout << "Trying " << endpoint_ << "...\n";
socket_ = std::make_shared<tcp::socket>(service_);
socket_->async_connect(endpoint_,
boost::bind(&client::handle_connect, this, _1));
}
void handle_connect(const boost::system::error_code& ec)
{
if (ec)
{
std::cout << "Connect error: " << ec.message() << "\n";
start_connect();
}
else
{
std::cout << "Connected to " << endpoint_ << "\n";
start_read();
}
}
void start_read()
{
boost::asio::async_read_until(*socket_, input_buffer_, '\n',
boost::bind(&client::handle_read, this, _1));
}
void handle_read(const boost::system::error_code& ec)
{
if (!ec)
{
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
if (!line.empty())
std::cout << "Received: " << line << "\n";
start_read();
}
else
{
std::cout << "Error on receive: " << ec.message() << "\n";
start_connect();
}
}
private:
std::shared_ptr<tcp::socket> socket_;
boost::asio::io_service service_;
boost::asio::ip::tcp::endpoint endpoint_;
boost::asio::streambuf input_buffer_;
};
Is it possible to occupy only one client port for the entire execution time of the program? Or at least do not change the port for each attempt to connect to an unavailable server.
I think explicit setting the client port is not good way. Tried not to re-create the socket every time but in this case it is impossible to re-establish the connection.