I need some help sending a file over a serial connection. I have two RS232 to USB cables, and I am testing out my code by using one to send data, and the other to receive it. I have them both physically connected to each other.
So I wrote some code adapted from a few sources, and I can successfully transfer a series of characters. One program receives the data, the other sends it. I have these two open in two separate terminals. Find the two blocks of code below:
serialout.c
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
int main()
{
//writing
int writeport = open_port("/dev/ttyUSB0");
char str[] = "hello how are you?";
int n = write(writeport, str, strlen(str));
if (n < 0)
fputs("write() of bytes failed!\n", stderr);
//closing ports
close(writeport);
}
int open_port(char str[])
{
int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd, &options); //this gets the current options set for the port
// setting the options
cfsetispeed(&options, B9600); //input baudrate
cfsetospeed(&options, B9600); // output baudrate
options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
//options.c_cflag &= ~CSIZE; /* mask the character size bits */
options.c_cflag |= CS8; /* select 8 data bits */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
options.c_iflag &= ~INPCK; // disable parity check
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
options.c_oflag |= OPOST; // ?? choosing processed output
options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)
// settings for no parity bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately
return (fd);
}
serialin.c
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
int main()
{
//reading
int readport = open_port("/dev/ttyUSB1");
//trying to read one character at a time
char buff;
int n = 1;
while (n > 0)
{
n = read(readport, &buff, 1);
printf("%c", buff, buff);
}
printf("\n");
//closing ports
close(readport);
}
int open_port(char str[])
{
int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK); // ?? NDELAY or NONBLOCK?
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
fcntl(fd, F_SETFL, 0);
struct termios options;
tcgetattr(fd, &options); //this gets the current options set for the port
// setting the options
cfsetispeed(&options, B9600); //input baudrate
cfsetospeed(&options, B9600); // output baudrate
options.c_cflag |= (CLOCAL | CREAD); // ?? enable receicer and set local mode
//options.c_cflag &= ~CSIZE; /* mask the character size bits */
options.c_cflag |= CS8; /* select 8 data bits */
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // choosing raw input
options.c_iflag &= ~INPCK; // disable parity check
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable software flow control
options.c_oflag |= OPOST; // ?? choosing processed output
options.c_cc[VMIN] = 0; // Wait until x bytes read (blocks!)
options.c_cc[VTIME] = 0; // Wait x * 0.1s for input (unblocks!)
// settings for no parity bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &options); //set the new options ... TCSANOW specifies all option changes to occur immediately
return (fd);
}
So what I'd like to do is send a file. For example, a jpeg file. Is there some way to convert it to bytecode and reassemble it as a jpeg? I have searched around but have found little relevant information unfortunately - maybe I'm searching the wrong terms.
Even better would be to send the files after they are zipped. Ultimately, I am using gzip to zip jpeg files, then send them over the serial connection. Thanks everyone!
If you want to transfer files, you should break the file up into chunks, and use a checksum on each chunk; then verify the checksums as you rejoin the chunks on the other side.
This is not a new problem. Others have solved it for you. You should get an existing reliable file-transfer program and run it over the serial link.
The top choice would be rsync. That is GPL, so the license may keep you from using it if you are doing proprietary work.
Another good choice would be XMODEM (or YMODEM or ZMODEM). I found a C implementation of XMODEM with a BSD license, so you can use this for sure. This is also smaller and simpler than rsync.
http://www.menie.org/georges/embedded/#xmodem
http://en.wikipedia.org/wiki/XMODEM
I was also going to suggest Z-Modem or X-Modem for file transferring. But what you've got to understand is that there's nothing special about files. As far as the transferring computer and receiving terminal are concerned, the file being sent is just a stream of binary data, no matter if it's a jpeg image, a compressed file or just a text file.
All you've got to do is write the incoming stream into a file an make sure it has the right extension so it can be properly handled.
The transfer protocols mentioned above only add layers to ensure the whole file is being sent and that it is not corrupted, but you can dismiss them on a first instance to understand the concept.
Related
My project uses serial ports on an ARM dev board.
The older version of the board (TS-7800) continues to work. I must upgrade to the newer version of the board (TS-7800-V2) to complete the project.
On the new board, I can open a file descriptor for the serial port, but when I read from the port, a -1 is returned. Here is the open file code:
gps_fd = initPort("/dev/ttts4", "COM3", O_RDWR, B19200);
That is a function call in main.c. Here is
initPort()
int initPort( char *port, char *name, int oflags, speed_t baudRate ){
int fd; //File descriptor
fd = open(port, oflags); //Open the port like a file
printf("fd = %d\n", fd); //Print file descriptor to terminal
assert(fd > 0); //Open returns -1 on error
struct termios options; //Initialize a termios struct
tcgetattr(fd, &options); //Populate with current attributes
cfsetospeed (&options, baudRate); //Set baud rate out
cfsetispeed (&options, baudRate); //Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE; //Clear bit-length flag so it can be set
//8N1 Serial Mode
options.c_cflag |= CS8; //Set bit-length: 8
options.c_cflag &= ~PARENB; //Set parity: none
options.c_cflag &= ~CSTOPB; //Set stop bit: 1
options.c_cflag &= ~CRTSCTS; //Set flow control: none
options.c_iflag &= ~ICANON; //Enable canonical input
options.c_cflag |= (CLOCAL | CREAD); //Enable receiver, and set local mode
tcsetattr(fd, TCSANOW, &options); //Set new attributes to hardware
return fd;
}
The fd (gps_fd) returns a "3", which is a good fd. Another application I was working in returned "15" for the fd, which is also good. Performing a read of the serial port:
bytesRead = read(gps_fd, buf, sizeof(buf) - 1);
Causes "bytesRead" to be -1. Putting in a "perror" line printed "Input/output error".
I am running the same code that I was running on the earlier version of the board and the device names (/dev) have not changed. Is there anything else I should look for that would cause the port not to read?
Thanks, everybody who commented, for increasing my knowledge!
It turns out that the FPGA (supplies hardware UARTs to the board) was turned off. This is a new board that is still in development and this is a problem the designers are aware of and they have a fix in the works. If you find yourself in a similar situation, keep in contact with the maker of the board.
This single board computer (TS-7800-V2) from Technologic Systems is great and they provide excellent customer service.
I want to read data from UART, i followed this tutorial, the write function works as expected, however i'am getting problem with the read function :
This is the uart_init function:
void uart_init()
{
printf("\n +----------------------------------+");
printf("\n | Serial Port Write |");
printf("\n +----------------------------------+");
/*------------------------------- Opening the Serial Port -------------------------------*/
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY| O_SYNC); /* !!blocks the read */
/* O_RDWR Read/Write access to serial port */
/* O_NOCTTY - No terminal will control the process */
/* O_NDELAY -Non Blocking Mode,Does not care about- */
/* -the status of DCD line,Open() returns immediatly */
if(fd == -1) /* Error Checking */
printf("\n Error! in Opening ttyUSB0 ");
else
printf("\n ttyUSB0 Opened Successfully ");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
cfsetispeed(&SerialPortSettings,B19200); /* Set Read Speed as 19200 */
cfsetospeed(&SerialPortSettings,B19200); /* Set Write Speed as 19200 */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 10; /* Read at least 10 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 19200 \n StopBits = 1 \n Parity = none");
}
the receive function :
void uart_receive()
{
char read_buffer[32]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int i = 0;
bytes_read = read(fd,&read_buffer,10); /* Read the data */
printf("\n\n Bytes Rxed %d", bytes_read); /* Print the number of bytes read */
printf("\n\n ");
for(i=0;i<bytes_read;i++) /*printing only the received characters*/
printf("%c",read_buffer[i]);
printf("\n +----------------------------------+\n\n\n");
}
the main function :
void main(void)
{
uart_init();
/*------------------------------- Write data to serial port -----------------------------*/
//uart_write_commande(write_buffer); //Write function works well
uart_receive();
close(fd);/* Close the Serial port */
}
I execute the program and wait for data bytes to be received in UART, i send data using UART but the read function keeps blocked.
I'am using a Virtual machine with Ubunutu 14.04 on it, and i'am not sure that using an emulated UART can cause problems during reception.
Your program is hanging in the read() syscall because it is blocked waiting for a line-termination character.
You tried to configure the port for non-canonical mode with the statement
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode
but that operation is on the wrong termios element.
The ICANON attribute is part of the lflag element (and not the iflag). (This error originates from the tutorial you referenced!)
Therefore your program is performing blocking canonical reads.
There's a convenient termios function for configuring non-canonical mode:
cfmakeraw() sets the terminal to something like the "raw" mode of the old
Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. The terminal
attributes are set as follows:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
There's an error in the read function
bytes_read = read(fd,&read_buffer,10); /* Read the data
should be
bytes_read = read(fd,read_buffer,10); /* Read the data
Your read() function may be blocked, for whatever reason. Here is a discussion on reading from a serial port, including code for blocked/unblocked settings.
It may also be possible that there is no data being transferred, leading to nothing being read. Without having access to the hardware setup, it is difficult to go further without very specific information about what you are seeing.
Also, in addition to passing the read_buffer correctly (another answer), there are at least two additional things that may improve:
1) check the return of read before using it:
bytes_read = read(fd,&read_buffer,10); /* Read the data*/
if(bytes_read > 0)
{
...
}
2) Change:
for(i=0;i<bytes_read;i++) /*printing only the received characters*/
printf("%c",read_buffer[i]);
To:
//after successful read:
read_buffer[bytes_read]=0;//place null termination after last character read.
printf("%s",read_buffer);//note format specifier
This will print the number of characters read, without the loop.
In my program a connection is opened with serial port that recieves 10-byte packages with set frequency (usually 100 Hz). However i have run into the following problem: sometimes (but not always) when i am closing connection, the program hangs.
In the following code i do not specify constants; at runtime connection parameters (baud rate, parity...) are determined from command line. In practice, connection is usually opened with baud rate 230400, no parity, 8-bit character size and 1 stop bit.
This is how i am opening serial port:
struct termios config;
LOGPRINT("Will operate on device \'%s\'\n", pconfig->device);
LOGPRINT("Opening serial device\n");
int flag = O_RDWR | O_NOCTTY | ASYNC_LOW_LATENCY;
// pconfig contains some necessary information to set connection parameters
// async == false for connectin that causes problems
int fd = open(pconfig->device, async? flag | O_NONBLOCK: flag);
if(fd == -1)
{ /* skipped error handling */ }
LOGPRINT("Retrieving terminal attributes\n");
if(!!tcgetattr(fd, &config))
{ /* skipped error handling */ }
else
{
// backup old configuration for restoring it on exit
if(poldconfig != NULL)
memcpy(poldconfig, &config, sizeof(struct termios));
LOGPRINT("Setting terminal attributes\n");
set_attribs(fd, &config, pconfig);
}
...
int set_attribs(const int fd, struct termios* pterm, const struct serial_config* pconfig)
{
// set baud rate
// baud_rates contains integers of actual rates
LOGPRINT("Setting baud rate to %d.\n", baud_rates[pconfig->baud_rate_index]);
// change to raw mode
LOGPRINT("Setting terminal to raw mode\n");
pterm->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
| IGNCR | ICRNL | IXON);
pterm->c_oflag &= OPOST;
pterm->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
// baud_bits contains actual pre-defined baud rate flags
cfsetispeed(pterm, baud_bits[pconfig->baud_rate_index]);
cfsetospeed(pterm, baud_bits[pconfig->baud_rate_index]);
LOGPRINT("Set character size, parity and stop bit flags.\n");
// set character size
pterm->c_cflag &= ~CSIZE;
pterm->c_cflag |= pconfig->csize_flag;
// set parity
pterm->c_cflag &= ~(PARENB | PARODD);
pterm->c_cflag |= pconfig->parity_flag;
// set stopbits
pterm->c_cflag &= ~CSTOPB;
pterm->c_cflag |= pconfig->stopbits_flag;
// enable reading; ignore control lines
pterm->c_cflag |= CREAD | CLOCAL;
LOGPRINT("Flush terminal.\n");
// flush terminal
tcflush(fd, TCIOFLUSH);
LOGPRINT("Apply parameters.\n");
return tcsetattr(fd, TCSANOW, pterm);
}
For valid reasons, i needed to force serial device driver to use 1-byte buffer for incoming/outgoing data, which i have set using this function:
int set_port_type(int fd, int ptype)
{
struct serial_struct temp;
int res = ioctl(fd, TIOCGSERIAL, &temp);
if(!!res)
{ /* stipped error handling */ }
temp.type = ptype;
res = ioctl(fd, TIOCSSERIAL, &temp);
if(!!res)
{ /* stipped error handling */ }
return res;
}
with ptype = PORT_UNKNOWN or ptype = PORT_16450.
When finished with serial port, i perform the following operations:
Flush it
Set its type back to 16550A
Restore its settigns to saved ones
Close it
From time to time it causes the program to hang and stop responding to any external stimulus. It forces me to suspend the program and then reboot system, as it never releases file descriptors it holds.
What can i do to ensure that does not happen? Even if settings are not restored, the program needs to close on its own - maybe with error code.
Operating system i am using is TinyCore Linux. No other user program is being run an the same time as my program.
IMportant clarification. Serial device i am working with is actually NPort 5232 - asyncronous RS-422/485 - Ethernet communicator (description from vendor. Consequently, serial devices i am working with are virtual, which enables me to try and change their type / size of internal buffer.
I have the following set up: a computer that runs Linux (TCL) and device that sends packets of fixed length (10 bytes) with fixed frequency (100 Hz).
On computer, i have that serial port open, and i am trying to read incoming data. However, instead of recieving it in 10-byte chunks all the time, lesser amounts of data are being read, and i am forced to reassemble them back.
My goal is to send a responce for each packet once it arrives, preserving intervals between packets as much as possible.
This is how i open the serial port:
int fd = open(device_name, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd == -1)
{
LOGPRINT("Failed to open terminal file.\nError #%d (%s)\n",
errno, strerror(errno));
return fd;
}
LOGPRINT("Setting terminal attributes\n");
struct termios config;
struct termios *pterm = &config;
// set baud rate
LOGPRINT("Setting baud rate to %d.\n", baud_rates[baud_rate_index]);
// change to raw mode
LOGPRINT("Setting terminal to raw mode\n");
pterm->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
| IGNCR | ICRNL | IXON);
pterm->c_oflag &= OPOST;
pterm->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
cfsetispeed(pterm, baud_bits[pconfig->baud_rate_index]);
cfsetospeed(pterm, baud_bits[pconfig->baud_rate_index]);
LOGPRINT("Set character size, parity and stop bit flags.\n");
// set character size
pterm->c_cflag &= ~CSIZE;
pterm->c_cflag |= csize_flag;
// set parity
pterm->c_cflag &= ~(PARENB | PARODD);
pterm->c_cflag |= parity_flag;
// set stopbits
pterm->c_cflag &= ~CSTOPB;
pterm->c_cflag |= stopbits_flag;
// enable reading; ignore control lines
pterm->c_cflag |= CREAD | CLOCAL;
// disable flow control
pterm->c_cc[VMIN] = 1;
pterm->c_cc[VTIME] = 0;
LOGPRINT("Flush terminal.\n");
// flush terminal
tcflush(fd, TCIOFLUSH);
LOGPRINT("Apply parameters.\n");
return WRAPSYSCALL(tcsetattr(fd, TCSANOW, pterm));
Additionally i set the following on file descriptor with fnctl:
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC | O_DIRECT);
I am reading from file descriptor in a cycle with select() call on file descriptor set containing fd, then reading all bytes avaiable (requesting number of bytes much bigger then 10).
What should i change to ensure incoming data are processed as they come properly, in timely manner?
If your program does nothing else than waiting for bytes and answering, then you can try using blocking file operations. Remove O_NONBLOCK flag from your open function call, and after setting the port just do a read(fd, buffer, 10). The function will return after it reads exactly 10 bytes (or an error happens or signal arrives).
However, bear in mind that you read operation can start in the middle of packet transmission, so for example you can get last x bytes of n-th packet and first 10-x of packet n+1.
I am having some trouble reading some data from a serial port I opened the following way. I've used this instance of code plenty of times and all worked fine, but now, for some reason that I cant figure out, I am completely unable to read anything from the serial port.
I am able to write and all is correctly received on the other end, but the replies (which are correctly sent) are never received (No, the cables are all ok ;) )
The code I used to open the serial port is the following:
fd = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd == -1)
{
Aviso("Unable to open port");
return (fd);
}
else
{
//Get the current options for the port...
bzero(&options, sizeof(options)); /* clear struct for new port settings */
tcgetattr(fd, &options);
/*-- Set baud rate -------------------------------------------------------*/
if (cfsetispeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetispeed:");
if (cfsetospeed(&options, SerialBaudInterp(BaudRate))==-1)
perror("On cfsetospeed:");
//Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB; /* Parity disabled */
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= SerialDataBitsInterp(8); /* CS8 - Selects 8 data bits */
options.c_cflag &= ~CRTSCTS; // disable hardware flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY); // disable XON XOFF (for transmit and receive)
options.c_cflag |= CRTSCTS; /* enable hardware flow control */
options.c_cc[VMIN] = 0; //min carachters to be read
options.c_cc[VTIME] = 0; //Time to wait for data (tenths of seconds)
//Set the new options for the port...
tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &options)==-1)
{
perror("On tcsetattr:");
}
PortOpen[ComPort] = fd;
}
return PortOpen[ComPort];
After the port is initializeed I write some stuff to it through simple write command...
int nc = write(hCom, txchar, n);
where hCom is the file descriptor (and it's ok), and (as I said) this works. But... when I do a read afterwards, I get a "Resource Temporarily Unavailable" error from errno.
I tested select to see when the file descriptor had something t read... but it always times out!
I read data like this:
ret = read(hCom, rxchar, n);
and I always get an EAGAIN and I have no idea why.
Update:
The HW is working fine! I can see that there is inbound data on the serial port because I've made a debug cable to read whats going on on another terminal. So...
I know what nonblocking should do. My question is... why isn't anything getting read!. The same setup works fine on windows, so all hardware is working fine...
This is driving me nuts! I'm sure it's something simple as hell! I even tried getting rid of O_NONBLOCK to see when I would receive something... but nothing...
Read this.
EAGAIN Non-blocking I/O has been
selected using O_NONBLOCK and no data
was immediately available for reading.
You need to check the serial terminal settings first.
use command - stty -F /dev/ttyUSB0 -a
Check that ctsrts is selected as -ctsrts
and do the other required settings with stty utility and you are done.
EAGAIN with O_NONBLOCK means there's been no data received on the port. Check that the port and cable are working properly (using minicom or some other known-good program), and that the remote really is sending some data.
see my code samples, if EAGAIN, you'd try to read again:
...
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
...
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // input
options.c_oflag &= ~OPOST; // output
...
fd = open("/dev/ttyUSB0", O_RDWR | O_NDELY | O_NOCTTY);
fcntl(fd, F_SETFL, 0);
...
int nc = write(hCom, txchar, n);
msleep(500); // wait 500ms
fcntl(hCom, F_SETFL, FNDELAY); // don't block serial read
ret = read(hCom, rxchar, n);
if (ret > 0) {
here had read n bytes or just partial data, read again if partial.
}
if (ret < 0) {
if (EAGAIN == errno) {
not a real error, just read again.
} else {
oops, errors.
}
}
...