How to check boost socket write_some method ends or not - c++

I am trying to send some data by using boost socket.
TCPClient class's role is to make a connection cna can send data throw sendMessage method.
When I executed under code it does not work. However, it works when I debug it.
I think the problem is timing.
delete[] msg; works before sending msg.(just my thought)
so, I want to check whether msg is sent or not.
or any other good way.
client main() code
TCPClient *client = new TCPClient(ip, port);
client->sendMessage((char *)msg, 64 + headerLength + bodyLength);
delete[] msg;
under code is snedMessage method.
void TCPClient::sendMessage(const char *message, int totalLength) throw(boost::system::system_error) {
if(false == isConnected())
setConnection();
boost::system::error_code error;
this->socket.get()->write_some(boost::asio::buffer(message, totalLength), error);
if(error){
//do something
}
}

Your sendMessage() function is written incorrectly. You cannot expect that socket will send all of your data at once, you need a loop where you try to send, check how many bytes were sent, offset buffer (and update totalLength accordingly of course) if necessary and repeat until all data is sent. Or interrupt if there is error condition. You try to send only once, ignore result and assume that if there is no error then all data was sent. This is not a case. Stream socket may send one or two or whatever amount of bytes at a time, and your code needs to handle that.
Your code should be something like this:
while( totalLength ) {
boost::system::error_code error;
auto sz = this->socket.get()->write_some(boost::asio::buffer(message, totalLength), error);
if(error){
//do something and interrupt the loop
}
totalLength -= sz;
message += sz;
}

Related

C++ + linux handle SIGPIPE signal

Yes, I understand this issue has been discussed many times.
And yes, I've seen and read these and other discussions:
1
2
3
and I still can't fix my code myself.
I am writing my own web server. In the next cycle, it listens on a socket, connects each new client and writes it to a vector.
Into my class i have this struct:
struct Connection
{
int socket;
std::chrono::system_clock::time_point tp;
std::string request;
};
with next data structures:
std::mutex connected_clients_mux_;
std::vector<HttpServer::Connection> connected_clients_;
and the cycle itself:
//...
bind (listen_socket_, (struct sockaddr *)&addr_, sizeof(addr_));
listen(listen_socket_, 4 );
while(1){
connection_socket_ = accept(listen_socket_, NULL, NULL);
//...
Connection connection_;
//...
connected_clients_mux_.lock();
this->connected_clients_.push_back(connection_);
connected_clients_mux_.unlock();
}
it works, clients connect, send and receive requests.
But the problem is that if the connection is broken ("^C" for client), then my program will not know about it even at the moment:
void SendRespons(HttpServer::Connection socket_){
write(socket_.socket,( socket_.request + std::to_string(socket_.socket)).c_str(), 1024);
}
as the title of this question suggests, my app receives a SIGPIPE signal.
Again, I have seen "solutions".
signal(SIGPIPE, &SigPipeHandler);
void SigPipeHandler(int s) {
//printf("Caught SIGPIPE\n%d",s);
}
but it does not help. At this moment, we have the "№" of the socket to which the write was made, is it possible to "remember" it and close this particular connection in the handler method?
my system:
Operating System: Ubuntu 20.04.2 LTS
Kernel: Linux 5.8.0-43-generic
g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
As stated in the links you give, the solution is to ignore SIGPIPE, and CHECK THE RETURN VALUE of the write calls. This latter is needed for correct operation (short writes) in all but the most trivial, unloaded cases anyways. Also the fixed write size of 1024 that you are using is probably not what you want -- if your response string is shorter, you'll send a bunch of random garbage along with it. You probably really want something like:
void SendRespons(HttpServer::Connection socket_){
auto data = socket_.request + std::to_string(socket_.socket);
int sent = 0;
while (sent < data.size()) {
int len = write(socket_.socket, &data[sent], data.size() - sent);
if (len < 0) {
// there was an error -- might be EPIPE or EAGAIN or EINTR or ever a few other
// obscure corner cases. For EAGAIN or EINTR (which can only happen if your
// program is set up to allow them), you probably want to try again.
// Anything else, probably just close the socket and clean up.
if (errno == EINTR)
continue;
close(socket_.socket);
// should tell someone about it?
break; }
sent += len; }
}

Socket Write Failure when sending File

I've been stuck on this issue for awhile where I'm unable to send a file through a socket. I've sent other information just fine using this method, but the problem seems to appear when I try to send a PNG file as a string.
These are the methods I use to to send and receive information:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
// Wait for Write Confirmation
receive(socket, &response, 2);
}
// Receives Message from the specified Socket
string Server::receiveMessage(int socket)
{
// Read the Message Size from the Socket
int size;
receive(socket, &size, sizeof(size_t));
// Send Write Confirmation
send(socket, itoa(true), 2);
// Receive the Message from the Socket
char message[size];
receive(socket, message, size);
// Send Write Confirmation
send(socket, itoa(true), 2);
// Return the Message as a String
string msg(message);
return msg;
}
The send and receive methods are just relays for write and read respectively. I'm only doing error checking in those methods, and it's the send method that's telling me that the write isn't working. In case it matters, this is my send method:
// Sends a Data Packet to the specified Socket
int Server::send(int socket, void* data, int size)
{
// Write the Data to the Socket
int count = write(socket, data, size);
// Make sure the Write Succeeded
if(count == -1)
{
print("$f1Error: $f0Unable to Write to Socket $t1%i$t0\n", socket);
exit(1);
}
return count;
}
I should note that the Server operates as a Thread, therefore the above three functions are static. The Client also contains the same four networking functions.
The command line breaking this happens in a separate static function which I use to handle Clients. Here is the relevant portion of said method:
// Handles each Client with a Thread
void* Server::server_handleClient(void* arg)
{
// Determine the Socket Descriptor
int socket = *((int*) arg);
free(arg);
// Create the Rover
Rover* rover = new Rover();
// Loop Indefinitely
while(true)
{
...
// Take a Picture and Send it
sendMessage(socket, rover -> takePicture());
...
}
// Delete the Rover
delete rover;
// Close the Socket
close(socket);
// Return a Successful Status
return (void*) new int(0);
}
Here you can see that I make use of a method from another class I've created. Here is the takePicture method from the Rover class, which is where I actually grab the picture:
// Takes a Picture and Returns the Photo as a String
inline string Rover::takePicture()
{
// Open the Picture File
ifstream picture;
string filepath = "./Server/Pictures/" + getDirection() + ".png";
picture.open(filepath.c_str());
// Make sure the File Opened
if(!picture.is_open())
return "";
// Read the File into a String Buffer
stringstream buffer;
buffer << picture.rdbuf();
return buffer.str();
}
So in short, the Server gets a picture from the Rover which it then sends to a Client. When I check the contents of the string for the photo, it's all there. All possible photos are reasonable in size (the photo used for testing is 674,962 bytes, and the buffer size sent is 674,963 which is expected).
I've used these methods for sending various messages, and all of that worked fine. I'm able to send strings (Like "Hello World!") and integers just fine.
Is there something that I'm doing wrong? Is the file that I'm trying to send simply too large? Is there some information that I'm missing? I need help...
Edit:
I've made a few changes with a little progress. I made one small change to the sendMessage command. The current problem is that the picture isn't being sent properly.
New sendMessage function:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message, bool data = false)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Determine the Type of Data to Send
if(data)
{
// Write the Message Data to the Socket
send(socket, (char*) message.data(), message.length() + 1);
}
else
{
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
}
// Wait for Write Confirmation
receive(socket, &response, 2);
}
The Client's copy of this function has been updated to match as well.
Now that we're working on getting the PNG file saved, here's the function that deals with that as well:
// Handles each Client with a Thread
void* Client::client_handleServer(void* arg)
{
// Define Socket Variables
int socket = *((int*) arg);
free(arg);
...
// Export the Picture to the Client's Directory
message = receiveMessage(socket);
ofstream picture;
picture.open("./Client/Pictures/Picture.png", std::ifstream::binary);
picture << message;
picture.close();
...
}
Currently you are opening the file in textmode. that means any characters in the files which contain newlines "\n" are converted to new line + carriage returns "\r\n".
Open your file in binary mode, like so
picture.open(filepath.c_str(), std::ifstream::binary);
then it may work.
void Server::sendMessage(int socket, string message)
The problem is right here. Don't use string as a container for binary data. Pass the image around as a byte array. Same applies to this:
string Server::receiveMessage(int socket)
I eventually figured everything out in the long run.
Pictures are binary files, and I was using Strings which use ASCII Characters. The issue with this is that binary data does not always translate to ASCII, and Strings are terminated by null characters, whereas binary data can contain null data within it. Long story short, strings do not work.
To preserve the message handling I had in place, I ended up just converting the binary data to hexadecimal data (0-F) which could be displayed in a String.

TCP client in Boost asio

Im building a TCP client using Boost::asio Libs. My program has a write() thread that sends a command to the server
write(*_socket,boost::asio::buffer("sspi l1\n\n",sizeof("sspi l1\n\n")));
Then a read thread is started that reads from the buffer all the time, as there can be messages broadcasted from the server due to any other client
void TCP_IP_Connection::readTCP()
{
size_t l=0;
this->len=0;
boost::system::error_code error;
try
{//loop reading all values from router
while(1)
{
//wait for reply??
l=_socket->read_some(boost::asio::buffer(this->reply,sizeof(this->reply)),error);
if(error)
throw boost::system::system_error(error);
if(l>0)
{
this->dataProcess(l);
}
else
boost::this_thread::sleep(boost::posix_time::milliseconds(5000));
_io.run();
if(error==boost::asio::error::eof) //connection closed by router
std::cout<<"connection closed by router";
_io.reset();
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
This thread runs al time in a while(1) loop and is supposed to sleep when the received data length is less than zero. It reads all the data and calls the data parser function. After that the write thread is used to send another command, with read thread running. But instead of the required response the server sends back
? ""
ERROR: Unknown command
I tried using the wireshark. I can see the command being send properly. What can be mistake I'm doing here?
sizeof("sspi l1\n\n") returns 10, but I can only count 9 characters in that string.
Try this instead:
const std::string cmd("sspi l1\n\n");
write(*_socket,boost::asio::buffer(cmd, cmd.length()));
Or when you have it as a string it is enough to do
const std::string cmd("sspi l1\n\n");
write(*_socket,boost::asio::buffer(cmd));
The second argument specifies a maximum length of the string to use. But since it is a constant string, the second argument is not strictly necessary.

Authenticating users on a Qt Server

I am trying to implement an authentication system using C++/QtTcpSocket for a personal project (A Multiplayer Chess Game).
My friend suggested a method for verifying a user but I wanted to ask if there was an easier or better way. Coming from a Python background and mostly doing this project to develop a deeper understanding of C++.
I will post the method my friend suggested and ask for maybe a better solution.
He built it in a kind of pseudo code fashion. The server is mostly built, I am now hoping to implement Authentication
*cheers
void process_packet(PACKET *pkt)
{
switch(pkt->PacketID)
{
case 0: // let's say packet id 0 is the logon packet; packet contents are username and password
{
//let's say packet size is 101 bytes; packet id was already received, so get the other 100 bytes
unsigned char BUFFER[101] = {0}; // i always add an extra byte to the end of the buffer to allow for off-by-one errors ^_^
int result = recv_packet(pkt->cSocket, 100, BUFFER);
if(result <= 0)
return; // connection error; no packet data was received
unsigned char *UserName = BUFFER+0; //+0 is not neccessary, but the username starts at the beginning. just getting the point across.
unsigned char *PassWord = BUFFER+50;
//side note: if we did "unsigned long *blah = BUFFER+4" or something, we would have to make sure the byte order is right. network byte order is BIG ENDIAN
// WINDOWS byte order is LITTLE ENDIAN
result = QueryDatabase("SELECT username, password FROM chess_players WHERE username = '%s'", FILTER_INVALID_CHARS(UserName));
// check result
unsigned char ServerResponse[2] = {0};
if(result['password'] == PassWord)
{
ServerResponse[0] = 1; // packet id will be 1. the next byte can be 1 or 0 to indicate logon success or failure.
ServerResponse[1] = true; // so packet 0x0101 mean logon success, packet 0x0100 means logon failure
send_packet(pkt->cSocket, ServerResponse, 2);
} else {
ServerResponse[0] = 1;
ServerResponse[1] = false;
send_packet(pkt->cSocket, ServerResponse, 2);
}
}
break;
default:
{
// received an unknown packet id; send a packet to the client that indicates an error_status_t
unsigned char *ServerResponse[2] = {0};
ServerResponse[0] = 2; // packet id 2 means server error
ServerResponse[1] = 0; // error code 0 means 'unknown packet id'
send_packet(pkt_cSocket, ServerResponse, 2);
}
break;
}
delete pkt; // must delete pkt, was created with 'new' in get_client_packets()
}
This seems rather C-stylish and not like the Qt way of doing things.
There is no general answer to your question but my suggestions are the following:
Listen to the newConnection() signal of the QTcpServer. Your handler has to call the nextPendingConnection() to get the next client waiting in the queue. The first thing you will do is probably your user authentication.
Once authenticated, you keep the QTcpSocket in your list of active connections.
Take a look at e.g. the fortune client/server examples how to actually write/read packets.
You might also want to look into the stream operators << to serialize your objects. This is much easier and less error prone than the low-level method you posted. ALso, QDataStream will take care of host and network byte orders automatically.
If you have followed the fortune client/server examples, you should have a QTcpServer (Rfserver) with a QThread subclass (Rfdevice, its instance variable is called thread in the following code) that contains a QTcpSocket (listenSocket).
Having said that, in your server class, listen for incoming connections, my setup looks like this:
void Rfserver::incomingConnection(int socketDescriptor){
if(thread){ //if thread exists, there is probably still an open connection
if(thread->listenSocket){//if thread exists and the listenSocket is filled, there is definately an open connection
if(thread->listenSocket->state() == QAbstractSocket::UnconnectedState){
//but alas, it could just be in the unconnected state, if so kill it.
this->disconnect();
thread->terminate();
thread=0;
connected=false;
}//otherwise, do nothing, because the software is happily connected to a device
}
}
if(!thread){ //if no thread exists, we are by no means connected
thread = new rfdevice(socketDescriptor, this); //set up a new thread
//this first connection communicates the string from your socket to the server parent...use it if you want.
connect( thread, SIGNAL(RemoteButton(QString)),this,SLOT(remoteButton(QString)),Qt::BlockingQueuedConnection);
connect( thread, SIGNAL(error(QTcpSocket::SocketError)),this,SLOT(tcpError(QTcpSocket::SocketError)),Qt::AutoConnection);
connect( thread, SIGNAL(finished()), this, SLOT(threadZero())); //I have a threadZero function that deletes all the data then schedules the socket for deletion.
thread->start();
connected=true;
QString *welcome = new QString("Enter your password:\r\n");
echoCommand(welcome); //this is a function you will implement that sends the welcome message to the pending device.
}
}
Okay, so now, when a device tries to connect to the server the device is presented with "Enter your password:\r\n". Your device will respond to this with a password and username perhaps. But the Qt side of things would look like this:
/*
FUNCTION:read
this is a polling runloop that listens for data as long as the socket is connected or connecting. If a
write is ever scheduled, it will be called from this runloop..
*/
void Rfdevice::read(void){
while((listenSocket->state() == QAbstractSocket::ConnectedState) || (listenSocket->state() == QAbstractSocket::ConnectingState)){
//if there is data available to send write it to the socket
if(dataToSend) this->write();
if(listenSocket->waitForReadyRead(50)) readBytes();
//wait for 50ms for data from the device
//if there is ever data available to be read, read it.
}
}
Your device responds with a username/password in the format username---password\r\n. Then the socket does this:
/*
FUNCTION:readBytes
this is like a callback function because it only gets called when there is data available for read.
It basically converts the data to a string.
*/
void Rfdevice::readBytes(void){
QByteArray newData;
newData = listenSocket->readAll();
QString *recieved = new QString(newData);
QStringList userAndPass = recieved.split("---");//this is your delimiter
QString username = userAndPass.at(0);
QString password = userAndPass.at(1);
//NOW, check the username and password vs your SQL or wherever it's saved.
}
The pseudo-code is pretty complete on the particulars. Hopefully you can put it all together! Let me know if you need more code.

Consume only part of data in boost::asio basic_stream_socket::async_read_some handler

I am new into boost::asio so my question maight be dumb - sorry if it is such.
I am writing asynchronous server application with keepalive (multiple requests may be sent on single connection).
Connection handling routine is simple:
In a loop:
schedule read request with socket->async_read_some(buffer, handler)
from handler schedule write response with async_write.
The problem I am facing is that when
handler passed to async_read_some is called by on of io_service threads, buffers may actually contain more data than single request (e.g. part of next request sent by client).
I do not want to (and cannot if it is only part of request) handle this remaining bytes at the moment.
I would like to do it after handling previous request is finished.
It would be easy to address this if I had the possiblity to reinject unnecessary remainging data back to the socket. So it is handled on next async_read_some call.
Is there such possiblity in boost::asio or do I have to store the remaining data somewhere aside, and handle it myself with extra code.
I think what you are looking for is asio::streambuf.
Basically, you can inspect your seeded streambuf as a char*, read as much as you see fit, and then inform how much was actually processed by consume(amount).
Working code-example to parse HTTP-header as a client:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>
namespace asio = boost::asio;
std::string LINE_TERMINATION = "\r\n";
class Connection {
asio::streambuf _buf;
asio::ip::tcp::socket _socket;
public:
Connection(asio::io_service& ioSvc, asio::ip::tcp::endpoint server)
: _socket(ioSvc)
{
_socket.connect(server);
_socket.send(boost::asio::buffer("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"));
readMore();
}
void readMore() {
// Allocate 13 bytes space on the end of the buffer. Evil prime number to prove algorithm works.
asio::streambuf::mutable_buffers_type buf = _buf.prepare(13);
// Perform read
_socket.async_read_some(buf, boost::bind(
&Connection::onRead, this,
asio::placeholders::bytes_transferred, asio::placeholders::error
));
}
void onRead(size_t read, const boost::system::error_code& ec) {
if ((!ec) && (read > 0)) {
// Mark to buffer how much was actually read
_buf.commit(read);
// Use some ugly parsing to extract whole lines.
const char* data_ = boost::asio::buffer_cast<const char*>(_buf.data());
std::string data(data_, _buf.size());
size_t start = 0;
size_t end = data.find(LINE_TERMINATION, start);
while (end < data.size()) {
std::cout << "LINE:" << data.substr(start, end-start) << std::endl;
start = end + LINE_TERMINATION.size();
end = data.find(LINE_TERMINATION, start);
}
_buf.consume(start);
// Wait for next data
readMore();
}
}
};
int main(int, char**) {
asio::io_service ioSvc;
// Setup a connection and run
asio::ip::address localhost = asio::ip::address::from_string("127.0.0.1");
Connection c(ioSvc, asio::ip::tcp::endpoint(localhost, 80));
ioSvc.run();
}
One way of tackling this when using a reliable and ordered transport like TCP is to:
Write a header of known size, containing the size of the rest of the message
Write the rest of the message
And on the receiving end:
Read just enough bytes to get the header
Read the rest of the message and no more
If you know the messages are going to be of a fixed length, you can do something like the following:
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
void
Connection::readMore()
{
if (m_connected)
{
// Asynchronously read some data from the connection into the buffer.
// Using shared_from_this() will prevent this Connection object from
// being destroyed while data is being read.
boost::asio::async_read(
m_socket,
boost::asio::buffer(
m_readMessage.getData(),
MessageBuffer::MESSAGE_LENGTH
),
boost::bind(
&Connection::messageBytesRead,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
),
boost::bind(
&Connection::handleRead,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
}
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
std::size_t
Connection::messageBytesRead(const boost::system::error_code& _errorCode,
std::size_t _bytesRead)
{
return MessageBuffer::MESSAGE_LENGTH - _bytesRead;
}
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
void
Connection::handleRead(const boost::system::error_code& _errorCode)
{
if (!_errorCode)
{
/// Do something with the populated m_readMessage here.
readMore();
}
else
{
disconnect();
}
}
The messageBytesRead callback will indicate to boost::asio::async_read when a complete message has been read. This snippet was pulled from an existing Connection object from running code, so I know it works...