Socket nicht blockend lesen



  • Hallo,
    ich habe hier einen Socket (um genau zu sein einen serial_port) und will eine Klasse schreiben mit der es möglich ist nicht blockend zu lesen, sprich alles was da ist soll zurück gegeben werden und fertig. Das scheint jedoch deutlich schwerer zu sein als zunächst gedacht.

    #ifndef BOOSTCOMPORT_HPP_
    #define BOOSTCOMPORT_HPP_
    
    #include <boost/asio.hpp>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    class BoostComPort
    {
    public:
    	BoostComPort();
    	~BoostComPort();
    	int open(std::string port, int baud);
    	int close();
    	bool isOpended();
    	int write(char* Data, unsigned int Length);
    	int read(char* data, unsigned int length, bool blocking=false);
    	boost::system::error_code& getLastError();
    
    private:
    	void onPortRead(const boost::system::error_code& error, std::size_t bytes_transferred);
    
    	char* data;
    	std::size_t bytes_transferred;
    	boost::asio::io_service io_service;
    	boost::asio::serial_port serialPort;
    	boost::system::error_code ec;
    	boost::system::error_code lastError;
    protected:
    
    };
    
    #endif
    
    #include "BoostComPort.hpp"
    #include <boost/bind.hpp>
    
    BoostComPort::BoostComPort():
    data(NULL),
    serialPort(io_service)
    {
    	lastError.clear();
    }
    
    BoostComPort::~BoostComPort()
    {
    	serialPort.close();
    }
    
    int BoostComPort::open(std::string port, int baud)
    {
    	if(serialPort.is_open())
    		serialPort.close();
    	serialPort.open(port);
    	boost::asio::serial_port_base::baud_rate baudRate(baud);
    	serialPort.set_option(baudRate);
    	char* tmp=new char[100]; // 5MBi of temporary buffer to clean the system buffers
    	int read;
    	do
    	{
    		read=this->read(tmp, 100, false);
    	} while(read>0);
    	delete[] tmp;
    	return 0;
    }
    
    boost::system::error_code& BoostComPort::getLastError()
    {
    	return lastError;
    }
    
    int BoostComPort::close()
    {
    	return 0;
    }
    
    bool BoostComPort::isOpended()
    {
    	return serialPort.is_open();
    }
    
    int BoostComPort::write(char* data, unsigned int length)
    {
    	size_t written=boost::asio::write(serialPort, boost::asio::buffer(data, length), boost::asio::transfer_all(), ec);
    	if(written!=length)
    		return -1;
    	return 0;
    }
    
    int BoostComPort::read(char* data, unsigned int length, bool blocking)
    {
    	// start async read
    	serialPort.async_read_some(boost::asio::buffer(data, length), boost::bind(&BoostComPort::onPortRead, this, _1, _2));
    	bytes_transferred=-1;
    	io_service.poll(); // read the data
    	// cancel all pending operations
    	serialPort.cancel();
    	return bytes_transferred;
    }
    
    void BoostComPort::onPortRead(const boost::system::error_code& error, std::size_t bytes_transferred)
    {
    	if(error==0)
    	{
    		this->bytes_transferred=bytes_transferred;
    		cout<<"Read some bytes..."<<endl;
    	}
    	else if(error!=boost::asio::error::operation_aborted)  // not cancelled
    	{
    		cerr<<"BoostComPort::onPortRead(): Unable to read from com port with error: "<<endl;
    		cerr<<"BoostComPort::onPortRead():   Error message: "<<error.message()<<endl;
    		cerr<<"BoostComPort::onPortRead():   Error code: "<<error<<endl;
    		lastError=ec;
    	}
    }
    
    #include <iostream>
    #include "BoostComPort.hpp"
    
    using namespace std;
    
    int main()
    {
    	BoostComPort comport;
    	comport.open("/dev/ttyUSB0", 19200);
    	for(int i=0; i<3; i++)
    	{
    		if(comport.write((char*)"test 1234567890", 15)==-1)
    			cout<<"Failed to write data to port!"<<endl;
    	}
    	for(unsigned int i=0; i<0xffffff; i++);
    	char* data=new char[100];
    	memset(data, 0, 100);
    	int read=comport.read(data, 15);
    	cout<<"read: "<<read<<endl;
    	cout<<"data: "<<data<<endl;
    	memset(data, 0, 100);
    	read=comport.read(data, 15);
    	cout<<"read: "<<read<<endl;
    	cout<<"data: "<<data<<endl;
    	memset(data, 0, 100);
    	read=comport.read(data, 15);
    	cout<<"read: "<<read<<endl;
    	cout<<"data: "<<data<<endl;
    	memset(data, 0, 100);
    	read=comport.read(data, 15);
    	cout<<"read: "<<read<<endl;
    	cout<<"data: "<<data<<endl;
    	return 0;
    }
    

    Soweit mein Code, Ausgabe sieht so aus:

    Read some bytes...
    read: 15
    data: test 1234567890
    read: -1
    data: test 1234567890
    read: -1
    data: test 1234567890
    read: -1
    data:
    

    Kann mir jemand sagen warum er bei den ersten drei Lesevorgängen zwar etwas liest, aber scheinbar die Methode onPortRead nur beim ersten mal aufruft? Daten stehen ja im Buffer drin. Und da er beim letzten mal dann keine Daten mehr drin stehen hat würde ich mal davon ausgehen, dass er alle im Systempuffer vorhanden Daten bei den ersten drei mal gelesen hat, mir diese jedoch nicht gibt bzw. mir nicht mitteilt, dass sie im Puffer stehen.

    Ach ja: RX/TX sind kurzgeschlossen, also alles was ich sende kommt wieder zurück (und das funktioniert auch sicher).



  • Ich glaube ich habe das Problem eben behoben. Ich verwende die cancel Methode nicht mehr, um read Vorgänge abzubrechen, statt dessen läuft nun immer ein Read Vorgang und startet sich dann quasi neu wenn er beendet wurde.

    #ifndef BOOSTCOMPORT_HPP_
    #define BOOSTCOMPORT_HPP_
    
    #include <boost/asio.hpp>
    #include <string>
    #include <iostream>
    #include <boost/timer.hpp>
    
    #define BUFFER_SIZE 1000000
    
    using namespace std;
    
    class BoostComPort
    {
    public:
    	BoostComPort();
    	~BoostComPort();
    	int open(std::string port, int baud);
    	int close();
    	bool isOpended();
    	int write(char* Data, unsigned int Length);
    	int read(char* data, int length, bool blocking=false, int timeout=-1 /* timeout in ms */);
    	boost::system::error_code& getLastError();
    
    private:
    	void onPortRead(const boost::system::error_code& error, std::size_t bytes_transferred);
    
    	char* buffer;
    	int currentContent;
    	bool executed;
    	boost::asio::io_service io_service;
    	boost::asio::serial_port serialPort;
    	boost::system::error_code ec;
    	boost::system::error_code lastError;
    protected:
    
    };
    
    #endif
    
    #include "BoostComPort.hpp"
    #include <boost/bind.hpp>
    
    BoostComPort::BoostComPort():
    currentContent(0),
    serialPort(io_service)
    {
    	lastError.clear();
    	buffer=new char[BUFFER_SIZE];
    }
    
    BoostComPort::~BoostComPort()
    {
    	serialPort.close();
    	delete[] buffer;
    }
    
    int BoostComPort::open(std::string port, int baud)
    {
    	if(serialPort.is_open())
    		serialPort.close();
    	serialPort.open(port);
    	boost::asio::serial_port_base::baud_rate baudRate(baud);
    	serialPort.set_option(baudRate);
    	serialPort.async_read_some(boost::asio::buffer(buffer+currentContent, BUFFER_SIZE-currentContent), boost::bind(&BoostComPort::onPortRead, this, _1, _2));
    	char* tmp=new char[100]; // 5MBi of temporary buffer to clean the system buffers
    	int read;
    	do
    	{
    		read=this->read(tmp, 100, false);
    	} while(read>0);
    	delete[] tmp;
    	return 0;
    }
    
    boost::system::error_code& BoostComPort::getLastError()
    {
    	return lastError;
    }
    
    int BoostComPort::close()
    {
    	serialPort.close();
    	return 0;
    }
    
    bool BoostComPort::isOpended()
    {
    	return serialPort.is_open();
    }
    
    int BoostComPort::write(char* data, unsigned int length)
    {
    	size_t written=boost::asio::write(serialPort, boost::asio::buffer(data, length), boost::asio::transfer_all(), ec);
    	if(written!=length)
    		return -1;
    	return 0;
    }
    
    int BoostComPort::read(char* data, int length, bool blocking, int timeout)
    {
    	boost::timer time;
    	do
    	{
    		executed=false;
    		io_service.poll(); // read new data
    		if(executed)
    			serialPort.async_read_some(boost::asio::buffer(buffer+currentContent, BUFFER_SIZE-currentContent), boost::bind(&BoostComPort::onPortRead, this, _1, _2));
    		if(timeout>0)
    		{
    			if((int)(time.elapsed()*1000)>timeout || !serialPort.is_open())
    				return -1;
    		}
    	} while(blocking && currentContent<length);
    	int toCopy;
    	if(length<currentContent)
    		toCopy=length;
    	else
    		toCopy=currentContent;
    	memcpy(data, buffer, toCopy);
    	memcpy(buffer, buffer+toCopy, currentContent-toCopy);
    	currentContent-=toCopy;
    	return toCopy;
    }
    
    void BoostComPort::onPortRead(const boost::system::error_code& error, std::size_t bytes_transferred)
    {
    	//cout<<"BoostComPort::onPortRead(): called"<<endl;
    	if(error==0)
    	{
    		currentContent+=bytes_transferred;
    		cout<<"Read some bytes: "<<bytes_transferred<<endl;
    	}
    	else if(error!=boost::asio::error::operation_aborted)  // not cancelled
    	{
    		cerr<<"BoostComPort::onPortRead(): Unable to read from com port with error: "<<endl;
    		cerr<<"BoostComPort::onPortRead():   Error message: "<<error.message()<<endl;
    		cerr<<"BoostComPort::onPortRead():   Error code: "<<error<<endl;
    		lastError=ec;
    	}
    	executed=true;
    }
    

Log in to reply