I am trying to write a simple program to send single characters to a program via a COM port and read the answers I get back. I think I have working script where I can at least send commands via the com port, but when the ReadFile function begins it freezes. I have the comm timeout set for 100ms, so I don't think that it is locking the port, but I may be wrong. I am not getting any errors, and no warnings when I compile. I am very new to C++ (normally work with python), so please be as clear a possible with your answers.
// comtest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
#include <dos.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <windows.h>
int main(int argc, char **argv)
{
std::cout << "TOP! \n";
char buffer[1];
HANDLE file;
COMMTIMEOUTS timeouts;
DWORD read, written;
DCB port;
char init[] = ""; // e.g., "ATZ" to completely reset a modem.
// open the comm port.
file = CreateFile(L"COM1",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
std::cout << "file made \n";
// get the current DCB, and adjust a few bits to our liking.
memset(&port, 0, sizeof(port));
port.DCBlength = sizeof(port);
// set short timeouts on the comm port.
timeouts.ReadIntervalTimeout = 100;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 100;
int N = 10;
while (N > 1)
{
std::cout << "i'm in the loop!" << N << " loops left \n";
char command [1];
char * commandbuff;
std::cin >> command;
commandbuff = &command[1];
WriteFile(file, commandbuff, sizeof(commandbuff),&written, NULL);
Sleep(1000);
std::cout << "I just slept \n";
ReadFile(file, buffer, sizeof(buffer), &read, NULL);
N--;
}
// close up and go home.
CloseHandle(file);
return 0;
Your code doesn't appear to actually call SetCommTimeouts, so the timeouts you have defined would have no way to be applied.
Receiving data from a com port, don't start reading unless you have first sent a command or something that gets a response. Then, it's preferable to just read one byte at a time, but if you are sending modbus/at commands like I'm doing and know you're expecting 8 bytes back, then it's ok to use readfile to read 8 bytes. Most of the C++ com port examples have SetCommState, SetCommTimeouts, SetCommMask and WaitCommEvent before you can read that single byte.
Mine had an "&" on the second parameter of ReadFile. MS Visual C++ though.
Status = ReadFile(fileusb, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
Related
I am trying to write a named pipe reader to consume events from another application that provides named pipe streaming. I've run into a problem where I do not receive all events from the named pipe server when it sends batches of events very quickly together.
To simplify the issue and isolate it from the other activity that is being done by the server application, I decided to cut it out and write a simple c++ console app that writes to a named pipe and have my code listen to that.
After creating the test application to send batches of 10 messages with a three second wait between batches. The server indicates success for every write operation, but in the end my named pipe reader is consistently only receiving two messages.
I tried adding in delays between read operations using Sleep(), and that had no effect. However, when I tried adding delays between write operations using Sleep() on the server, it allows me to receive the messages in the client.
With batches of 10, a 1ms Sleep() will generally allow me to get all messages. If I send larger batches, I have to increase that Sleep() a bit.
I tried reading with a simple C# app as well, and that did not improve my ability to read all events without delays between write operations.
This is extremely confusing to me, because as I understand it the pipes are supposed to function as FIFO, so I have no idea where these messages are going.
Does anyone have some insight into why these writes are successful, but I am unable to get all of them in the reader? Or why adding a small delay between the writes allows me to read them all just fine?
I have included the writer and reader code below. I removed error handling/validation code to simplify the readability of it. For the server side piece, I got the pipe definition from the production app I'm trying to consume so I made sure the creation was the same.
Writer
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
using namespace std;
#define BUFFER_SIZE 65535
int main()
{
HANDLE namedPipeHandle;
BOOL fSuccess = FALSE;
DWORD bytesRead;
wstring pipeName = L"\\\\.\\pipe\\TestPipe";
namedPipeHandle = CreateNamedPipe(pipeName.c_str(),
PIPE_ACCESS_DUPLEX |FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFFER_SIZE,
BUFFER_SIZE,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
bool isConnected = ConnectNamedPipe(namedPipeHandle, NULL);
DWORD bytesWritten = 0;
string message = "TEST";
int count = 0;
while (true)
{
for (int i = 0; i < 10; i++)
{
fSuccess = WriteFile(namedPipeHandle, message.c_str(), message.size(), &bytesWritten, NULL);
if (fSuccess)
{
count++;
}
//Sleep(1); // Adding the Sleep(1) allows me to receive each batch of 10 in the client.
}
cout << "Sent " << count << " total messages" << endl;
Sleep(3000);
}
}
Reader
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
using namespace std;
#define BUFFER_SIZE 65535
int main()
{
HANDLE hPipe;
char buffer[BUFFER_SIZE];
DWORD bytesRead;
wstring pipeName = L"\\\\.\\pipe\\TestPipe";
hPipe = CreateFile(pipeName.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
int receivedCount = 0;
int failedCount = 0;
do
{
bool fSuccess;
fSuccess = ReadFile(hPipe, buffer, BUFFER_SIZE, &bytesRead, NULL);
if (fSuccess)
{
receivedCount++;
}
else
{
failedCount++;
}
cout << "Total Events Received: " << receivedCount << endl;
cout << "Total Events Failed: " << failedCount << endl;
} while (true);
CloseHandle(hPipe);
return 0;
}
Hey I am trying to interface with the xbees I have connected to my windows machine. I am able to write to the end device through the coordinator in AT mode, and can see the data streamed to my XCTU console. However, I am having trouble understanding how to read that incoming data.
The code I am currently using is below. Essentially the only part that is crucial is the last 5 lines or so (Specifically the read and write file lines), but I am going to post all of it just to be thorough. How do I read the data I sent to the xbee over the com port? The data I sent was simply 0x00-0x0F.
I think I am misunderstanding how the read file functions. I am assuming that the bits I send to the xbee is stored in a buffer which can than be read one at a time. Is that correct? Or do I need to write the entire byte than read the data available? Im sorry if my train of though is confusing, I am fairly new to serial communication. Any help is appreciated.
#include <cstdlib>
#include <windows.h>
#include <iostream>
using namespace std;
/*
*
*/
int main(int argc, char** argv) {
int n = 8; // Amount of Bytes to Read
HANDLE hSerial;
HANDLE hSerial2;
hSerial = CreateFile("COM3",GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);// dont need to GENERIC _ WRITE
hSerial2 = CreateFile("COM4",GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);// dont need to GENERIC _ WRITE
if(hSerial==INVALID_HANDLE_VALUE || hSerial2==INVALID_HANDLE_VALUE){
if(GetLastError()==ERROR_FILE_NOT_FOUND){
//serial port does not exist. Inform user.
cout << "Serial port error, does not exist" << endl;
}
//some other error occurred. Inform user.
cout << "Serial port probably in use" << endl;
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
cout << "error getting state" << endl;
}
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams)){
cout << "error setting serial port state" << endl;
}
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier =10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hSerial, &timeouts)){
cout << "Error occurred" << endl;
}
DWORD dwBytesWritten = 0;
DWORD dwBytesRead = 0;
unsigned char oneChar;
for (int i=0; i<16; i++)
{
oneChar=0x00+i;
WriteFile(hSerial, (LPCVOID)&oneChar, 1, &dwBytesWritten, NULL);
ReadFile (hSerial2, &oneChar, 1, &dwBytesRead, NULL); // what I tried to do, just outputs white space
}
CloseHandle(hSerial);
return 0;
}
In your statement:
ReadFile (hSerial2, &oneChar, 1, &dwBytesRead, NULL);
You need to check the value of dwBytesRead to see if you actually read any bytes. Maybe on one side of the connection you want a simple program to send a byte every second. On the other end, you want to check for available bytes and dump them as they come in.
What's likely happening in your program is that you're filling an outbound serial buffer in a short amount of time, not waiting long enough to read any data back, and then exiting the loop and closing the serial port, likely before it finishes sending the queued data. For example, write before your CloseHandle() call, you could add:
COMSTAT stat;
if (ClearCommError(hCom, NULL, &stat))
{
printf("%u bytes in outbound queue\n", (unsigned int) stat.cbOutQue);
}
And see whether your closing the handle before it's done sending.
I am about to program a c++ GUI application (wxWidgets) to control an Arduino. I want to use the SerialClass.h and SerialClass.cpp from the Arduino playground site (http://playground.arduino.cc/Interfacing/CPPWindows). I already built a console application with these .h and .cpp files that is working fine. Lately, I somehow get a strange error message:
In constructor Serial::Serial(char*)
error: cannot convert 'char*' to 'const WCHAR*' for argument '1' to 'void* CreateFileW(const WCHAR*, DWORD, DWORD, _SECURITY_ATTRIBUTES*, DWORD, DWORD, void*)'
I don't get that message. What should be changed in the SerialClass.h or SerialClass.cpp for it to be working? The Arduino code is fine. For completeness I attach the c++ code for the console application. A lot of google-fu was to no avail.
#include <stdio.h>
#include <tchar.h>
#include "SerialClass.h" // Library described above
#include <string>
#include <iostream>
using namespace std;
bool weiter = true;
int dummy1 = 0;
int _tmain(int argc, _TCHAR* argv[]) {
cout << "*** This is my Arduino LED app! ***\n" << endl;
//Serial* SP = new Serial("COM4");
//Serial serial("COM4");
Serial serial("COM4");
if (serial.IsConnected())
//printf("We are connected\n");
cout << "We are connected!\n" << endl;
while (weiter == true) {
cout << "Press 1 for LED on; press 0 for LED off!" << endl;
cin >> dummy1;
if (dummy1 == 1) {
if (serial.IsConnected()){
serial.WriteData("o",1);
cout << "LED is on!" << endl;
cout << "Do you want to continue? 1 for continue, 0 for exit!" << endl;
//printf("\nData sent successfully!\n");
cin >> weiter;
}
}
else {
serial.WriteData("p", 1 );
cout << "LED is off!" << endl;
cout << "Do you want to continue? 1 for continue, 0 for exit!" << endl;
cin >> weiter;
}
}
if (weiter == 1)
{
weiter = true;
}
if (weiter == 0) {
weiter = false;
return 0;
}
}
EDIT: Here is the code for SerialClass.h and Serial.cpp:
#include "SerialClass.h"
Serial::Serial(char *portName)
{
//We're not yet connected
this->connected = false;
//Try to connect to the given port throuh CreateFile
this->hSerial = CreateFile(portName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//Check if the connection was successfull
if(this->hSerial==INVALID_HANDLE_VALUE)
{
//If not success full display an Error
if(GetLastError()==ERROR_FILE_NOT_FOUND){
//Print Error if neccessary
printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else
{
//If connected we try to set the comm parameters
DCB dcbSerialParams = {0};
//Try to get the current
if (!GetCommState(this->hSerial, &dcbSerialParams))
{
//If impossible, show an error
printf("failed to get current serial parameters!");
}
else
{
//Define serial connection parameters for the arduino board
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
//Setting the DTR to Control_Enable ensures that the Arduino is properly
//reset upon establishing a connection
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
//Set the parameters and check for their proper application
if(!SetCommState(hSerial, &dcbSerialParams))
{
printf("ALERT: Could not set Serial Port parameters");
}
else
{
//If everything went fine we're connected
this->connected = true;
//Flush any remaining characters in the buffers
PurgeComm(this->hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);
//We wait 2s as the arduino board will be reseting
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
And here is the SerialClass.h:
#ifndef SERIALCLASS_H_INCLUDED
#define SERIALCLASS_H_INCLUDED
#define ARDUINO_WAIT_TIME 2000
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class Serial
{
private:
//Serial comm handler
HANDLE hSerial;
//Connection status
bool connected;
//Get various information about the connection
COMSTAT status;
//Keep track of last error
DWORD errors;
public:
//Initialize Serial communication with the given COM port
Serial(char *portName);
//Close the connection
~Serial();
//Read data in a buffer, if nbChar is greater than the
//maximum number of bytes available, it will return only the
//bytes available. The function return -1 when nothing could
//be read, the number of bytes actually read.
int ReadData(char *buffer, unsigned int nbChar);
//Writes data from a buffer through the Serial connection
//return true on success.
bool WriteData(char *buffer, unsigned int nbChar);
//Check if we are actually connected
bool IsConnected();
};
#endif // SERIALCLASS_H_INCLUDED
Serial serial("COM4");
That line passes a char*. To pass a WCHAR* instead you need to change it to:
Serial serial(L"COM4");
the CreateFile() function in your constructor requires a const WCHAR * so you somehow need to convert you const char * to const wchar* .
here is a post to do the conversion.
Although I don't have much info on this , the post describes several ways to do it One of them being :
char *p="D:\\"; //just for proper syntax highlighting ..."
const WCHAR *pwcsName;
// required size
int nChars = MultiByteToWideChar(CP_ACP, 0, p, -1, NULL, 0);
// allocate it
pwcsName = new WCHAR[nChars];
MultiByteToWideChar(CP_ACP, 0, p, -1, (LPWSTR)pwcsName, nChars);
// use it....
// delete it
delete [] pwcsName;
}
I am attempting to read a message that was sent on one COM port and received on another. The two ports are connected via two USB to Serial converters. When I attempt to read the transmitted message I get this:
Tx Baud rate: 9600
Rx Baud rate: 9600
Attempting to read...
Hello, is ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠á☼
Done...
Press any key to continue . . .
The message should read "Hello, is there anybody out there!?"
we is the code I have written:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <Windows.h>
typedef struct COMDevice {
HANDLE deviceHandle;
DWORD actualBytesReadOrWritten;
int deviceStatus;
std::string message;
DCB controlSettings;
} COMDevice;
int main(int argc, char *argv[]) {
// create new device
COMDevice *comWriter = new COMDevice;
COMDevice *comReader = new COMDevice;
// setup
comWriter->deviceHandle = NULL;
comWriter->actualBytesReadOrWritten = 0;
comWriter->deviceStatus = 0;
comWriter->message = "Hello, is there anybody out there!?";
comReader->deviceHandle = NULL;
comReader->actualBytesReadOrWritten = 0;
comReader->deviceStatus = 0;
comReader->message = "";
// open COM1 for writing
comWriter->deviceHandle = CreateFile(TEXT("COM5"), GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0);
if(comWriter->deviceHandle == INVALID_HANDLE_VALUE) {
std::cout << "Error occurred opening port for writing...\n";
return (int)GetLastError();
}
// open COM4 for reading
comReader->deviceHandle = CreateFile(TEXT("COM4"), GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
if(comReader->deviceHandle == INVALID_HANDLE_VALUE) {
std::cout << "Error occurred opening port for reading...\n";
return (int)GetLastError();
}
// check baud rates
if(GetCommState(comWriter->deviceHandle, &comWriter->controlSettings) == 0 ||
GetCommState(comReader->deviceHandle, &comReader->controlSettings) == 0) {
std::cout << "Error occurred getting the comm state...\n";
return (int)GetLastError();
}
else {
std::cout << "Tx Baud rate: " << comWriter->controlSettings.BaudRate << std::endl;
std::cout << "Rx Baud rate: " << comReader->controlSettings.BaudRate << std::endl;
}
// write message to serial port
comWriter->deviceStatus = WriteFile(comWriter->deviceHandle, comWriter->message.c_str(),
comWriter->message.length(), &comWriter->actualBytesReadOrWritten, NULL);
if(comWriter->deviceStatus == FALSE) {
std::cout << "Error occurred writing to port..\n";
return (int)GetLastError();
}
// wait a few
int i = 0, count = 4000;
while(i < count) { i++; }
// read
std::cout << "Attempting to read...\n";
char buffer[1024];
comReader->deviceStatus = ReadFile(comReader->deviceHandle, buffer, 1023,
&comReader->actualBytesReadOrWritten, NULL);
if(comReader->deviceStatus == FALSE) {
return (int)GetLastError();
}
std::cout << buffer << std::endl;
// close handles
(void)FlushFileBuffers(comReader->deviceHandle);
(void)CloseHandle(comWriter->deviceHandle);
(void)CloseHandle(comReader->deviceHandle);
// clean up...
delete comWriter;
delete comReader;
std::cout << "Done...\n";
return 0;
}
I also use the DCB structure to check the baud rates at both ends...they match. Is there something else I may be missing?
When you read from the serial port with
ReadFile(comReader->deviceHandle, buffer, 1023,
&comReader->actualBytesReadOrWritten, NULL);
the actual number of bytes read is stored in comReader->actualBytesReadOrWritten (the 4th parameter). But you are not using it for printing. The end result is that you read a few bytes, and then you try to print them, but since they are not NUL-terminated, you print the text and a lot of garbage, until it happens to find a NUL character (or crash).
The easy solution is to put a NUL character just after the ReadFile:
buffer[comReader->actualBytesReadOrWritten] = '\0';
But then, there is actually the problem that you did not receive all the bytes yet. There are a few ways to ensure that all the data has been read, retry, wait for a while... retry again...
Hint
The character '╠', if you look for it in the old OEM codepage, it is byte 0xCC, (it will be 'Ì' with ANSI western codepage) that is the byte VC++ uses to fill uninitialized stack space in debug builds. So a lot of these characters strongly suggest an uninitialized local buffer.
I'm trying to use the select function to accept input but every 2 seconds do something else if the user hasn't entered anything. The code below is waiting two seconds the first time select() is reached but once it prints the first "timed out" message it rapidly keep printing out "timed out" without waiting for 2 seconds, basically entering an infinite loop. Anyone know what the problem is? Thanks for any help.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
const int STDIN = 0;
int main(int argc, int *argv[])
{
struct timeval tv;
fd_set readfds, master;
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_ZERO(&master);
FD_SET(STDIN, &readfds);
FD_SET(STDIN, &master);
string buffer = "";
while(buffer != "quit"){
readfds = master;
if(select(STDIN+1, &readfds, NULL, NULL, &tv) == -1) perror("select");
if (FD_ISSET(STDIN, &readfds)){
getline(cin, buffer);
cout << "You entered: " << buffer << endl;
}else
cout << "Timed out.\n" << endl;
}
return 0;
}
per man: select() may update the timeout argument to indicate how much time was left. pselect() does not change this argument.
This implies that if it times out after 2 seconds it could set your tv_sec to 0.
If both of the fields of timeval are 0 it will return immediately.
Try setting your timeout every loop inside the while() to insure it's not getting overwritten.
I do some change based on your code, after select tv will be changed.
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
using namespace std;
const int STDIN = 0;
int main(int argc, int *argv[])
{
struct timeval tv,tv1;
fd_set readfds, master;
tv.tv_sec = 2;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_ZERO(&master);
FD_SET(STDIN, &readfds);
FD_SET(STDIN, &master);
string buffer = "";
while(buffer != "quit"){
readfds = master;
memcpy(&tv1, &tv, sizeof(tv));
if(select(STDIN+1, &readfds, NULL, NULL, &tv1) == -1) perror("select");
if (FD_ISSET(STDIN, &readfds)){
getline(cin, buffer);
cout << "You entered: " << buffer << endl;
}else
cout << "Timed out.\n" << endl;
}
return 0;
}
If you ever read the source of select.c in linux kernel, you'll find that in the select() call, the last parameter timeout will be set to zero after it is used.
So, you should set the value of tv inside the loop, before each call of select().
If memory serves, the call to select() can change the value of tv to indicate the time remaining. You should reinitialize tv before each call to select().
You must put
FD_ZERO(&readfds);
FD_SET(STDIN, &readfds);
into your loop. Assigning: readfds = master; will not always work (actually I'm pretty sure it will not work in every platform - depending on the definition of fd_et).
also setting tv in a loop is a good idea.