只能通过串行读取一个字节

我的机器上有一个C / Python设置,我正在做一些串行通信testing,出于某种原因,我从来没有读过多于1个字节的数据。

我的设置:我有一个Windows 7的机器,在虚拟框中运行OpenSUSE。 我有2个USB-RS232转换器和它们之间的适配器(所以它是从一个USB端口到另一个的循环)。

在Windows方面,我可以让他们通过Python到Python和C到Python进行通信。 一旦我使用Linux虚拟机,我可以从C(Linux)获取数据到Python(Windows),但是当我这样做的时候,我只能得到1个字节。 我在想如何打开文件或者在Linux C代码上执行读取操作有什么问题,但是我不知道可能是什么问题。

Python代码(使用PySerial):

>>> import serial >>> ser = serial.Serial(3) >>> ser Serial<id=0x2491780, open=True>(port='COM4', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False) >>> ser.read(5) 'Hello' >>> ser.write("hi you") 6L 

C代码:

 #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> int open_port() { int fd; fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) perror("open_port: Unable to open /dev/ttyUSB0 - "); else fcntl(fd, F_SETFL, 0); return fd; } int swrite(int fd, char * str) { int n; n = write(fd, str, strlen(str)); if (n<0) printf("write() of %d bytes failed\n", strlen(str)); return n; } int main() { int fd, databytes; char buf[100] = {0}; struct termios options; fd = open_port(); //Set the baud rate to 9600 to match tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); tcsetattr(fd, TCSANOW, &options); tcgetattr(fd, &options); databytes = swrite(fd, "Hello"); if(databytes > 0) printf("Wrote %d bytes\n", databytes); databytes = read(fd, buf, 100); if(databytes < 0) printf("Error! No bytes read\n"); else printf("We read %d bytes, message: %s\n", databytes, buf); close(fd); return 0; } 

我回来了:

 mike@linux-4puc:~> gcc serial_com.c mike@linux-4puc:~> ./a.out Wrote 5 bytes We read 1 bytes, message: h 

所以Linux-> Windows写入工作,python显示正确的“你好”string,但由于某种原因,我只能得到一个字节在Windows-> Linux的一面。

任何人都看到错误?

编辑:
根据我得到的反馈,我已经尝试了两个代码的调整。 听起来我不能保证所有的数据都会在那里,所以我试过了:

1)睡觉

  if(databytes > 0) printf("Wrote %d bytes\n", databytes); sleep(15); // Hack one to get the data there in time, worked databytes = read(fd, buf, 100); 

2)一个while循环

 while(1){ // Hack two to catch the data that wasn't read the first time. Failed // this only saw 'h' like before then sat waiting on the read() databytes = read(fd, buf, 100); if(databytes < 0) printf("Error! No bytes read\n"); else printf("We read %d bytes, message: %s\n", databytes, buf); } 

似乎循环不工作,所以没有读取的数据被丢弃? /编辑

从读(2)手册 ;

成功时返回读取的字节数(零表示文件结束),文件位置按此编号提前。 如果这个数字小于请求的字节数就不是错误; 这可能会发生,例如,因为现在实际上可用的字节更少(可能是因为我们接近文件结束,或者因为我们正在从管道读取数据或从终端读取数据)

换句话说,由于您在写入后立即从套接字中读取数据,并且您的计算机比串行端口快很多, 因此很可能只有一个字符可用于读取read(2)仅返回该字符。

阅读手册页说

尝试读取计数字节…

你的代码看起来像是假设整个缓冲区总是被一次read返回; 其有效的数据要通过多次调用返回。

errno == EINTR检查read返回-1是否是一个好习惯,然后重试(如果在GNU系统上运行,使用TEMP_FAILURE_RETRY )。 如果信号中断, read可能会返回一个瞬态错误。

正如其他人所回答的那样,C read()函数通过只返回一个字节来履行合同。

C read()和Python read()函数是完全不同的。

PySerial说,关于read(),

从串口读取大小字节。 如果设置超时,它可能会返回更少的字符按要求。 在没有超时的情况下,它将被阻塞,直到所请求的字节数被读取。

而C API没有这样的保证。 它将返回缓冲区中可用的任何字符(即使是0,如果还没有的话)。

如果你希望C端的行为像Python一样,你需要使用一个不同的函数。 像这样的东西:

 int read_exact_chars(int fildes, char *buf, size_t nbyte) { ssize_t chars_read; int chars_left = nbyte; while (chars_left) { chars_read = read(fildes, buf, chars_left) if (chars_read == -1) { /* An error occurred; bail out early. The caller will see that we read fewer bytes than requested, and can check errno */ break; } else { buf += chars_read; chars_left -= chars_read; } } /* return the actual number of characters read */ return nbyte - chars_left; } 

得到了一些很好的答案,并煽动了这个(+ 1的全部!),而所有的答案导致正确的结论(关于阅读没有得到的数据),没有任何输入实际上“固定”我遇到的问题。

下面是我最终得到这个工作的方法:

termios结构中的设置正在杀死我。 当我正在设置一些标志时,我没有设置所有的标志。 所以这:

 tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); tcsetattr(fd, TCSANOW, &options); 

更改为:

 tcgetattr(fd, &options); cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); options.c_iflag = 0; // Disable Input flags options.c_lflag = 0; // Disable Local mode flags options.c_oflag = 0; // Disable Output flags options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; //Data bits per character (8) options.c_cc[VMIN] = 50; // Wait for 50 characters or options.c_cc[VTIME] = 50; // Wait for 5 seconds options.c_cflag |= (CLOCAL | CREAD | HUPCL); // Ignore modem status lines, // enable receiver, // and hang up on last close options.c_cflag &= ~(PARENB | PARODD); //Clearing even and odd parity options.c_cflag &= ~CSTOPB; //Clear double stop bits tcsetattr(fd, TCSANOW, &options); 

现在有了这些变化,我可以从Python编写的C ++代码中获取数据。

我使用了这两个来源,提供了很多有关termios结构选项的“事实”:

  1. 标志描述
  2. 快速概览