In a Linux environment, I have an application that gets a file descriptor from an API call to a driver. The following function is what I use to read the data read in by a card on my system. About 1 out of 10 reads fail. I am puzzled as to why, after a successful select, and checking to see if the read_fd is set, no data is returned.
int MyClass::Read(int file_descriptor)
{
unsigned short read_buffer[READ_BUFFER_SIZE];
fd_set read_set;
time_val timeout;
int return_value = 0;
int count = 0;
int status = -1;
// Initialize read file descriptor
FD_ZERO(&read_set)
// Add driver file descriptor
FD_SET(file_descriptor, &read_set)
// Set timeout
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
while(!count)
{
// Wait for data to be available to read
return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);
// Make sure an error or a timeout didn't occur
if (-1 == return_value)
{
cout << "an error occurred" << endl;
}
else if (0 == return_value)
{
cout << "a timeout occurred" << endl;
}
else
{
// If the read file descriptor is set, read in the data
if (FD_ISSET(file_descriptor, &read_set))
{
count = read(file_descriptor, read_buffer, sizeof(read_buffer));
// Double check that data was read in
if (!count)
{
cout << "read failed" << endl;
}
else
{
// Set status to success
status = 0;
}
}
}
}
return status;
}
A return value of 0 from read (your if (!count) check) does not mean that the read failed -- it means that the read succeeded and got an EOF.
In any case, select returning with the file descriptor set does not mean that a read of that fd will not fail -- it means that a read of that fd will not block, and will return something immediately, either failure or success.
You are not using select() correctly. It modifies the fd_set, and possibly the time_val, so you have to reset them on each loop iteration.
Also, you are not handling errors correctly. read() returns -1 on error, 0 on disconnect, and > 0 on bytes read. You are not handling error and disconnect conditions correctly.
Try something more like this instead:
int MyClass::Read(int file_descriptor)
{
unsigned short read_buffer[READ_BUFFER_SIZE];
fd_set read_set;
time_val timeout;
int return_value, count;
do
{
// Initialize read file descriptor
FD_ZERO(&read_set);
// Add driver file descriptor
FD_SET(file_descriptor, &read_set);
// Set timeout
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
// Wait for data to be available to read
return_value = select(file_descriptor + 1, &read_set, NULL, NULL, &timeout);
// Make sure an error or a timeout didn't occur
if (-1 == return_value)
{
cout << "select failed" << endl;
return -1;
}
if (0 == return_value)
{
cout << "select timed out" << endl;
continue; // or return, your choice...
}
// select() returned > 0, so the fd_set MUST be set,
// so no need to check it with FD_ISSET()...
// read in the data
count = read(file_descriptor, read_buffer, sizeof(read_buffer));
// Double check that data was actually read in
if (-1 == count)
{
cout << "read failed" << endl;
return -1;
}
if (0 == count)
{
cout << "peer disconnected" << endl;
return 0; // or -1, or whatever you want...
}
// success
break;
}
while (true);
return 0;
}
Related
Here's my entire program:
#include <iostream>
#include <windows.h>
int main() {
//? create the serial port file with read and write perms
HANDLE hPort = CreateFileW(L"COM3",
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (hPort == INVALID_HANDLE_VALUE) {
std::cout << "INVALID_HANDLE_VALUE/6\n";
if (GetLastError() == 2) {
std::cout << "serial port doesn't exist. error code: 2/ERROR_FILE_NOT_FOUND\n";
} else {
std::cout << "error occured with serial port file creation (CreateFileW). error code: " << GetLastError() << std::endl;
}
CloseHandle(hPort);
} else {
std::cout << "serial port created successfully (probably)\n";
}
DCB port_conf;
int err = GetCommState(hPort, &port_conf);
if (err == 0) {
std::cout << "GetCommState failed. error code: " << GetLastError() << "\n";
CloseHandle(hPort);
}
port_conf.BaudRate = 9600;
port_conf.Parity = NOPARITY;
port_conf.ByteSize = 8;
port_conf.StopBits = ONESTOPBIT;
port_conf.DCBlength = sizeof(port_conf);
err = SetCommState(hPort, &port_conf);
COMMTIMEOUTS timeouts_conf;
timeouts_conf.ReadIntervalTimeout = 1;
timeouts_conf.ReadTotalTimeoutConstant = 1;
timeouts_conf.ReadTotalTimeoutMultiplier = 1;
timeouts_conf.WriteTotalTimeoutConstant = 1;
timeouts_conf.WriteTotalTimeoutMultiplier = 1;
err = SetCommTimeouts(hPort, &timeouts_conf);
DWORD buffer_size_read;
char buffer_read[512]{};
int buffer_read_size;
char buffer_read_last[512]{};
while (1){
ReadFile(hPort,
buffer_read,
512,
&buffer_size_read,
0);
std::cout << buffer_read;
// if (buffer_read_last != buffer_read) {
// std::cout << buffer_read;
// }
// buffer_read_size = strlen(buffer_read);
// for (int i = 0; i <= buffer_read_size; i++) {
// buffer_read_last[i] = buffer_read[i];
// }
if (GetKeyState(VK_SPACE) != 0) {
break;
}
}
CloseHandle(hPort);
}
The problem with it is that everything is spit out too fast into cout. I made a miserable attempt at limiting this (it is commented out), but the program just doesn't do anything then. Another attempt was using the timeouts_conf.ReadIntervalTimeout, which Microsoft describes this way:
The maximum time allowed to elapse before the arrival of the next byte on the communications line, in milliseconds. If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.
but it didn't change anything. The serial port is continuously receiving data from a microcontroller in which I will not do the limiting for a pretty specific reason.
I need some sort of reliable way of not spitting everything out to cout at the speed of light. Thanks in advance
I checked this code several times and cannot understand why does poll() return immediately?
Here file is opened for read and should wait for event. How to make it wait for input?
#include <iostream>
#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
using namespace std;
ssize_t read_out_to_the_end(int fd){
char chunk[1024];
ssize_t ret = 0, n;
while((n = ::read(fd, chunk, sizeof chunk)) > 0){
ret += n;
cerr << "read chunk: " << n << " | ";
cerr.write(chunk, n);
cerr << endl;
}
if (n < 0) {
cerr << "err in read" << endl;
}
else if (ret == 0){
cerr << "nothing to read" << endl;
}
return ret;
}
int main() {
int bininfd = open("bin-in", O_RDONLY | O_CREAT);//, 0644/*S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH*/);
if (bininfd < 0) {
perror("err in open(binin)");
return -1;
}
struct pollfd pollfds[] = {
{bininfd, POLLIN, 0},
};
auto&[pfd] = pollfds;
while (1) {
pfd.revents = 0; // cleanup, shouldn't it be redundant
int pollret = poll(pollfds, 1, -1);
if (pollret > 0) {
if (pfd.revents & POLLIN) {
cerr << "(pfd.revents & POLLIN)" << endl;
read_out_to_the_end(pfd.fd);
}
} else if (pollret == 0) {
cerr << "poll timed out" << endl;
continue;
} else {
cerr << "check for error" << endl;
continue;
}
}
}
the output is
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
nothing to read
(pfd.revents & POLLIN)
............... etc ....................
live example
UPDATE:
read_out_to_the_end() fixed. Thanks to #RemyLebeau
it works (blocks) on fifos as I expect, but not on regular files. Why?
poll() or select() never block on regular files. They always return a regular file as "ready". If you want to use poll() to do what tail -f does, you're on the wrong track.
Quoting from the SUSv4 standard:
The poll() function shall support regular files, terminal and
pseudo-terminal devices, FIFOs, pipes, sockets and [OB XSR] STREAMS-based files. The behavior of poll() on
elements of fds that refer to other types of file is unspecified.
Regular files shall always poll TRUE for reading and writing.
Since using poll() or select() on regular files is pretty much useless, newer interfaces have tried to remedy that. On BSD, you could use kqueue(2) with EVFILT_READ, and on Linux inotify(2) with IN_MODIFY. The newer epoll(7) interface on Linux will simply error out with EPERM if you try to watch a regular file.
Unfortunately, neither of those is standard.
read_out_to_the_end() has several issues:
ret is uninitialized.
The while loop is incrementing n when it should be assigning it instead. But then if the while loop hits the EOF, if( n == 0) will be true even if data was actually read before hitting EOF.
chunk may be null-terminated, but it may also receive nulls too, depending on the input data. So it should not be written to cerr (why not cout?) using operator<<, use cerr.write() instead so that you can pass it the actual number of bytes read.
Try this instead:
ssize_t read_out_to_the_end(int fd){
char chunk[1024];
ssize_t ret = 0, n;
while((n = ::read(fd, chunk, sizeof chunk)) > 0){
ret += n;
cerr << "read chunk: " << n << " | ";
cerr.write(chunk, n);
cerr << endl;
}
if (n < 0) {
cerr << "err in read" << endl;
}
else if (ret == 0){
cerr << "nothing to read" << endl;
}
return ret;
}
int main() {
int bininfd = open("bin-in", O_RDONLY | O_CREAT);//, 0644/*S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH*/);
if (bininfd < 0) {
perror("err in open(binin)");
return -1;
}
pollfd pfd = {};
pfd.fd = bininfd;
pfd.events = POLLIN;
while (true) {
pfd.revents = 0; // cleanup, shouldn't it be redundant
int pollret = poll(&pfd, 1, -1);
if (pollret > 0) {
if (pfd.revents & POLLIN) {
cerr << "(pfd.revents & POLLIN)" << endl;
read_out_to_the_end(pfd.fd);
}
} else if (pollret == 0) {
cerr << "poll timed out" << endl;
continue;
} else {
cerr << "poll error " << errno << endl;
break;
}
}
}
Also, on a side note, the open() documentation says:
The mode argument specifies the file mode bits be applied when a new file is created. This argument must be supplied when O_CREAT or O_TMPFILE is specified in flags; if neither O_CREAT nor O_TMPFILE is specified, then mode is ignored. The effective mode is modified by the process's umask in the usual way: in the absence of a default ACL, the mode of the created file is (mode & ~umask). Note that this mode applies only to future accesses of the newly created file; the open() call that creates a read-only file may well return a read/write file descriptor.
At the time we are trying to create an interface for serial communication, to be able to communicate with a microprocessor.
Actually - everything works fine. Almost!
To be able to communicate with our controller, we need to sync up with it. To do this, we write a string: "?0{SY}13!", and the controller should then reply with "!0{SY}F5?" to accept the request for sync.
To do this, we use a writeData function (that works - we know that by using echo), and after that we use a readData to read the answer.
The problem is that, for some reason, it will not read anything. Though it returns 1 for success, the chars it reads is constanly " " (nothing).
Now comes the weird part - if we use an external terminal program to initialize the port (like putty), and then close the program, then everything works fine. It accepts the sync request, answers (and we can read it), and then we can do all that we want. But unless we use an external program to initialize the port, it doesn't work.
The constructor for the initializing the interface looks like this:
SerialIF::SerialIF(int baud, int byteSize, int stopBits, char* parity, int debug)
{
string coutport = getPort();
wstring wideport;
debug_ = debug; //Debuglevel
sync = false; //sync starts with false
error = false; //Error false as beginnging
//this is just for converting to the right type
for (int i = 0; i < coutport.length(); i++)
{
wideport += wchar_t(coutport[i]);
}
const wchar_t* port = wideport.c_str();
SerialIF::hserial = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hserial == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
if (debug_ != LOW)
{
cout << "[-] Port " << coutport << "doesn't exist." << endl;
}
}
if (debug_ != LOW)
{
cout << "[-] Handle error - is there another terminal active?" << endl;
}
error = true;
}
DCB dcbParms = { 0 };
dcbParms.DCBlength = sizeof(dcbParms);
if (!GetCommState(hserial, &dcbParms))
{
if (debug_ != LOW)
{
cout << "[-] Couldn't get status from port " << coutport << endl;
}
error = true;
}
if (!error)
{
setBaud(dcbParms, baud);
setParity(dcbParms, parity);
setByteSize(dcbParms, byteSize);
setStopbits(dcbParms, stopBits);
if (debug_ == HIGH)
{
cout << "[+] Serial port " << coutport << " has been activated. \nBaud-rate: " << baud << "\nParity: "
<< parity << "\nStop bits: " << stopBits << endl;
}
}
else if (debug_ != LOW)
{
cout << "[-] Port not initialized" << endl;
}
}
This should work - I really don't know why it shouldn't. It returns no errors, I've tried A LOT of error searching the last couple of days, I tried timeouts, I tried other ways of building it, but it all boils down to the same problem.
Why wont this initialize the port?
EDIT:
The output when trying to sync:
Can't post pictures due to lack of reputation. though it outputs as follows:
[+] Serial port COM1 has been activated.
Baud-rate: 9600
Parity: NONE
Stop bits: 1
[+] -> ?0{SY}13! is written to the port.
((And this is where it goes in to the infinite loop reading " "))
EDIT: code for read:
const int bytesToRead = 1; //I byte pr læsning
char buffer[bytesToRead + 1] = { 0 }; //Bufferen til data
DWORD dwBytesRead = 0; //Antal bytes læst
string store; //Store - den vi gemmer den samlede streng i
bool end = false; //Kontrolvariabel til whileloop.
while (end == false)
{
if (ReadFile(hserial, buffer, bytesToRead, &dwBytesRead, NULL))
/*Readfile læser fra interfacet vha. hserial som vi oprettede i constructoren*/
{
if (buffer[0] == '?') //Da protokollen slutter en modtaget streng med "?", sætter vi end til true
{ //Hvis denne læses.
end = true;
}
store += buffer[0];
}
else
{
if (debug_ != LOW)
{
cout << "[-] Read fail" << endl; //Hvis readfile returnerer false, så er der sket en fejl.
}
end = true;
}
}
if (debug_ == HIGH)
{
cout << "[+] Recieved: " << store << endl; //I forbindelse med debug, er det muligt at få udsrkevet det man fik ind.
}
recentIn = store; //RecentIN brugES i andre funktioner
if (verify()) //Som f.eks. her, hvor vi verificerer dataen
{
if (debug_ == HIGH)
{
cout << "[+] Verification success!" << endl;
}
return convertRecData(store);
}
else
{
if (debug_ != LOW)
{
cout << "[-] Verification failed." << endl;
}
vector <string> null; //Returnerer en string uden data i, hvis der er sket en fejl.
return null;
}
You never call SetCommState.
I'm not sure where your functions setBaud,setParity etc. come from, but I can't see how they can actually modify the serial port, as they don't have access to the comm device's handle.
ReadFile() can return success even when zero bytes are read. Use dwBytesRead to find the actual number of received characters.
while (ReadFile(hserial, buffer, 1, &dwBytesRead, NULL))
{
if (dwBytesRead != 0)
{
store += buffer[0];
if (buffer[0] == '?')
{
end = true;
break;
}
}
}
Had a similar problem between a PC and an arduino nano clone including a CH340. This post was the only one which discribes my problem very good.
I solved it by switching off DTR (data-terminal-ready) and RTS (request-to-send) flow control, which is normaly activated after (re)start the PC or plugging in the arduino. I found a descrition of this parameters in the documentation of DCB
I know that shis post is very old but maybe i can help somebody else with this idea/solution.
I've had my socket class working for a while now, but I wanted to add a timeout using select(). Seems pretty straight forward but I always have 0 returned from select(). I've even removed the select() check so it reads data regardless of select() and the data gets read, but select() still reports that data is not present. Any clue on how to get select() to stop lying to me? I've also set the socket to non-blocking. Thanks.
Code:
char buf [ MAXRECV + 1 ];
s = "";
memset ( buf, 0, MAXRECV + 1 );
struct timeval tv;
int retval;
fd_set Sockets;
FD_ZERO(&Sockets);
FD_SET(m_sock,&Sockets);
// Print sock int for sainity
std::cout << "\nm_sock:" << m_sock << "\n";
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(1, &Sockets, NULL, NULL, &tv);
std::cout << "\nretval is :[" << retval << "]\n\n";
// Check
if (FD_ISSET(m_sock,&Sockets))
std::cout << "\nFD_ISSET(m_sock,&Sockets) is true\n\n";
else
std::cout << "\nFD_ISSET(m_sock,&Sockets) is false\n\n";
// If error occurs
if (retval == -1)
{
perror("select()");
std::cout << "\nERROR IN SELECT()\n";
}
// If data present
else if (retval)
{
std::cout << "\nDATA IS READY TO BE READ\n";
std::cout << "recv ( m_sock, buf, MAXRECV, 0)... m_sock is " << m_sock << "\n";
int status = recv ( m_sock, buf, MAXRECV, 0 );
if ( status == -1 )
{
std::cout << "status == -1 errno == " << errno << " in Socket::recv\n";
return 0;
}
else if ( status == 0 )
{
return 0;
}
else
{
s = buf;
return status;
}
}
// If data not present
else
{
std::cout << "\nDATA WAS NOT READY, TIMEOUT\n";
return 0;
}
Your call to select is incorrect, as you have already discovered. Even though the first parameter is named nfds in many forms of documentation, it is actually one more than the largest file descriptor number held by any of the fd_sets passed to select. In this case, since you are only passing in one file descriptor, the call should be:
retval = select(m_sock + 1, &Sockets, NULL, NULL, &tv);
If you have an arbitrary number of sockets you are handling each in a different thread, you might find my answer to this question a better approach.
Whoops. Looks like I forgot to set select()'s int nfds:
working good now.
I have a strange problem. I have 2 binaries by the name cpp and another is called mnp_proxy_server.
cpp will start mnp_proxy_binary by calling a method executeScript. The code of this method is
int executeScript(string script, unsigned int scriptTmOut)
{
fd_set readfd;
const int BUFSIZE = 1024;
//stringstream strBuf;
char buf[ BUFSIZE];
time_t startTime = time(NULL);
struct timeval tv;
int ret, ret2 = 0;
FILE * pPipe = popen(script.c_str(), "r");
if (pPipe == NULL)
{
// cout << "popen() failed:"<< strerror(errno) << endl;
return -1;
}
while(1)
{
FD_ZERO(&readfd);
FD_SET(fileno(pPipe), &readfd);
/** Select Timeout Hardcode with 1 secs **/
tv.tv_sec = scriptTmOut;
tv.tv_usec = 0;
ret = select(fileno(pPipe)+1, &readfd, NULL, NULL, &tv);
if(ret < 0)
{
// cout << "select() failed " << strerror(errno) << endl;
}
else if (ret == 0)
{
// cout << "select() timeout" << endl;
break;
}
else
{
//cout << "Data is available now" <<endl;
if(FD_ISSET(fileno(pPipe), &readfd))
{
if(fgets(buf, sizeof(buf), pPipe) != NULL )
{
//cout << buf;
//strBuf << buf;
}
/** No Problem if there is no data ouput by script **/
#if 1
else
{
//ret2 = -1;
// cout << "fgets() failed " << strerror(errno) << endl;
break;
}
#endif
}
else
{
ret2 = -1;
// cout << "FD_ISSET() failed " << strerror(errno) << endl;
break;
}
}
/** Check the Script-timeout **/
if((startTime + scriptTmOut) < time(NULL))
{
// cout<<"Script Timeout"<<endl;
break ;
}
}
pclose(pPipe);
return ret2;
}
cpp is a server which listens on various ports 7001 and 7045. Once mnp_proxy_server is started it connects to 7001 port and starts sending messages.
Now coming to the problem. when i send ctr^c signal to cpp the signal is propagated to mnp_proxy_server and if i kill cpp process then all the ports on which cpp was listning now becomes the part of mnp_proxy_server process.
output of netstat after killing cpp process
[root#punith bin]# netstat -alpn | grep mnp_pr
tcp 0 0 0.0.0.0:7045 0.0.0.0:* LISTEN 26186/mnp_proxy_ser
tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN 26186/mnp_proxy_ser
I know it has something to do with the way I am executing the startup script of mnp_proxy_server through cpp.
There is a signal handler in both the binaries. And also to exit the socket select when ctr^c is pressed I have used pipes in select, so when ctr^c is pressed i close the write end of the pipe so that select is notified and select comes out and breaks the run loop.
Both of them are written in c++ and I am using rhel
Any clue will greatly help me in solving this. Thanking in advance.
You should set the flag CLOEXEC on the server sockets of cpp so that they are closed in the child process:
fcntl(fd, F_SETFD, FD_CLOEXEC);
While using socket like in your processes, I would suggest use fork and exec instead of popen to be able to close or manage all sockets between fork and exec, but the flag CLOEXEC might be enough to solve your problem.