How to handle buffering serial data - c

I am trying to figure out a nice solution to reading serial data, and what to do when a read() is done but it contains an incomplete message.
The expected messages between devices have a defined start and end byte so its easy to see when a message starts and ends.
I can open a serial port fine and read from the serial port. But I am encountering the computer is reading faster than data coming through and I get an incomplete message.
For this example, lets say the message expected is
0x10 0xFF 0xFF 0xFF 0xFF 0x11
With 0x10 the start, 0x11 the end, and 0xFF is the data bytes
I am new to C so I may be missing something obvious,
My current solution
int main() {
/* Ommited serial port opening and checking*/
char read_buffer[80];
char message_buffer[80];
int message_buffer_index = 0;
int start_index = -1;
int end_index = -1;
int read_bytes;
read_bytes = read(serial_port, read_buffer, sizeof(read_buffer) - 1);
/* Now lets say read_bytes returns 3 and read buffer is {0x10, 0xFF, 0xFF} */
/* What should I do with the read_buffer? Currently appending to message buffer*/
memcpy(&message_buffer[message_buffer_index], &read_buffer[0], read_bytes);
/* Now check the message buffer for a full message */
for (int i = 0; i < 80; i++) {
if (message_buffer[i] = 0x10) {
start_index = i;
continue;
}
if (message_buffer[i] = 0x11) {
end_index = i;
}
if (start_index != -1 && end_index != -1) {
/* Found a message, do something with it, not super important here */
process_message();
/* Now how to erase the full message from the
buffer and push any non processed data to the
front? */
remove_message();
}
}
}
int process_message();
int remove_message();

To minimize the overhead of making many read() syscalls of small byte counts (e.g. the misguided solution of reading a byte at a time), use an intermediate buffer in your code.
The read() of the serial terminal should be in blocking mode to avoid a return code of zero bytes.
#define BLEN 1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;
/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}
For proper termios initialization code for the serial terminal, see this answer. You should increase the VMIN parameter to something closer to the BLEN value or at least the length of longest expected message, and a VTIME of 1.
Now you can conveniently access the received data a byte at a time with minimal performance penalty.
#define MLEN 1024 /* choose appropriate value for message protocol */
int main()
{
unsigned char mesg[MLEN];
...
while (1) {
while (getbyte() != 0x10)
/* discard data until start found */ ;
length = 0;
while ((mesg[length] = getbyte()) != 0x11) {
/* accumulate data until end found */
length++;
}
/* process the message */
...
} /* loop for next message */
...
}
Note that your detection for a message frame is not robust.
If the data is binary and therefore can use the same values as these start and end bytes, then this parsing of the received data is prone to misaligned message frames.
See this answer for a description of a proper alogrithm.

You need circular buffer. Place data in the buffer and the process takes them when for example there is enough data or in any convenient moment.
Wikipedia has excellent article about it https://en.wikipedia.org/wiki/Circular_buffer

Better use stdio for reading, something like this:
FILE *fp = fdopen(serial_port, "r");
while (blabla) {
while (fgetc(fp) != 0x10)
; // wait until start
while ((c = fgetc(fp)) != 0x11)
message_buffer[message_buffer_index++] = c;
// here you have a complete message
}
Insert checks for EOF and errors if needed

Related

How to send raw binary data over serial in C without non-native libraries in linux

I'm currently trying to send raw binary data in the format of decimal to an external device over serial. I currently have the data in a buffer array but would like it in a structure like this:
struct packetData{
uint8_t sync1;
uint8_t sync2;
uint16_t messageId;
uint16_t dataWordCount;
uint16_t flags;
uint16_t checksum;
};
I'm also using 9600 baud, and have all the termios settings set using cfmakeraw and I'm currently writing using:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int flags = O_RDWR | O_NOCTTY | O_NDELAY;
fd = open(device, flags);
uint16_t buf_tx[BUFFER_SIZE] = {255,129,191,0,2057,0};
if(fd == -1){
printf("\n Failed to open port! ");
return -1;
}
tcgetattr(fd, &tty); //Get the current attributes of the Serial port
cfmakeraw(&tty);
cfsetispeed(&tty, B9600); //Set read speed as 9600 baud
cfsetospeed(&tty, B9600); //Set write speed as 9600 baud
if((tcsetattr(fd, TCSANOW, &tty)) != 0){
printf("Error! Can't set attributes.\n");
return -1;
}
else{
printf("Connection successful! \n");
}
while(x < 1000){
memset(buf_tx, 0, sizeof(buf_tx));
tcflush(fd, TCOFLUSH);
if(y < 5){
if(write(fd, buf_tx, 5) == -1){
printf("\n");
printf("Error>>: %s\n",strerror(errno));
y++;
}
}
tcflush(fd, TCIOFLUSH);
usleep(1000);
x++;
}
This code isnt the full code, just the setup/write parts so no need to worry about its syntax. if possible it would be nice not to have that buffer array and just use the struct directly, but I'll take what I can get.
It seems you have the serial port opening more or less in hand. I prefer to set the termios member components explicitly myself, but cfmakeraw() is perfectly fine too.
What you should consider, is having a separate function to send one or more of those structures at a time. For example,
int write_all(const int fd, const void *buf, const size_t len)
{
const char *data = buf;
size_t written = 0;
ssize_t n;
while (written < len) {
n = write(fd, data + written, len - written);
if (n > 0) {
written += n;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Error; n == -1, so errno is already set. */
return -1;
}
}
/* Success. */
return 0;
}
The function will return 0 if all data was successfully written, and -1 with errno set if an error occurs.
To send a struct packetData pkt; just use write_all(fd, &pkt, sizeof pkt).
To send a full array struct packetData pkts[5]; use write_all(fd, pkts, sizeof pkts).
To send n packets starting at pkts[i], use write_all(fd, pkts + i, n * sizeof pkts[0]).
However, you do not want to use tcflush(). It does not do what you think it does; it actually just discards data.
Instead, to ensure that the data you have written has been transmitted, you need to use tcdrain(fd).
I recommend against adding tcdrain(fd) at the end of write_all() function, because it blocks, pauses the program, until the data has been transmitted. This means that you should only use tcdrain() before you do something that requires the other end has received the transmission; for example before trying to read the response.
However, if this is a query-response interface, and you do intend to also read from the serial device, you should set tty.c_cc[VMIN] and tty.c_cc[VTIME] to reflect how you intend to use the interface. I prefer asynchronous full-duplex operation, but that requires select()/poll() handling. For half-duplex, with these exact structures only, you can use tty.c_cc[VMIN] = sizeof (struct packetData) with say tty.c_cc[VTIME] = 30, which causes read() to try and wait until a full structure is available, but at most 30 deciseconds (3.0 seconds). Something like tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 1; is more common; that causes read() to return a short count (even 0!) if there is no additional data received within a decisecond (0.1 seconds). Then, the receive function could be along the following lines:
int read_all(const int fd, void *buf, const size_t len)
{
char *const ptr = buf;
size_t have = 0;
ssize_t n;
/* This function is to be used with half-duplex query-response protocol,
so make sure we have transmitted everything before trying to
receive a response. Also assumes c_cc[VTIME] is properly set for
both the first byte of the response, and interbyte response interval
in deciseconds. */
tcdrain(fd);
while (have < len) {
n = read(fd, ptr + have, len - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
/* Timeout or disconnect */
errno = ETIMEDOUT;
return -1;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Read error; errno set by read(). */
return -1;
}
}
/* Success; no errors. */
return 0;
}
If this returns -1 with errno == ETIMEDOUT, the other side took too long to answer. There may be remainder of the late response in the buffer, which you can discard with tcflush(TCIFLUSH) (or with tcflush(TCIOFLUSH), which also discards any written data not yet transmitted). Synchronization in this case is a bit difficult, because the above read_all() function doesn't return how many bytes it received (and therefore how many bytes to discard of a partial structure).
Sometimes the interface used always returns the number of bytes, but also sets errno (to 0 if no error occurred, and a nonzero error constant otherwise). That would be better for a query-response interface read and write functions, but many programmers find this use case "odd", even though it is perfectly okay by POSIX.1 standard (which is the relevant standard here).

Reading the maximum size of an expected packet from a C socket

I have a client/server program. The client sends a message to the server and the server processes this message and gets back to the client.
The first four bytes of the client message is the size of the whole message. I also know the maximum size a message can be. However, messages are of variable sizes.
I am using the read function in a while loop to read the client messages
while((n = read(socket, buffer, MAX_SIZE)) != 0){
process_message();
write(socket, ...);
}
I was wondering if there is any harm in reading more bytes than what the client is sending? I can first read the size of the message and then read the exact number of bytes but I was wondering if this is necessary.
As mentioned in some other comments, reading from a socket can return any number of bytes, up to the maximum requested.
A better loop, although still not without problems, would be something along these lines:
/* 4 byte message header that contains the length */
#define MSG_HEADER_SIZE 4
#define MAX_MESSAGE_LENGTH 128
struct message {
uint32_t length;
char body[MAX_MESSAGE_LENGTH];
};
#define BUFFER_SIZE 1024
char buffer[BUFFER_SIZE];
uint32_t buf_used = 0;
/* main loop */
while (1) {
n = recv(socket, buffer + buf_used, sizeof(buffer) - buf_used, 0);
if (n == -1) {
/* handle error */
exit(1);
}
if (n == 0) {
/* connection closed, do something. */
exit(1);
}
buf_used += n;
/* check for partial/completed message(s) */
while (buf_used >= MSG_HEADER_SIZE) {
struct message *cur_msg = (struct message *) buffer;
uint32_t total_msg_length;
total_msg_length = cur_msg->length + MSG_HEADER_SIZE;
/* is this message completed yet? */
if (buf_used >= total_msg_length) {
process_message(cur_msg);
/* remove message since it has been processed */
buf_used -= total_msg_length;
/* this could potentially be optimized */
memmove(buffer, buffer + total_msg_length, buf_used);
} else {
/* have incomplete message */
break;
}
}
}
For an introduction to socket programming, I would recommend checking out Beej's Guide to Network Programming.

C: sockets: can't read the whole server response

I'm programming in C an IRC chat client. everything it's working well except I can't read the whole answer sent by the server. here's the code:
char buffer[2048];
write_on_screen(current_page(), "LOG COMMAND", command);
write(sockfd, command, strlen(command)); //write to socket
bzero(buffer, sizeof(buffer));
read(sockfd, buffer, sizeof(buffer));
write_on_screen(current_page(), "RESPONSE", buffer);
return buffer;
most of the time buffer will contain just a piece of the response (which is shorter than 2048 bytes) and other times it contains nothing. in both cases if I do another read() after the first one, it returns me the rest of the answer or another small piece (and then I've to do another read() again). if I put a sleep(1) between write() and read() I get the whole answer, but I'm sure this not a good pratice.
Is there some way I can avoid this?
thank you in advance
You're making the usual mistakes. It is impossible to write correct network code without storing the result of read() or recv() into a variable. You have to:
Check it for -1, and if so look at errno to see whether was fatal, which it almost always is except for EAGAIN/EWOULDBLOCK, and if fatal close the socket and abandon the process.
Check it for zero, which means the peer disconnected. Again you must close the socket and abandon the process.
Use it as the count of bytes actually received. These functions are not obliged nor guaranteed to fill the buffer. Their contract in blocking mode is that they block until an error, end of stream, or at least one byte is transferred. If you're expecting more than one byte, you normally have to loop until you get it.
According to RFC-1459, a single line of text in IRC can contain up to 512 characters and is terminated by a CRLF (\r\n) pair. However:
You're not guaranteed to receive exactly 512 bytes each time. For example, you might receive a comparatively short message from someone else one in the channel: Hi!
Related to the above: A group of 512 bytes might represent more than one message. For example, the buffer might contain a whole line, plus part of the next line: PRIVMSG <msgtarget> <message>\r\nPRIVMS
Given that you could have zero-or-more complete lines plus zero-or-one incomplete lines in your buffer[] at any time, you could try doing something along the lines of:
char buffer[2048];
while(keep_going)
{
char **lines;
int i, num_lines;
// Receive data from the internet.
receiveData(buffer);
// Create an array of all COMPLETE lines in the buffer (split on \r\n).
lines = getCompleteLines(buffer, &num_lines);
removeCompleteLinesFromBuffer(buffer);
// Handle each COMPLETE line in the array.
for (i = 0; i < num_lines; ++i) { handle_line(lines[i]); }
freeLines(lines);
}
This would allow you to handle zero or more complete lines in one go, with any incomplete line (i.e anything after the final \r\n pair) being kept around until the next call to receiveData().
You need to loop around read() until a CRLF had been detected.
A possible way to do this would be:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt)
{
ssize_t bytes_read = 0;
ssize_t result = 0;
int read_cr = 0;
int read_crlf = 0;
while (bytes_read < s)
{
result = read(sd, p + bytes_read, 1);
if (-1 == result)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue;
}
else if (EINTR == errno)
{
if (break_on_interupt)
{
break;
}
continue;
}
else
{
perror("read() failed");
break;
}
}
else if (0 == result)
{
break; /* peer disconnected */
}
if ('\r' == p[bytes_read])
{
read_cr = 1;
}
else if (('\n' == p[bytes_read]) && read_cr)
{
read_crlf = 1;
break; /* CRLF detected */
}
else
{
read_cr = 0;
}
++bytes_read;
}
if (!read_crlf)
{
result = -1; /* Buffer full without having read a CRLF. */
errno = ENOSPC; /* ... or whatever might suite. */
}
return (0 >= result) ?result :bytes_read;
}
Call it like this:
#include <stdio.h>
ssize_t read_until_crlf(int sd, char * p, size_t s, int break_on_interupt);
int main(void)
{
int sd = -1;
/* init sd here */
{
char line[2048] = "";
ssize_t result = read_until_crlf(sd, line, sizeof line, 0);
if (-1 == result)
{
perror("read_until_newline() failed");
}
printf("read '%s'\n", line);
}
return 0;
}

Blocking read until specified number bytes arrived

I have a requirement of waiting using "read" until a buffer is full on an audio codec device. For make it easier, lets take similar example of:
fd= read(fileno(stdin), &buf, 10);
How can I return from the read when I type 10 characters in stdin? (I hope if this is success, I can wait on codec until specified bytes of data is arrived).
The above example needs an "Enter Key" from console, where as I want "read" to unblock only when desired bytes of data is arrived.
EDIT: Requirement is waiting using a single "read" till specified bytes are arrived.
How about this:
#define BUFFER_SIZE 10
char buffer[BUFFER_SIZE];
/* ... */
size_t total_read = 0;
size_t total_left = BUFFER_SIZE; /* The total size of the buffer */
char *buffer_pointer = buffer; /* buffer is where to store the data */
while (total_left > 0)
{
ssize_t current = read(STDIN_FILENO, buffer_pointer, total_left);
if (current <= 0)
{
/* Error or end of file */
if (current < 0)
perror("read"); /* Error! */
break;
}
else
{
total_read += current; /* We have read some more data */
total_left -= current; /* Less data left to read */
buffer_pointer += current; /* So we don't read over already read data */
}
}
printf("Received %ld characters\n", total_read);
for (unsigned int i = 0; i < total_read; i++)
printf("Character #%d: '%c'\n", i, buffer[i]);
Beware that this will block your program until all data is read. The amount of characters read may be less than everything, because there might be an error or the user pressed CTRL-D (end of file).
Also note that the STDIN_FILE file-descriptor is most likely connected to a tty, which means it might be buffered, so doesn't return data until newline, and that you might have to make the tty unbuffered.
Edit
To make sure the tty connected to stdin is unbuffered, use the following code:
#include <termios.h>
/* ... */
/* Somewhere before reading from stdin... */
struct termios tty_settings;
tcgetattr(STDIN_FILENO, &tty_settings);
tty_settings.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &tty_settings);
For more information about the tcsetattr function and the ICANON flag, check the manual page for tcsetattr.
do {ioctl(fd_port, FIONREAD, &bytes);} while (bytes < size);
May help, before issuing a read().

recv() until a NUL byte is received?

I'm trying to receive a single packet at a time from the server, since packets are going too fast, and each is of undefined size, calling recv() with number of bytes to read will read the first packet and maybe a part of the second packet. Since each packet is NULL terminated, I thought reading byte by byte until a NULL byte is received.
int recvLen = 0;
char TB;
char recvBuffer[1024];
while (recv(Socket, &TB, 1, 0) > 0 && TB != 0 && recvLen < 1024)
{
recvBuffer[recvLen] = TB;
recvLen++;
}
I don't think this method is efficient at all. If the server sent 1024 bytes, recv() will be called 1024 times.
Is there any other method to recv() until a NULL char is received, or some better method than this one I'm using?
EDIT:
i added the packet size infront of the data sent from the server, but now, if a false packet or even sometimes for no reason, packets gets messed up and no correct data is received. here is my code
#define UPLOAD_LEN 2755
int PacketSize, recvLen;
char Size[4];
char recvBuffer[UPLOAD_LEN+1];
while(1)
{
if(recv(Socket,Size,4,0)>0)
{
Size[4] = '\0';
PacketSize = atoi(Size);
if (PacketSize > UPLOAD_LEN || PacketSize <= 0) continue;
recvLen = recv(Socket, recvBuffer, PacketSize, 0);
} else recvLen = -1;
if (recvLen > 0)
{
recvBuffer[recvLen] = '\0';
ProcessData(recvBuffer);
}
else
{
closesocket(Socket);
}
}
I have never understood why communications protocols never support the one use case programmers expect to be able to do: exchange arbitrarily sized blobs with sends and recv's aligned on boundaries.
So theres no real shortcut here. You need to keep a persistent buffer that holds any data left over from the previous call to recv. Keep adding data to the end as you receive it, and return up to the terminating zero each time you find one. You'll probably have at least a partial following packet, so move that to the start of the buffer to serve as your initial state on the next call.
Create a buffer and extract your protocol messages from that. If the buffer does not contain a complete message, then recv() until it does. Here's a simple C implementation to buffer a socket (lightly tested, compiles on MS VS2008):
#include <winsock2.h>
#include <string.h>
typedef struct buffsock {
SOCKET s;
char* buf;
size_t maxlen;
size_t curlen;
} buffsock_t;
void buffsock_init(buffsock_t* bs,SOCKET s,size_t maxlen)
{
bs->s = s;
bs->buf = malloc(maxlen);
bs->maxlen = maxlen;
bs->curlen = 0;
}
void buffsock_free(buffsock_t* bs)
{
free(bs->buf);
bs->buf = NULL;
bs->maxlen = 0;
bs->curlen = 0;
bs->s = INVALID_SOCKET;
}
/* Attempt to fill internal buffer.
* Returns 0 if socket closed.
* Returns number of additional bytes in buffer otherwise.
*/
int buffsock_fill(buffsock_t* bs)
{
int bytes;
bytes = recv(bs->s,bs->buf + bs->curlen,bs->maxlen - bs->curlen,0);
if(bytes == SOCKET_ERROR)
return -1;
bs->curlen += bytes;
return bytes;
}
/* Return up to <bytes> from buffered socket.
* If return value 0 socket was closed.
* If return value >0 and <bytes socket received partial message.
*/
int buffsock_bytes(buffsock_t* bs,size_t bytes,void* msg)
{
while(bs->curlen < bytes)
{
int result;
result = buffsock_fill(bs);
if(result == -1)
return -1; /* error on socket */
if(result == 0)
break;
}
if(bytes > bs->curlen)
bytes = bs->curlen;
memcpy(msg,bs->buf,bytes);
bs->curlen -= bytes;
memmove(bs->buf,bs->buf + bytes,bs->curlen);
return bytes;
}
/* Implmementation of a protocol with two big-endian bytes indicating
* msg size followed by <size> bytes of message.
* Returns -1 if error on socket.
* Returns -2 if partial message recv'd (shouldn't happen as long as
* internal buffer is bigger than max message size).
* Returns -3 if user buffer not big enough to hold message.
* Returns size of message otherwise.
*/
int get_protocol_message(buffsock_t* bs,void* msg,size_t maxlen)
{
int bytes;
u_short len;
bytes = buffsock_bytes(bs,sizeof(u_short),&len);
if(bytes == 0)
return 0; /* socket closed, no more messages */
if(bytes == -1)
return -1; /* error on socket */
if(bytes < sizeof(u_short))
return -2; /* partial message */
len = ntohs(len);
if(len > maxlen)
return -3; /* message exceeds user buffer */
bytes = buffsock_bytes(bs,len,msg);
if(bytes < len)
return -2; /* partial message */
return bytes;
}
Use it like this:
int len;
char msg[256];
buffsock_t bs;
/* open a socket */
buffsock_init(&bs,sock,1024);
len = get_protocol_message(&bs,msg,sizeof(msg));
The key is TCP/IP has no concept of message boundaries, so recv() can return 1 to number of bytes requested. The received buffer could contain multiple or even partial messages.
This code just appends received data into a buffer. The protocol requests bytes from the buffer, and the buffer is filled from the socket. as bytes are removed the remaining buffered data is shifted to the beginning of the buffer.
In this case, two bytes are requested, converted to a length, then the remaining bytes are requested. If a request can't be satisfied, more data is recv'd.
Hope this helps.
There are several ways that you could do this.
Option #1: Before sending out any information, send out an int at the front of your packet which contains the size of the packet. Read this int, and then allocate a buffer which is the length of the int that you just received. Then you can recv() the entire packet at one time.
Option #2: Read in 1024 bytes at a time. recv() will give you back the number of bytes read. You can then use strlen() to figure out if you have more than one packet in your buffer. It would probably make the most sense to make this recursive(assuming that you could have several packets in 1024 bytes); so that you split the packets based on NULL bytes.

Resources