TCP packets sent in a particular sequence are not received accordingly - qt6

For an online game I connect multiple clients to a server. And the sequence of messages is crucial to comply with the game logic. For example, to start a new game I want all clients to agree first.
Problem is that my messages go through, but I don't receive it in the right sequence (the sender is also receiver).
class network : public QTcpSocket
void network::doSend(const MessageType msgType, QString msgReceiver, QString msgText) {
...
if( this->write( msgText.toUtf8() ) != msgText.toUtf8().length() )
qWarning() << "Not all data have been sent";
this->waitForBytesWritten(5000);
#ifdef QT_DEBUG
qDebug() << "sent" << QVariant::fromValue(msgType).toString() << "from" << m_sName;
#endif
}
void network::doReadyRead() {
...
case nwSyncNewGame: emit onSyncNewGame(aLastMessage); break;
...
#ifdef QT_DEBUG
qDebug() << "received" << aLastMessage["MessageType"].toString() << "from" << aLastMessage["Sender"].toString();
#endif
}
Both messages to send and received are handled in the main thread. Like:
QObject::connect(m_pNetwork, SIGNAL(onSyncNewGame(QVariantMap)),
this, SLOT(doNetworkSyncNewGame(QVariantMap)));
void GamePlay::syncNewGame(QVariantMap aConfig) {
m_pNetwork->doSend(network::nwPoll, "group", "");
...
m_pNetwork->doSend(network::nwSyncNewGame, "group", configData.join("\a"));
void GamePlay::doNetworkSyncNewGame(QVariantMap aMsg) {
emit applyConfig(aMsg);
emit newGame(aMsg["IsLoading"].toBool());
}
sent "nwPoll" from "Scotty"
sent "nwSyncNewGame" from "Scotty"
sent "nwAnswer" from "Scotty"
received "nwSyncNewGame" from "Scotty"
received "nwRefresh" from "Scotty"
received "nwPoll" from "Scotty"
Where shall I look for a solution?

Sent more than one package from a function in the main thread to the socket which ended in a buffer. Restructured the code and everything is fine now. Plenty of questions about immediately sending packages are handled in different topics.

Related

Cancelling callbacks in Boost ASIO

I've been trying to switch my code from using one io_service per network connection to using shared ones, and I'm seeing some very odd behaviour on server sockets (client ones seem to work OK).
In order to try to work out what's going on I've re-started building up a simple example that will allow me to check my assumptions about everything that ought to happen. The first problem I've hit is that io_service::run doesn't exit when there are no handlers left, and as far as I can tell the handlers aren't removed from the work queue.
I have one thread that does an async_accept followed by an async_read. There is a separate client thread (which has its own io_service). The client thread's io_service is never run, and the server's one is run in yet another thread.
I'm using a condition variable to wait in the server thread for the read to complete (which will never happen as the client never writes). This times out just fine and then I call socket.cancel(). I would expect this to remove the read handler and run to exit as the work queue is now empty.
I do see the read handler get called (with a cancel error), but run never exits. When I tie the socket lifetime to the handler lifetime (by lambda capturing a shared_ptr to the socket) the memory isn't freed either.
The server is set up like this:
std::mutex mutex;
std::unique_lock<std::mutex> lock(mutex);
std::condition_variable signal;
boost::asio::io_service server_service;
boost::asio::ip::tcp::acceptor listener(server_service);
std::mutex read_mutex;
std::unique_lock<std::mutex> read_lock(read_mutex);
std::condition_variable read_done;
std::thread server([&]() {
std::unique_lock<std::mutex> lock(mutex);
listener.open(boost::asio::ip::tcp::v4());
listener.set_option(boost::asio::socket_base::enable_connection_aborted(true));
listener.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 4567));
listener.listen();
std::shared_ptr<connection> server_cnx(new connection(server_service));
listener.async_accept(server_cnx->socket,
[&, server_cnx](const boost::system::error_code& error) {
log_thread() << "Server got a connection " << error << std::endl;
boost::asio::async_read_until(server_cnx->socket, server_cnx->buffer, '\n',
[&, server_cnx](const boost::system::error_code& error, std::size_t bytes) {
log_thread() << "Got " << bytes << ", " << error << std::endl;
std::unique_lock<std::mutex> lock(read_mutex);
lock.unlock();
read_done.notify_one();
});
});
lock.unlock();
signal.notify_one();
if ( read_done.wait_for(read_lock, std::chrono::seconds(1)) == std::cv_status::timeout ) {
log_thread() << "Server read timed out -- cancelling socket jobs" << std::endl;
server_cnx->socket.cancel();
server_cnx->socket.close();
} else {
log_thread() << "Server data read" << std::endl;
}
log_thread() << "Exiting server thread" << std::endl;
});
signal.wait(lock);
log_thread() << "Server set up" << std::endl;
The io_service thread is set up like this:
std::thread server_io([&]() {
log_thread() << "About to service server IO requests" << std::endl;
try {
server_service.run();
} catch ( ... ) {
log_thread() << "Exception caught" << std::endl;
}
log_thread() << "**** Service jobs all run" << std::endl;
signal.notify_one();
});
The output is as follows:
10.0002 139992957945728 Server set up
10.0005 139992957945728 Client set up
10.0006 139992848398080 About to service server IO requests
10.0006 139992848398080 Server got a connection system:0
11.0003 139992934819584 Server read timed out -- cancelling socket jobs
11.0004 139992934819584 Exiting server thread
11.0004 139992848398080 Got 0, system:125
20.0006 139992957945728 IO thread timed out servicing requests -- stopping it
^^^ This should not happen because the server service should have run out of work
20.0006 139992957945728 Waiting for things to close....
22.0008 139992957945728 Wait over, exiting
(Columns are time + 10s, thread ID, log message)
At the 11 second mark you can see that the async_read_until is called. This is the last handler in the server's io_service and yet run doesn't exit.
Even after the time out waiting for run to exit fires and the waiting thread does io_service::stop(), still run doesn't exit (there's another 2 second wait there).
The full code is on github
The program is invoking undefined behavior when the server thread attempts to unlock the read_lock that it does not own.
int main()
{
...
std::mutex read_mutex;
std::unique_lock<std::mutex> read_lock(read_mutex); // Acquired by main.
std::condition_variable read_done;
std::thread server([&]() { // Capture lock reference.
std::unique_lock<std::mutex> lock(mutex);
...
// The next line invokes undefined behavior as this thread does did
// not acquire read_lock.mutex().
if (read_done.wait_for(read_lock, ...)
// ^^^^^^^^^ caller does not own.
{
...
}
});
signal.wait(lock);
...
}
In particular, when invoking condition_variable::wait_for(lock), the standard requires that lock.owns_lock() is true and lock.mutex() is locked by the calling thread.
Mixing synchronous and asynchronous flows often add complexity. In this particular case, where the synchronous calls are intertwined throughout each layer using lower-level constructs for event/signal notification without a persisted state, I think that it adds unnecessary complexity and overcomplicates the flow. Furthermore, the broad scope of variables can add complexity. If read_lock had never been captured by the lambdas, then a compiler error would have occurred.
Consider the separation in space when trying to observe two events:
// I will eventually be interested when the server starts
// accepting connections, so start setting up now.
std::mutex server_mutex;
std::unique_lock<std::mutex> server_lock(server_mutex);
std::condition_variable server_started;
std::thread server([&]()
{
// I will eventually be interested when the server reads
// data, so start setting up now.
std::mutex read_mutex;
std::unique_lock<std::mutex> read_lock(read_mutex);
std::condition_variable read_done;
listener.async_accept(...,
[&](...)
{
// Got connection.
async_read_until(...,
[&](...)
{
// Someone may be interested that data has been read,
// so use the correct mutex and condition_variable
// pair.
std::unique_lock<std::mutex> read_lock(read_mutex);
read_lock.unlock();
read_done.notify_one();
});
}); // async_accept
// Someone may be interested that I am accepting connections,
// so use the correct mutex and condition_variable pair.
std::unique_lock<std::mutex> server_lock(server_mutex);
server_lock.unlock();
server_done.notify_one();
// I am now interested in if data has been read.
read_done.wait_for(read_lock);
}); // server thread
// I am now interested in if the server has started.
server_started.wait(server_lock);
The caller has to prepare to handle an event, start an operation, then wait for the event, and the operation must know the event the caller is interested in. To worsen the situation, one must now consider lock ordering to prevent deadlocks. Note how in the above example, the server thread acquires the read_mutex and then the server_mutex. Another thread cannot acquire the mutexes in a difference order without introducing the chance of a deadlock. In terms of complexity, this approach scales poorly with the number of events.
It may be worth considering re-examining the program's flow and control structure. If it can be written to be primarily asynchronous, then callback chains, continuations, or a signal-and-slot system (Boost.Signals) may uncomplicate the solution. If one prefers to have asynchronous code read as if it was synchronous, then Boost.Asio's support for coroutines can provide a clean solution. Finally, if one needs to synchronously wait on an asynchronous operation's result or timeout, then consider using Boost.Asio's support for std::future or using them directly.
// Use an asynchronous operation so that it can be cancelled on timeout.
std::future<std::size_t> on_read = boost::asio::async_read_until(
socket, buffer, '\n',boost::asio::use_future);
// If timeout occurs, then cancel the operation.
if (on_read.wait_for(std::chrono::seconds(1)) == std::future_status::timeout)
{
socket.cancel();
}
// Otherwise, the operation completed (with success or error).
else
{
// If the operation failed, then on_read.get() will throw a
// boost::system::system_error.
auto bytes_transferred = on_read.get();
}
While I would strongly advocate re-examining the overall control structure and reducing variable scope, the following sample is roughly equivalent to the above example, but may be slightly easier to maintain with its use of std::future:
// I will eventually be interested when the server starts
// accepting connections, so start setting up now.
std::promise<void> server_started_promise;
auto server_started = server_started_promise.get_future();
std::thread server([&]()
{
// I will eventually be interested when the server reads
// data, so start setting up now.
std::promise<void> read_done_promise;
auto read_done = read_done_promise.get_future();
listener.async_accept(...,
[&](...)
{
// Got connection.
async_read_until(...,
[&](...)
{
// Someone may be interested that data has been read.
read_done_promise.set_value();
});
}); // async_accept
// Someone may be interested that I am accepting connections.
server_started_promise.set_value();
// I am now interested in if data has been read.
read_done.wait_for(...);
}); // server thread
// I am now interested in if the server has started.
server_started.wait();
Here is a complete example based on the original code that demonstrates using std::future to control flow and timeout asynchronous operations in a synchronous manner:
#include <future>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
int main()
{
using boost::asio::ip::tcp;
// Setup server thread.
boost::asio::io_service server_io_service;
std::promise<tcp::endpoint> server_promise;
auto server_future = server_promise.get_future();
// Start server thread.
std::thread server_thread(
[&server_io_service, &server_promise]
{
tcp::acceptor acceptor(server_io_service);
acceptor.open(tcp::v4());
acceptor.set_option(
boost::asio::socket_base::enable_connection_aborted(true));
acceptor.bind(tcp::endpoint(tcp::v4(), 0));
acceptor.listen();
// Handlers will not chain work, so control the io_service with a work
// object.
boost::optional<boost::asio::io_service::work> work(
boost::in_place(std::ref(server_io_service)));
// Accept a connection.
tcp::socket server_socket(server_io_service);
auto on_accept = acceptor.async_accept(server_socket,
boost::asio::use_future);
// Server has started, so notify caller.
server_promise.set_value(acceptor.local_endpoint());
// Wait for connection or error.
boost::system::system_error error =
make_error_code(boost::system::errc::success);
try
{
on_accept.get();
}
catch (const boost::system::system_error& e)
{
error = e;
}
std::cout << "Server got a connection " << error.code() << std::endl;
// Read from connection.
boost::asio::streambuf buffer;
auto on_read = boost::asio::async_read_until(
server_socket, buffer, '\n', boost::asio::use_future);
// The async_read operation is work, so destroy the work object allowing
// run() to exit.
work = boost::none;
// Timeout the async read operation.
if (on_read.wait_for(std::chrono::seconds(1)) ==
std::future_status::timeout)
{
std::cout << "Server read timed out -- cancelling socket jobs"
<< std::endl;
server_socket.close();
}
else
{
error = make_error_code(boost::system::errc::success);
std::size_t bytes_transferred = 0;
try
{
bytes_transferred = on_read.get();
}
catch (const boost::system::system_error& e)
{
error = e;
}
std::cout << "Got " << bytes_transferred << ", "
<< error.code() << std::endl;
}
std::cout << "Exiting server thread" << std::endl;
});
// Wait for server to start accepting connections.
auto server_endpoint = server_future.get();
std::cout << "Server set up" << std::endl;
// Client thread.
std::promise<void> promise;
auto future = promise.get_future();
std::thread client_thread(
[&server_endpoint, &promise]
{
boost::asio::io_service io_service;
tcp::socket client_socket(io_service);
boost::system::error_code error;
client_socket.connect(server_endpoint, error);
std::cout << "Connected " << error << std::endl;
promise.set_value();
// Keep client socket alive, allowing server to timeout.
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting client thread" << std::endl;
});
// Wait for client to connect.
future.get();
std::cout << "Client set up" << std::endl;
// Reset generic promise and future.
promise = std::promise<void>();
future = promise.get_future();
// Run server's io_service.
std::thread server_io_thread(
[&server_io_service, &promise]
{
std::cout << "About to service server IO requests" << std::endl;
try
{
server_io_service.run();
}
catch (const std::exception& e)
{
std::cout << "Exception caught: " << e.what() << std::endl;
}
std::cout << "Service jobs all run" << std::endl;
promise.set_value();
});
if (future.wait_for(std::chrono::seconds(3)) ==
std::future_status::timeout)
{
std::cout << "IO thread timed out servicing requests -- stopping it"
<< std::endl;
server_io_service.stop();
}
// Join all threads.
server_io_thread.join();
server_thread.join();
client_thread.join();
}
So of course this multi-threading is a tricky business. Turns out that in this case the read lock is acquired in the wrong place so the handler is being blocked by the thread waiting for it to complete.
I guess the lesson here is to never handle thread locks without some sort of time out.

Linux Netcat works as Expected but not QTCPSocket on Raspberry Pi

I have 2 Raspberry Pis, one sender and one receiver which acts as an Access Point using a USB WiFi dongle. I have Qt 5.4.0 code on the sender that uses a USB/FTDI XBee SB6 WiFi unit to send TCP packets to the receiver Pi after connecting to it's Access Point successfully as a client.
The code is sending TCP packets correctly through the XBee to the receiver Pi because I can use the Netcat program on the receiver and watch the packets arrive successfully on port 0x2616 ( 9750 ):
>> sudo nc -l 10.10.10.1 9750
>> HELLOHELLOHELLO
When I try to replace Netcat on the receiver Pi with the following Qt code using QTCPSocket, it never receives any data on the socket. By this I mean that the 'readyRead()' slot is never called. I've run it as sudo and the sender Pi is doing exactly the same transfer as it was when Netcat was capturing the output. What is going on? Am I connecting wrong with QTCPSocket to the local port? How can I make it work? Thanks!
#include "tcpreceiver.h"
// Debug
#include <QDebug>
#define API_DEBUG true
#include <QApplication>
TcpReceiver::TcpReceiver(QObject *parent) :
QObject(parent)
{
// Debug
qDebug() << "Setting up a TCP Socket...";
// Create a socket
m_Socket = new QTcpSocket(this);
// Bind to the 2616 port
m_Socket->connectToHost("10.10.10.1", 0x2616);
//m_Socket->connectToHost( QHostAddress::Any, 0x2616 );
qDebug() << "Socket is valid: " << m_Socket->isValid();
//qDebug() << "Socket value: " << m_Socket->
// Get notified that data is incoming to the socket
connect(m_Socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
// Init to Zero
m_NumberTCPPacketsReceived = 0;
}
void TcpReceiver::readyRead() {
qDebug() << "Received data...";
// When data comes in
QByteArray buffer;
buffer.resize(m_Socket->bytesAvailable());
// Cap buffer size
int lenToRead = buffer.size();
if ( buffer.size() > NOMINAL_AUDIO_BUFFER_SIZE ) {
lenToRead = NOMINAL_AUDIO_BUFFER_SIZE;
}
// Read the data from the TCP Port
m_Socket->read(buffer.data(), lenToRead);
...
// Count up
m_NumberTCPPacketsReceived++;
}
Here is how you do it:
#include "tcpreceiver.h"
// Debug
#include <QDebug>
#include <QHostAddress>
TcpReceiver::TcpReceiver(QObject *parent) :
QObject(parent)
{
// Create a server
qDebug() << "Creating a TCP Server...";
// Create the server
m_Server = new QTcpServer(this);
// Listen on the proper port
m_Server->listen( QHostAddress::Any, 0x2616 );
// Hook up signal and slots
connect(m_Server, SIGNAL(newConnection()), this, SLOT(gotNewConnection()));
connect(m_Server, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(error()));
}
void TcpReceiver::gotNewConnection() {
qDebug() << "Got a new TCP Connection";
// Get the socket
m_Socket = m_Server->nextPendingConnection();
if(m_Socket->state() == QTcpSocket::ConnectedState)
{
qDebug() << "Socket was connected at: " << m_Socket->peerAddress();
}
// Hook up some signals / slots
connect(m_Socket, SIGNAL(disconnected()),this, SLOT(disconnected()));
connect(m_Socket, SIGNAL(readyRead()),this, SLOT(readyRead()));
}
void TcpReceiver::disconnected() {
qDebug() << "Socket Disconnected...";
// Cleanup
m_Socket->deleteLater();
}
void TcpReceiver::error() {
qDebug() << "Error: " << m_Server->errorString();
}
void TcpReceiver::readyRead() {
qDebug() << "Received data...";
// Now read data
QByteArray buffer;
if (m_Socket->canReadLine()) {
buffer = m_Socket->readLine();
qDebug() << "Got Data: " << buffer;
}
}

Using boost::thread to start/stop logging data

I'm currently trying to log real-time data by using boost::thread and a check box. When I check the box, the logging thread starts. When I uncheck, the logging thread stops. The problem arises when I check/uncheck repeatedly and very fast (program crashes, some files aren't logged, etc.). How can I write a reliable thread-safe program where these problems don't occur when repeatedly and quickly checking/unchecking? I also don't want to use join() since this temporarily stops the data input coming from the main thread. Below is a code snippet:
//Main thread
if(m_loggingCheckBox->isChecked())
{
...
if(m_ThreadLogData.InitializeReadThread(socketInfo))//opens the socket.
//If socket is opened and can be read, start thread.
m_ThreadLogData.StartReadThread();
else
std::cout << "Did not initialize thread\n";
}
else if(!m_loggingCheckBox->isChecked())
{
m_ThreadLogData.StopReadThread();
}
void ThreadLogData::StartReadThread()
{
//std::cout << "Thread started." << std::endl;
m_stopLogThread = false;
m_threadSendData = boost::thread(&ThreadLogData::LogData,this);
}
void ThreadLogData::StopReadThread()
{
m_stopLogThread = true;
m_ReadDataSocket.close_socket(); // close the socket
if(ofstreamLogFile.is_open())
{
ofstreamLogFile.flush(); //flush the log file before closing it.
ofstreamLogFile.close(); // close the log file
}
m_threadSendData.interrupt(); // interrupt the thread
//m_threadSendData.join(); // join the thread. Commented out since this temporarily stops data input.
}
//secondary thread
bool ThreadLogData::LogData()
{
...
while(!m_stopLogThread)
{
try {
//log the data to an output file
...
boost::this_thread::interruption_point();
} catch (boost::thread_interrupted& interruption) {
std::cout << "ThreadLogData::LogData(): Caught Interruption thread." << std::endl;
StopReadThread();
} catch (...) {
std::cout << "ThreadLogData::LogData(): Caught Something." << std::endl;
StopReadThread();
}
} // end while()
}

boost::asio::write does not seem to work while boost::asio::read is outstanding

I am using boost 1.52.0 32 bit libraries with OpenSSL 32 bit libraries with unmanaged Visual C++ 2008 for a new client I am writing to communicate with an existing server. My test machine uses Windows 8. I am using synchronous reads and writes. The code is built into a DLL that is accessed from C#, but all asio calls are done on unmanaged threads created with boost::thread_group.
What I have discovered is that when a synchronous read is waiting for data, then a synchronous write taking place in another thread appears to be blocked and will not go out - at least with the way I have things coded. So my question is - should a synchronous write be able to be completely executed while a synchronous read is waiting for data in another thread?
I have verified that I can write data out successfully when there is no pending read in another thread. I did this by freezing the thread the read was on right before it was about to read. The thread for writing then wrote a message out. I then thawed the read thread and it was able to successfully read the response back from the server about the message that was sent.
The following method is called by the create_thread method to handle reading messages off the wire from the server:
void SSLSocket::ProcessServerRequests()
{
// This method is responsible for processing requests from a server.
Byte *pByte;
int ByteCount;
size_t BytesTransferred;
boost::system::error_code Err;
Byte* pReqBuf;
string s;
stringstream ss;
//
try
{
ss << "ProcessServerRequests: Worker thread: " << Logger::NumberToString(boost::this_thread::get_id()) << " started.\n";
Log.LogString(ss.str(), LogInfo);
// Enable the handlers for the handshaking.
IOService->run();
// Wait for the handshake to be sucessfully completed.
do
{
Sleep(50);
} while (!HandShakeReady);
//
sClientIp = pSocket->lowest_layer().remote_endpoint().address().to_string();
uiClientPort = pSocket->lowest_layer().remote_endpoint().port();
ReqAlive = true;
// If the thread that handles sending msgs to all servers has not been created yet, then create that one.
// This thread is created just once to handle all outbound msgs to all servers.
WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread));
// Loop until the user quits, or an error is detected. The read method should wait until there is something to read.
do
{
pReqBuf = BufMang.GetPtr(MsgLenBytes);
boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, MsgLenBytes), boost::asio::transfer_exactly(MsgLenBytes), Err);
if (Err)
{
s = Err.message();
if ((s.find("short r")) == string::npos)
{
ss.str("");
ss << "SSLSocket::ProcessServerRequests: read(1) error = " << Err.message() << "\n. Terminating.\n\n";
Log.LogString(ss.str(), LogError);
}
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
}
else
{
// Get the number of bytes in the message.
pByte = pReqBuf;
B2I.B.B1 = *pByte++;
B2I.B.B2 = *pByte++;
B2I.B.B3 = *pByte++;
B2I.B.B4 = *pByte;
ByteCount = B2I.IntVal;
pReqBuf = BufMang.GetPtr(ByteCount);
// Do a synchronous read which will hang until the entire message is read off the wire.
BytesTransferred = boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, ByteCount), boost::asio::transfer_exactly(ByteCount), Err);
ss.str("");
ss << "SSLSocket::ProcessServerRequests: # bytes rcvd = " << Logger::NumberToString(BytesTransferred).c_str() << " from ";
ss << sClientIp.c_str() << " : " << Logger::NumberToString(uiClientPort) << "\n";
Log.LogString(ss.str(), LogDebug2);
Log.LogBuf(pReqBuf, (int)BytesTransferred, DisplayInHex, LogDebug3);
if ((Err) || (ByteCount != BytesTransferred))
{
if (Err)
{
ss.str("");
ss << "ProcessServerRequests:read(2) error = " << Err.message() << "\n. Terminating.\n\n";
}
else
{
ss.str("");
ss << "ProcessServerRequests:read(3) error - BytesTransferred (" << Logger::NumberToString(BytesTransferred).c_str() <<
") != ByteCount (" << Logger::NumberToString(ByteCount).c_str() << "). Terminating.\n\n";
}
Log.LogString(ss.str(), LogError);
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
break;
}
// Call the C# callback method that will handle the message.
Log.LogString("SSLSocket::ProcessServerRequests: sending msg to the C# client.\n\n", LogDebug2);
CallbackFunction(this, BytesTransferred, (void*)pReqBuf);
}
} while (ReqAlive);
Log.LogString("SSLSocket::ProcessServerRequests: worker thread done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::ProcessServerRequests: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
The following method is called by the create_thread method to handle sending messages to the server:
void SSLSocket::SendWorkerThread()
{
// This method handles sending msgs to the server. It is called upon 1st time class initialization.
//
DWORD WaitResult;
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " started.\n", LogInfo);
// Loop until the user quits, or an error of some sort is thrown.
try
{
do
{
// If there are one or more msgs that need to be sent to a server, then send them out.
if (SendMsgQ.Count() > 0)
{
Message* pMsg = SendMsgQ.Pop();
// Byte* pBuf = pMsg->pBuf;
const Byte* pBuf = pMsg->pBuf;
SSLSocket* pSSL = pMsg->pSSL;
int BytesInMsg = pMsg->BytesInMsg;
boost::system::error_code Error;
unsigned int BytesTransferred = boost::asio::write(*pSSL->pSocket, boost::asio::buffer(pBuf, BytesInMsg), Error);
string s = "SSLSocket::SendWorkerThread: # bytes sent = ";
s += Logger::NumberToString(BytesInMsg).c_str();
s += "\n";
Log.LogString(s, LogDebug2);
Log.LogBuf(pBuf, BytesInMsg, DisplayInHex, LogDebug3);
if (Error)
{
Log.LogString("SSLSocket::SendWorkerThread: error sending message - " + Error.message() + "\n", LogError);
}
}
else
{
// Nothing to send, so go into a wait state.
WaitResult = WaitForSingleObject(hEvent, INFINITE);
if (WaitResult != 0L)
{
Log.LogString("SSLSocket::SendWorkerThread: WaitForSingleObject event error. Code = " + Logger::NumberToString(GetLastError()) + ". \n", LogError);
}
}
} while (ReqAlive);
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::SendWorkerThread: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
So, if a synchronous write should be able to be executed while a synchronous read is pending in another thread, then can someone please tell me what my code is doing wrong.
Asio socket is not thread-safe, so you may not access it from different threads.
Use async_read and async_write instead.

QTcp[server and socket]: can't read file sent

Good morning, I’m looking for an example about sending a file from one pc to an other with QTcpSocket. I tried to create my own code. I have an application, in which, the user will choose a file from his DD ( all types) and send it to the TcpServer, this server will then send this file to the other clients.But, I have a problem, when i choose the file and i send it, in the client’s side, i have this message: file is sending , but in the server’s side, it shows me that the file isn’t recieved with it’s totaly bytes.
Any suggestion please. This is the function for sending the file in the client’s side:
void FenClient::on_boutonEnvoyer_2_clicked()
{
QString nomFichier = lineEdit->text();
QFile file(lineEdit->text());
if(!file.open(QIODevice::ReadOnly))
{
qDebug() << "Error, file can't be opened successfully !";
return;
}
QByteArray bytes = file.readAll();
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out << quint32(0);
out << nomFichier;
out << bytes;
out.device()->seek(0);
out << quint32((block.size() - sizeof(quint32)));
qDebug() << "Etat : envoi en cours...";
listeMessages->append("status : sending the file...");
socket->write(block);
}
and the server side:
void FenServeur::datarecieved()
{
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
if(socket == 0)
{
qDebug() << "no Socket!";
return;
}
forever
{
QDataStream in(socket);
if(blockSize == 0)
{
if(socket->bytesAvailable() )
{
qDebug() << "Error < sizeof(quint32))";
return;
}
in >> blockSize;
}
if(socket->bytesAvailable() < blockSize)
{
qDebug() << "data not recieved with its total bytes";
return;
}
qDebug() << "!!!!!!";
QByteArray dataOut;
QString nameFile;
in >> nameFile >> dataOut;
QFile fileOut(nameFile);
fileOut.open(QIODevice::WriteOnly);
fileOut.write(dataOut);
fileOut.close();
blockSize = 0;
}
}
void FenServeur::sendToAll(const QString &message)
{
QByteArray paquet;
QDataStream out(&paquet, QIODevice::WriteOnly);
out << (quint32) 0;
out << message;
out.device()->seek(0);
out << (quint32) (paquet.size() - sizeof(quint32));
for (int i = 0; i < clients.size(); i++)
{
clients[i]->write(paquet);
}
}
So i can't write the file that the server recieved into a new file.
Any suggestion please!! and thanks in advance
Your code is waiting for the other side, but the other side is waiting for you. Any protocol that allows both sides to wait for each other is fundamentally broken.
TCP allows the sender to wait for the receiver but does not allow the receiver to wait for the sender. This makes sense because not allowing the sender to wait for the receiver requires an unlimited amount of buffering. Thus for any application layered on top of TCP, the receiver may not wait for the sender.
But you do:
if(socket->bytesAvailable() < blockSize)
{
qDebug() << "data not recieved with its total bytes";
return;
}
Here, you are waiting for the sender to make progress (bytesAvailable to increase) before you are willing to receive (pull data from the socket). But the sender is waiting for you to make progress before it is willing to send more data. This causes a deadlock. Don't do this.
Receive as much data as you can, as soon as you can, whenever you can. Never insist on receiving more data over the network before you will pull already received data from the network stack.

Resources