I am trying to read a serial response from a hardware device. The string I read is long and I only need a portion of it. To get to portion of the string I want I use std::string.substr(x,y); . The problem I run into however is sometimes I get an exception error because the buffer I am reading from doesn't have y characters. Here is the code I use now to read values:
while(1)
{
char szBuff[50+1] = {0};
char wzBuff[14] = {"AT+CSQ\r"};
DWORD dZBytesRead = 0;
DWORD dwBytesRead = 0;
if(!WriteFile(hSerial, wzBuff, 7, &dZBytesRead, NULL))
std::cout << "Write error";
if(!ReadFile(hSerial, szBuff, 50, &dwBytesRead, NULL))
std::cout << "Read Error";
std:: cout << szBuff;
std::string test = std::string(szBuff).substr(8,10);
std::cout << test;
Sleep(500);
I am issuing the command "AT+CSQ". This returns:
N, N
OK
It returns two integer values seperated by a comma followed by a new line, followed by "OK".
My question is, how can I make sure I read all values from the serial port before grabbing a substring? From what I understand, the last character received should be a new line.
The interface of your ReadFile function seems to provide you with the number of bytes read. If you know the length that is expected, you should loop trying reading from the file (probably port descriptor) until the expected number of bytes is read.
If the length of the response is not known, you might have to read and check in the read buffer whether the separator token has been read or not (in this case your protocol seems to indicate that a new-line can be used to determine EOM --end of message)
If you can use other libraries, I would consider using boost::asio and the read_until functionality (or the equivalent in whatever libraries you are using). While the code to manage this is not rocket science, in most cases there is no point in reinventing the wheel.
As you said yourself in the last line, you know that the terminator for the response is a new line character. You need to read from the serial until you receive a new line somewhere in the input. Everything you received from the previous new line to the current new line is the response, with everything after the current new line is part of the next response. This is achieved by reading in a loop, handling each response as it is discovered:
char* myBigBuff;
int indexToBuff = 0;
int startNewLine = 0;
while (ReadFile(hSerial, myBigBuff + indexToBuff, 100, &dwBytesRead, NULL))
{
if (strchr(myBigBuff, '\n') != NULL)
{
handleResponse(myBigBuff + startNewLine, indexToBuff + dwBytesRead);
startNewLine = indexToBuff + dwBytesRead;
}
// Move forward in the buffer. This should be done cyclically
indexToBuff += dwBytesRead;
}
This is the basic idea. You should handle the left overs characters via any way you choose (cyclic buffer, simple copy to a temp array, etc.)
You should use ReadFile to read a certain amount of bytes per cycle into your buffer. This buffer should be filled until ReadFile reads 0 bytes, you have reached your \n or \r\n characters, or filled your buffer to the max.
Once you have done this, there would be no need to substr your string and you can iterate through your character buffer.
For example,
while (awaitResponse) {
ReadFile(hSerial, szBuff, 50, &dwBytesRead, NULL);
if (dwBytesRead != 0) {
// move memory from szBuff to your class member (e.g. mySerialBuff)
} else {
// nothing to read
if (buffCounter > 0) {
// process buffer
}
else {
// zero out all buffers
}
}
}
Old question, but I modified #Eli Iser code to:
while (ReadFile(hSerial, myBigBuff + indexToBuff, 1, &dwBytesRead, NULL)) {
if (strchr(myBigBuff, '-') != NULL || dwBytesRead < 1)
break;
// Move forward in the buffer. This should be done cyclically
indexToBuff += dwBytesRead;
}
if (indexToBuff != 0) {
//Do whatever with the code, it received successfully.
}
Related
i have this piece of code :
for (;;) {
BOOL ReadSuccess = ReadFile(rdPipe, StdOutBuffer, 8192, &dwRead, NULL);
if (strlen(StdOutBuffer) <= 0) {
Sleep(100);
send(sock, RECIEVE_BREAK, strlen(RECIEVE_BREAK), 0);
break;
}
else if (!ReadSuccess || dwRead == 0) {
Sleep(100);
send(sock, RECIEVE_BREAK, strlen(RECIEVE_BREAK), 0);
break;
}
else {
send(sock, StdOutBuffer, strlen(StdOutBuffer), 0);
}
cout << StdOutBuffer << endl;
cout << "\n\n\n\n";
}
And i have problem with doubled output in my StdOutBuffer, for example, when i call "tasklist" i've got some parts twice.
Dont u someone know where can be problem?
The function ReadFile() doesn't read a null terminated c-string, but a block of (here 8192) chars without any guarantee to have a null terminator.
Therefore, calling strlen(StdOutBuffer) is not guaranteed to give any useful result. You should rather rely on dwRead instead. If you read text data that might be not null terminated (in the file or, as it seems, via the pipe), you should add it yourself:
StdOutBuffer[dwLen] = '\0';
This works even if the read fails, because the first thing ReadFile() does is to set the length to 0. Note that your buffer should then have a size of at least 8192+1 char.
Why do you get the error ? Suppose you receive a full line of data, and that fortunately, it includes some null terminator. You'd then process it. Suppose now that in the next iteration you'd only receive a couple of chars (e.g. just a newline): the remaining of the buffer would not overwritten, and as you'd not have a null terminator at the end of the new bytes read, strlen() would cause to think there is more data to process than there is; some data would be processed a second time.
I am reading an Image URL sent from a Java client to a C++ server from Sockets. The server stops reading through recv() when it detects there is a null character in the char buffer[] as I do below in the following code:
void * SocketServer::clientController(void *obj)
{
// Retrieve client connection information
dataSocket *data = (dataSocket*) obj;
// Receive data from a client step by step and append data in String message
string message;
int bytes = 0;
do
{
char buffer[12] = {0};
bytes = recv(data->descriptor, buffer, 12, 0);
if (bytes > 0) // Build message
{
message.append(buffer, bytes);
cout << "Message: " << message << endl;
}
else // Error when receiving it
cout << "Error receiving image URL" << endl;
// Check if we are finished reading the image link
unsigned int i = 0;
bool finished = false;
while (i < sizeof(buffer) / sizeof(buffer[0]) && !finished)
{
finished = buffer[i] == '\0';
i++;
}
if (finished)
break;
}
while (bytes > 0);
cout << message << endl;
close(data->descriptor);
pthread_exit(NULL);
}
Is there a better and more elegant way to make this?
I read about sending first the size of the URL, but I do not know exactly how to stop recv() with it. I guess it is done by counting the bytes received until the size of the URL is reached. At that moment, we should be finished reading.
Another approach could be closing the Java socket so that recv() will return -1 and the loop will be finished. However, considering my Java client waits for a response from C++ server, closing the socket and then reopen it does not seem a suitable option.
Thank you,
Héctor
Apart from that your buffer has an unusual size (one typically chooses a power of 2, so 8, 16, 32, ...) and it looks a little small for your intent, your approach seems fine to me:
I assume that your java client will send a null terminated string and then wait anyway, i. e. especially it does not send any further data. So after you received the 0 character, there won't be any data to receive any more anyway, so there is not need to bother for something explicitly that recv does implicitly (recv normally returns only the data available, even if less than the buffer could consume).
Be aware that you initialized buffer with 0, so if you check the entire buffer (instead of the range [buffer, buffer + bytes), you might detect a false positive (if you receive less than 12 characters in the first iteration)! Detection of the 0 character can be done more elegantly, though, anyway:
if(std::find(buffer, buffer + bytes, 0) < buffer + bytes)
{
// found the 0 character!
break;
}
I have a relatively simple web server I have written in C++. It works fine for serving text/html pages, but the way it is written it seems unable to send binary data and I really need to be able to send images.
I have been searching and searching but can't find an answer specific to this question which is written in real C++ (fstream as opposed to using file pointers etc.) and whilst this kind of thing is necessarily low level and may well require handling bytes in a C style array I would like the the code to be as C++ as possible.
I have tried a few methods, this is what I currently have:
int sendFile(const Server* serv, const ssocks::Response& response, int fd)
{
// some other stuff to do with headers etc. ........ then:
// open file
std::ifstream fileHandle;
fileHandle.open(serv->mBase + WWW_D + resource.c_str(), std::ios::binary);
if(!fileHandle.is_open())
{
// error handling code
return -1;
}
// send file
ssize_t buffer_size = 2048;
char buffer[buffer_size];
while(!fileHandle.eof())
{
fileHandle.read(buffer, buffer_size);
status = serv->mSock.doSend(buffer, fd);
if (status == -1)
{
std::cerr << "Error: socket error, sending file\n";
return -1;
}
}
return 0
}
And then elsewhere:
int TcpSocket::doSend(const char* message, int fd) const
{
if (fd == 0)
{
fd = mFiledes;
}
ssize_t bytesSent = send(fd, message, strlen(message), 0);
if (bytesSent < 1)
{
return -1;
}
return 0;
}
As I say, the problem is that when the client requests an image it won't work. I get in std::cerr "Error: socket error sending file"
EDIT : I got it working using the advice in the answer I accepted. For completeness and to help those finding this post I am also posting the final working code.
For sending I decided to use a std::vector rather than a char array. Primarily because I feel it is a more C++ approach and it makes it clear that the data is not a string. This is probably not necessary but a matter of taste. I then counted the bytes read for the stream and passed that over to the send function like this:
// send file
std::vector<char> buffer(SEND_BUFFER);
while(!fileHandle.eof())
{
fileHandle.read(&buffer[0], SEND_BUFFER);
status = serv->mSock.doSend(&buffer[0], fd, fileHandle.gcount());
if (status == -1)
{
std::cerr << "Error: socket error, sending file\n";
return -1;
}
}
Then the actual send function was adapted like this:
int TcpSocket::doSend(const char* message, int fd, size_t size) const
{
if (fd == 0)
{
fd = mFiledes;
}
ssize_t bytesSent = send(fd, message, size, 0);
if (bytesSent < 1)
{
return -1;
}
return 0;
}
The first thing you should change is the while (!fileHandle.eof()) loop, because that will not work as you expect it to, in fact it will iterate once too many because the eof flag isn't set until after you try to read from beyond the end of the file. Instead do e.g. while (fileHandle.read(...)).
The second thing you should do is to check how many bytes was actually read from the file, and only send that amount of bytes.
Lastly, you read binary data, not text, so you can't use strlen on the data you read from the file.
A little explanations of the binary file problem: As you should hopefully know, C-style strings (the ones you use strlen to get the length of) are terminated by a zero character '\0' (in short, a zero byte). Random binary data can contain lots of zero bytes anywhere inside it, and it's a valid byte and doesn't have any special meaning.
When you use strlen to get the length of binary data there are two possible problems:
There's a zero byte in the middle of the data. This will cause strlen to terminate early and return the wrong length.
There's no zero byte in the data. That will cause strlen to go beyond the end of the buffer to look for the zero byte, leading to undefined behavior.
I'm currently working on a multiplayer game using sockets and I encountered some problems at the log-in.
Here's the server function - thread that deals with incoming messages from a user:
void Server::ClientThread(SOCKET Connection)
{
char *buffer = new char[256];
while (true)
{
ZeroMemory(buffer,256);
recv(Connection, buffer, 256, 0);
cout << buffer << endl;
if (strcmp(buffer, "StartLogIn"))
{
char* UserName = new char[256];
ZeroMemory(UserName, 256);
recv(Connection, UserName, 256, 0);
char* Password = new char[256];
ZeroMemory(Password, 256);
recv(Connection, Password, 256, 0);
cout << UserName << "-" << Password << " + "<< endl;
if (memcmp(UserName, "taigi100", sizeof(UserName)))
{
cout << "SMB Logged in";
}
else
cout << "Wrong UserName";
}
int error = send(Connection, "0", 1, 0);
// error = WSAGetLastError();
if (error == SOCKET_ERROR)
{
cout << "SMB D/Ced";
ExitThread(0);
}
}
}
And here is the function that sends the data from the client to the server:
if (LogInButton->isPressed())
{
send(Srv->getsConnect(), "StartLogIn", 256, 0);
const wchar_t* Usern = UserName->getText();
const wchar_t* Passn = Password->getText();
stringc aux = "";
aux += Usern;
char* User = (char*)aux.c_str();
stringc aux2 = "";
aux2 += Passn;
char* Pass = (char*)aux2.c_str();
if (strlen(User) > 0 && strlen(Pass) > 0)
{
send(Srv->getsConnect(), User, 256, 0);
send(Srv->getsConnect(), Pass, 256, 0);
}
}
I'm going to try to explain this as easy as possible. The first recv function from the while(true) in the Server-side function receives at first "StartLogIn" but does not enter the if only until the next loop of the while. Because it loops again it changes to "taigi100" ( a username I use ) and then it enters the if even tho it shouldn't.
A way to fix this would be to make a send-recv system in order to not send anything else until it got some feedback.
I want to know if there are any other fast ways of solving this problem and why such weird behaviour happens.
Well it's full of bugs.
Your overuse of new[]. Ok not a bug but you are not deleting any of these, and you could use either local stack buffer space or vector< char >
You need to always check the result of any call to recv as you are not guaranteed to receive the number of bytes you are expecting. The number you specify is the size of the buffer, not the number of bytes you are expecting to get.
strcmp returns 0 if the strings match, non-zero if they do not (actually 1 or -1 depending whether they compare less or greater). But it appears you are using non-zero to mean equal.
Not sure what stringc is. Some kind of conversion from wide string to string? In any case, I think send is const-correct so there is no need to cast the constness away.
3rd parameter of send is the number of bytes you are sending, not the capacity of your buffer. The user name and password are probably not 256 bytes. You need to send them as a "packet" though so the receiver knows what they are getting and will know when they have received a full packet. e.g. send a string like "User=vandamon\0". (And you need to check its return value too)
Because send() and recv() calls may not match up, two very good habits to get into are (1) preceed all variable length data by a fixed size length, and (2) only send the bare minimum needed.
So your initial send() call would be written as follows:
char const * const StartLogin = "StartLogIn";
short const StartLoginLength = static_cast<short>(strlen(StartLogin));
send(Srv->getsConnect(), reinterpret_cast<char *>(&StartLoginLength), sizeof(short), 0);
send(Srv->getsConnect(), StartLogin, StartLoginLength, 0);
The corresponding receive code would then have to read two bytes and guarantee that it got them by checking the return value from recv() and retrying if not enough was received. Then it would loop a second time reading exactly that many bytes into a buffer.
int guaranteedRecv(SOCKET s, char *buffer, int expected)
{
int totalReceived = 0;
int received;
while (totalReceived < expected)
{
received = recv(s, &buffer[totalReceived], expected - totalReceived, 0);
if (received <= 0)
{
// Handle errors
return -1;
}
totalReceived += received;
}
return totalReceived;
}
Note that this assumes a blocking socket. Non-blocking will return zero if no data is available and errno / WSAGetLastError() will say *WOULDBLOCK. If you want to go this route you'll have to handle this case specifically and find some way to block till data is available. Either that or busy-wait waiting for data, by repeatedly calling recv(). UGH.
Anyway, you call this first with the address of a short reinterpret_cast<char *> and expected == sizeof(short). Then you new[] enough space, and call a second time to get the payload. Beware of the lack of trailing NUL characters, unless you explicitly send them, which my code doesn't.
My goal is create an app client server, written in C++.
When the server read an input from the client, should process the string and give an output.
Basically, I have a simply echo server that send the same message.
But if the user types a special string (like "quit"), the program have to do something else.
My problem is that this one dont happend, because the comparison between strings is not working... I dunno why!
Here a simple code:
while(1) {
int num = recv(client,buffer,BUFSIZE,0);
if (num < 1) break;
send(client, ">> ", 3, 0);
send(client, buffer, num, 0);
char hello[6] ="hello";
if(strcmp(hello,buffer)==0) {
send(client, "hello dude! ", 12, 0);
}
buffer[num] = '\0';
if (buffer[num-1] == '\n')
buffer[num-1] = '\0';
std::cout << buffer;
strcpy(buffer, "");
}
Why the comparison is not working?
I have tried many solutions...but all failed :(
Your data in buf may not be NULL-terminated, because buf contains random data if not initialized. You only know the content of the first num bytes. Therefore you also have to check how much data you've received before comparing the strings:
const char hello[6] ="hello";
size_t hello_sz = sizeof hello - 1;
if(num == hello_sz && memcmp(hello, buffer, hello_sz) == 0) { ...
As a side note, this protocol will be fragile unless you delimit your messages, so in the event of fragmented reads (receive "hel" on first read, "lo" on the second) you can tell where one message starts and another one ends.
strcmp requires null terminated strings. The buffer you read to might have non-null characters after the received message.
Either right before the read do:
ZeroMemory(buffer, BUFSIZE); //or your compiler defined equivalent
Or right after the read
buffer[num] = '\0';
This will ensure that there is a terminating null at the end of the received message and the comparison should work.
A string is defined to be an array of chars upto and including the terminating \0 byte. Initially your buffer contains arbitrary bytes, and is not even guaranteed to contain a string. You have to set buffer[num] = '\0' to make it a string.
That of course means that recv should not read sizeof buffer bytes but one byte less.