A server in java, sends from a Server socket a byte array, this byte array contains text (utf-8) with the next format:
first 4 bytes: an int with the number of bytes of the text
next n
bytes: each byte represents a char.
So i am using "WiFiClient" from the "ESP8266WiFi.h" library (it should be the same as "WiFi.h" library), WifiClient has a method to receive a byte using the read() method, the problem is that i am unable to read correctly the int (first four bytes) or transform the bytes into int value. So i will be very gratefull if you help me with that:
Java (Server) simplified code:
String respuestaServer="RESPUESTAS DEL SERVER";
DataOutputStream out=new DataOutputStream(sock.getOutputStream());
out.writeInt(respuestaServer.getBytes(StandardCharsets.UTF_8).length);
out.write(respuestaServer.getBytes(StandardCharsets.UTF_8));
out.flush();
Arduino (Client) code to receive and interpret the byte array (the objective of this code is transform the bytes into a String):
String recibirInfo() {
//TRYING TO READ FIRST FOUR BYTES
byte bytesSizeMsj[4];
for (int i = 0; i < sizeof(bytesSizeMsj); i++) {
bytesSizeMsj[i] = client.read();
Serial.print("BYTE: "+bytesSizeMsj[i]);
}
//TRYING TO TRANSFORM THE FOUR BYTES INTO AN INT
int sizeMsj = int((unsigned char)(bytesSizeMsj[0]) |
(unsigned char)(bytesSizeMsj[1]) |
(unsigned char)(bytesSizeMsj[2])|
(unsigned char)(bytesSizeMsj[3]));
Serial.println(sizeMsj);
char charArray[sizeMsj];
//TRYING TO READ THE REST OF THE MESSAGE
for (int i = 0; i < sizeof(charArray); i++) {
charArray[i] = client.read();
}
//TRYING TO TRANSFORM THE BYTE ARRAY INTO A STRING
String msj=charArray;
return msj;
}
I fix it, i had problems with the way I read from the socket, the way I transform the 4 bytes into an Int datatype and with the way I transoform the bytes into a String, I figure out why i was not receiving all the bytes when trying to read from the socket, the reason was that when I call the method "client.read()" the server was not fast enought to send the information so it did not read anything, so I did a loop and with help of the method "client.available()" I checked that there was a byte ready to be read, so the loop ends when the first 4 bytes were read. Then I transform the 4 bytes readed into an Int, read the rest of the bytes in a similar way than before, and I tranformed the bytes into a String.
Here is my functional code:
String recibirInfo(bool* error) {
String msj = "";
byte bytesSizeMsj[4];
for (int i = 0; i < sizeof(bytesSizeMsj); i++) {
if (client.connected()) {
if (client.available()) {
bytesSizeMsj[i] = client.read();
} else {
delay(10);
i--;
}
} else {
*error = true;
return "";
}
}
//TRANSFORMAR LOS 4 BYTES A INT
int sizeMsj = 0;
sizeMsj = ((int)bytesSizeMsj[3]) | sizeMsj;
sizeMsj = ((int)bytesSizeMsj[2]) << 8 | sizeMsj;
sizeMsj = ((int)bytesSizeMsj[1]) << 16 | sizeMsj;
sizeMsj = ((int)bytesSizeMsj[0]) << 24 | sizeMsj;
char charArray[sizeMsj];
//LEER EL TEXTO
for (int i = 0; i < sizeof(charArray); i++) {
if (client.connected()) {
if (client.available()) {
charArray[i] = client.read();
} else {
delay(250);
i--;
}
} else {
*error = true;
return "";
}
}
//TRANSFORMAR BYTES A STRING
msj = charArrayToString(charArray, sizeMsj);
Serial.print("RECIBIDO: ");
Serial.println(msj);
return msj;
}
String charArrayToString(char arrChar[], int tam) {
String s = "";
for (int i = 0; i < tam; i++) {
s = s + arrChar[i];
}
return s;
}
It's kinda of hard to trouble shoot like this do you actually receive data ? Can you show the output you get ? Maybe you aren't even connected.
Also what i've done in the past is convert every int or "letter" into a char. Since i usually send a message of known lenght with a starting char i read every byte of my message and convert it to a char and then add it to my received message string. I dont know if this helps... Basically i do the conversion on the arrival of the byte.
Related
I've got a C++ server that communicates with multiple clients. It uses a vector to store the handles to the sockets for those clients (playerSockets in the code below). At the end of the "game" I want the server to loop through that vector and write the same string to each client. However, sometimes the data that the client reads (and then displays) is "corrupted" as you can see in the screenshot, but this doesn't happen for the first client, only the second. I can't figure out why this is happening! I use this same technique (looping and writing) earlier in the program and it always works fine in that instance.
Here is what it is supposed to be shown:
Here and here's what I get:
Here is the server code that writes:
std::string announcement = "";
if (playerWon) {
...
}
} else {
announcement = "?No one won the game!\nGAME BOARD: " + cn.getGameBoard();
for (int player : gameData->playerSockets) {
write(player, announcement.c_str(), announcement.size() + 1);
}
}
And here's the client code that reads. Keep in mind that more than one client is running and connected to the server, and this issue only happens with a client OTHER THAN the first client in the server's loop:
static bool readMyTurn(int clientSd) {
...
char buf[BUFSIZE];
read(clientSd, buf, BUFSIZE);
string myTurn(buf);
cout << "MYMYMYMY: " << myTurn << endl;
myTurn.erase(0, 1);
cout << myTurn << endl;
...
}
UPDATE
Here is my current code to read until encountering the null-terminator character.
string readOneStringFromServer(int clientSd, string &leftovers) {
ssize_t nullTerminatorPosition = 0;
std::string stringToReturn = "";
do {
char buf[BUFSIZE];
ssize_t bytesRead = read(clientSd, buf, BUFSIZE);
nullTerminatorPosition = findPositionOfNullTerminator(buf, bytesRead);
// found a null terminator
if (nullTerminatorPosition != -1) {
// create a buffer to hold all of the chars from buf1 up to and including the null terminator
char upToNullTerminator[nullTerminatorPosition + 1];
// get those chars from buf1 and put them into buf2 (including the null terminator)
for (int i = 0; i < nullTerminatorPosition + 1; ++i) {
upToNullTerminator[i] = buf[i];
}
// use buf2 to create a string
stringToReturn += upToNullTerminator;
// check if there are leftover bytes after the null terminator
int leftoverBytes = bytesRead - nullTerminatorPosition - 1;
if (leftoverBytes != 0) {
// if there are, create a char array of that size
char leftoverChars[leftoverBytes];
// loop through buf1 and add the leftover chars to buf3
for (int i = nullTerminatorPosition + 1; i < bytesRead; ++i) {
leftoverChars[i - (nullTerminatorPosition + 1)] = buf[i];
}
// make a string out of those leftover chars
leftovers = leftoverChars;
} else {
// if there are no leftover bytes, then we want to "erase" what is currently held in leftovers so that
// it doesn't get passed to the next function call
leftovers = "";
}
// didn't find one
} else {
stringToReturn += buf;
}
} while (nullTerminatorPosition == -1);
return stringToReturn;
}
I assume that for messages that are of only 1 byte (a char), I will use read() and write() directly.
For those messages having size > 1 bytes, I use two subfunctions to read and write them over sockets.
For example, I have the server construct a string called strcities (list of city) and print it out --> nothing strange. Then send the number of bytes of this string to the client, and then the actual string.
The client will first read the number of bytes, then the actual city list.
For some reason my code sometimes work and sometimes doesn't. If it works, it also prints out some extra characters that I have no idea where they come from. If it doesn't, it hangs and forever waits in the client, while the server goes back to the top of the loop and wait for next command from the client. Could you please take a look at my codes below and let me know where I did wrong?
Attempt_read
string attempt_read(int rbytes) { // rbytes = number of bytes of message to be read
int count1, bytes_read;
char buffer[rbytes+1];
bool notdone = true;
count1 = read(sd, buffer, rbytes);
while (notdone) {
if (count1 == -1){
perror("Error on write call");
exit(1);
}
else if (count1 < rbytes) {
rbytes = rbytes - count1; // update remaining bytes to be read
count1 = read(sd, buffer, rbytes);
}
else {notdone = false;}
} // end while
string returnme;
returnme = string(buffer);
return returnme;
}
Attempt_write
void attempt_write(string input1, int wbytes) { // wbytes = number of bytes of message
int count1;
bool notdone = true;
count1 = write(sd, input1.c_str(), wbytes);
while (notdone) {
if (count1 == -1){
perror("Error on write call");
exit(1);
}
else if (count1 < wbytes) {
wbytes = wbytes - count1;
count1 = write(sd, input1.c_str(), wbytes);
}
else {notdone = false;}
} // end while
return;
}
1) string class has a method size() that will return the length of the string, so you do not actually need a second attempt_write parameter.
2) You can transfer length of message before message or you can transfer a terminating 0 after, if you only will sent an ASCII strings. Because your connection could terminate at any time, it is better to send exact length before sending the string, so your client could know, what to expect.
3) What compilator do you use, that would allow char buffer[rbytes+1]; ? A standard c++ would require char buffer = new char[rbytes+1]; and corresponding delete to avoid a memory leaks.
4) In your code, the second read function call use same buffer with no adjustment to length, so you, practically, overwrite the already received data and the function will only work, if all data will be received in first function call. Same goes for write function
I would suggest something like this:
void data_read(unsigned char * buffer, int size) {
int readed, total = 0;
do {
readed = read(sd, buffer + total, size - total);
if (-1 == writted) {
perror("Error on read call");
exit(1);
}
total += readed;
} while (total < size);
}
string attempt_read() {
int size = 0;
data_read((unsigned char *) &size, sizeof(int));
string output(size, (char) 0x0);
data_read((unsigned char *) output.c_str(), size);
return output;
}
void data_write(unsigned char * buffer, int size) {
int writted, total = 0;
do {
writted = write(sd, buffer + total, size - total);
if (-1 == writted) {
perror("Error on write call");
exit(1);
}
total += writted;
} while (total < size);
}
void attempt_write(string input) {
int size = input.size();
data_write((unsigned char *) &size, sizeof(int));
data_write((unsigned char *) input.c_str(), size);
}
I am working on firmware of an ATMEL sensor board (accelerometer and gyro)and trying to read the data in a platform in Ubuntu.
Currently the firmware is like this:
Ubuntu sends a character "D" and the firmware in response sends back 20 bytes of data that ends in "\n" then ubuntu uses serialport_read_until(fd, buff, '\n') and assumes that buff[0] is byte zero and so on.The frequency of acquisition is 200hz.
BUT using this method sometimes I receive corrupted values and it is not working well. Also there are many "Unable to write on serial port" error in ubuntu.
I have found an example code from ATMEL for the firmware and there the data is sent in different packages and continuously (without waiting for the computer to ask for it) the structure is like this:
void adv_data_send_3(uint8_t stream_num, uint32_t timestamp,
int32_t value0, int32_t value1, int32_t value2)
{
/* Define packet format with 3 data fields */
struct {
adv_data_start_t start; /* Starting fields of packet */
adv_data_field_t field [3]; /* 3 data fields */
adv_data_end_t end; /* Ending fields of packet */
} packet;
/* Construct packet */
packet.start.header1 = ADV_PKT_HEADER_1;
packet.start.header2 = ADV_PKT_HEADER_2;
packet.start.length = cpu_to_le16(sizeof(packet));
packet.start.type = ADV_PKT_DATA;
packet.start.stream_num = stream_num;
packet.start.time_stamp = cpu_to_le32(timestamp);
packet.field[0].value = cpu_to_le32(value0);
packet.field[1].value = cpu_to_le32(value1);
packet.field[2].value = cpu_to_le32(value2);
packet.end.crc = 0x00; /* Not used */
packet.end.mark = ADV_PKT_END;
/* Write packet */
adv_write_buf((uint8_t *)&packet, sizeof(packet));
}
but I don't know how I can continuously read the data that is sent in a structure like above.
Sorry if it is a trivial question. I am not a programmer but I need to solve this and I could not find a solution (that I can understand!) after searching for a couple of days.
The reading function I use in linux:
int serialport_read_until(int fd, unsigned char* buf, char until){
char b[1];
int i=0;
do {
int n = read(fd, b, 1); // read a char at a time
if( n==-1) return -1; // couldn't read
if( n==0 ) {
usleep( 1 * 1000 ); // wait 1 msec try again
continue;
}
buf[i] = b[0]; i++;
} while( b[0] != until );
buf[i] = 0; // null terminate the string
return 0;}
The new Reading Func:
// Read the header part
adv_data_start_t start;
serial_read_buf(fd, reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
serial_read_buf(fd, data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_field_t* fields = reinterpret_cast<adv_data_field_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i].value << '\n';
The data packets that are sent from the firmware:
typedef struct {
uint8_t header1; // header bytes - always 0xFF5A
uint8_t header2; // header bytes - always 0xFF5A
uint16_t length; // packet length (bytes)
uint32_t time_stamp; // time stamp (tick count)
} adv_data_start_t;
typedef struct {
int32_t value; // data field value (3 VALUES)
} adv_data_field_t;
typedef struct {
uint8_t crc; // 8-bit checksum
uint8_t mark; // 1-byte end-of-packet marker
uint16_t mark2; // 2-byte end-of-packet marker (Added to avoid data structure alignment problem)
} adv_data_end_t;
Well you have the length of the packet in the packet "header", so read the header fields (the start structure) in one read, and in a second read you read the data and the end.
If the start and end parts are the same for all packets (which I guess they are), you can easily figure out the amount of data fields after the second read.
Something like this:
// Read the header part
adv_data_start_t start;
adv_read_buf(reinterpret_cast<uint8_t*>(&start), sizeof(start));
// Create a buffer for the data and the end marker
std::vector<uint8_t> data_and_end(start.length - sizeof(start));
// Read the data and end marker
adv_read_buf(data_and_end.data(), data_and_end.size());
// Iterate over the data
size_t num_data_fields = (data_and_end.size() - sizeof(adv_data_end_t)) / sizeof(adv_data_field_t);
adv_data_end_t* fields = reinterpret_cast<adv_data_end_t*>(data_and_end.data());
for (size_t i = 0; i < num_data_fields; i++)
std::cout << "Field #" << (i + 1) << " = " << fields[i] << '\n';
Possible read_buf implementation:
// Read `bufsize` bytes into `buffer` from a file descriptor
// Will block until `bufsize` bytes has been read
// Returns -1 on error, or `bufsize` on success
int serial_read_buf(int fd, uint8_t* buffer, const size_t bufsize)
{
uint8_t* current = buffer;
size_t remaining = bufsize
while (remaining > 0)
{
ssize_t ret = read(fd, current, remaining);
if (ret == -1)
return -1; // Error
else if (ret == 0)
{
// Note: For some descriptors, this means end-of-file or
// connection closed.
usleep(1000);
}
else
{
current += ret; // Advance read-point in buffer
remaining -= ret; // Less data remaining to read
}
}
return bufsize;
}
i m trying to use readfile function to read the serial port in c++. i manage to open and read the serial port in c++. the problem i facing now is the decoding of data after i read from the serial port. The below are my codes. When i run my code, my loop of decoding could not detect ((*&szChar == '$')), and it exit the loop by printing error. May i know how could i decode the gps data that i read from my serial port? thanks
char szChar[100];
int nRet;
DWORD dwBytesRead = 10;
char ReadBuffer[BUFFERSIZE] = {0};
nRet = ReadFile(hCom,&szChar,BUFFERSIZE-1,&dwBytesRead,NULL);
if((*&szChar == '$'))
{
printf("%s\n", &szChar);
}
else
{
printf("error\n");
I have to say, I find your code quite confused and confusing. Just for example, you're creating szChar as an array of 100 char, and ReadBuffer as an array of BUFFERSIZE chars. When you call ReadFile, however, you're passing the base address of szChar with the size given as BUFFERSIZE. Unless, by some coincidence, BUFFERSIZE happens to equal 100, that looks a lot like a potential buffer overrun.
Then we get to *&szChar. This doesn't really make much sense either. From the looks of things, you probably want szChar[0] -- but even that's not really a good idea, because you might not receive the data in exactly line-sized pieces. As such, you probably want to scan through the data to find the '$'.
int Ret;
DWORD BytesRead;
char ReadBuffer[BUFFERSIZE] = {0};
Ret = ReadFile(hCom,ReadBuffer,sizeof(ReadBuffer)-1,&BytesRead,NULL);
ReadBuffer[BytesRead] = '\0';
if (ReadBuffer[0] == '$')
printf(%s\n", ReadBuffer);
else
printf("error\n");
#Jerry: Thanks.. so i edited my code below to decode my data, is it correct way to put my ReadBuffer into another array for checking?
char lastCommaPosition;
char latitudeString[11];
char stringRead[MAXSIZE];
char tempString[MAXSIZE];
char *pChar;
char dummyChar;
float latitude;
int latDegrees;
float latMinutes;
int numLinesRead;
int Ret,i,j,k;
if (ReadBuffer[0] == '$')
{
i = 0;
numLinesRead++;
stringRead[i] = ReadBuffer;
}
stringRead[i+1] = '\0';
j = 0;
pChar = stringRead;
while(*(pChar+j) != ',')
{
tempString[j] = *(pChar+j);
j++;
}
tempString[j] = '\0';
if(tempString[3] == 'G' && tempString[4] == 'G' && tempString[5] == 'A')
{
pChar = stringRead;
j = lastCommaPosition + 1;
k = 0;
while(*(pChar+j) != ',')
{
latitudeString[k] = *(pChar+j);
j++;
k++;
}
lastCommaPosition = j;
latitudeString[k] = '\0';
sscanf(latitudeString, "%f", &latitude);
latDegrees = (int)(latitude/100);
latMinutes = (float)(latitude - latDegrees*100);
printf("\t%02d DEG\t%2.4f MIN", latDegrees, latMinutes);
I have two simple programs set up that share data through a unix domain socket. One program reads data out of a Queue and sends it to the other application. Before it is sent each piece of data is front-appended by four bytes with the length, if it is less then four bytes the left over bytes are the '^' symbol.
The client application then reads the first four bytes, sets a buffer to the appropriate size and then reads the rest. The problem that I'm having is that the first time through the message will be sent perfectly. Every other time after that there is extra data being sent so a message like "what a nice day out" would come out like "what a nice day out??X??". So I feel like a buffer is not being cleared correctly but I can't seem to find it.
Client code:
listen(sock, 5);
for (;;)
{
msgsock = accept(sock, 0, 0);
if (msgsock == -1)
perror("accept");
else do
{
char buf[4];
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 4)) < 0)
perror("reading stream message");
printf("--!%s\n", buf);
string temp = buf;
int pos = temp.find("^");
if(pos != string::npos)
{
temp = temp.substr(0, pos);
}
int sizeOfString = atoi(temp.c_str());
cout << "TEMP STRING: " << temp << endl;
cout << "LENGTH " << sizeOfString << endl;
char feedWord[sizeOfString];
bzero(feedWord, sizeof(feedWord));
if ((rval = read(msgsock, feedWord, sizeOfString)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", feedWord);
bzero(feedWord, sizeof(feedWord));
sizeOfString = 0;
temp.clear();
}
while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
Server Code
pthread_mutex_lock(&mylock);
string s;
s.clear();
s = dataQueue.front();
dataQueue.pop();
pthread_mutex_unlock(&mylock);
int sizeOfString = strlen(s.c_str());
char sizeofStringBuffer[10];
sprintf(sizeofStringBuffer, "%i", sizeOfString);
string actualString = sizeofStringBuffer;
int tempSize = strlen(sizeofStringBuffer);
int remainder = 4 - tempSize;
int x;
for(x =0; x < remainder; x++)
{
actualString = actualString + "^";
}
cout << "LENGTH OF ACTUAL STRING: " << sizeOfString << endl;
actualString = actualString + s;
cout << "************************" << actualString << endl;
int length = strlen(actualString.c_str());
char finalString[length];
bzero(finalString, sizeof(finalString));
strcpy(finalString, actualString.c_str());
if (write(sock, finalString, length) < 0)
perror("writing on stream socket");
Rather than padding your packet length with '^', you'd be far better off just doing:
snprintf(sizeofStringBuffer, 5, "%04d", sizeOfString);
so that the value is 0 padded - then you don't need to parse out the '^' characters in the receiver code.
Please also edit out your debug code - there's only one write() in the current code, and it doesn't match your description of the protocol.
Ideally - split your sending routine into a function of its own. You can also take advantage of writev() to handle coalescing the string holding the "length" field with the buffer holding the actual data and then sending them as a single atomic write().
Untested code follows:
int write_message(int s, std::string msg)
{
struct iovec iov[2];
char hdr[5];
char *cmsg = msg.c_str();
int len = msg.length();
snprintf(hdr, 5, "%04d", len); // nb: assumes len <= 9999;
iov[0].iov_base = hdr;
iov[0].iov_len = 4;
iov[1].iov_base = cmsg;
iov[1].iov_len = len;
return writev(s, iov, 2);
}
You have to check return values of both write and read not only for -1 but for short (less then requested) writes/reads. You also seem to just continue after printing an error with perror - do an exit(2) or something there.
Two things:
First - on the Server side you are writing off the end of your array.
char finalString[length];
bzero(finalString, sizeof(finalString));
strcpy(finalString, actualString.c_str());
The strcpy() will copy length+1 characters into finalString (character pull the null terminator).
Second (and most likely to be the problem) - on the client side you are not null terminating the string you read in, therefore the printf() will print your string, and then whatever is on the stack up to the point it hits a null.
Increase both buffers by one, and you should be in better shape.