前言
在使用涉及到系统调用的编程中,操作的文件描述符诸如管道之类的,获设备节点之类的(比如串口,USB口中讲usb作为camera的),涉及到内核态和用户态的数据读写,这时候经常遇到读写达不到理想,本文编写方式先列出问题点,后面给出对应的解决方案。
该文章主要针对系统调用注意事项,未完待续…
昨天(2022年7月7日)一位上海的朋友找我咨询串口的问题, 他采用的接收端模型为select+read方式, 发送端是RFID一直发送数据,发送频率10hz, 接收没有采用缓存起来然后解析, 导致read的数据产生黏包。
问题和处理
在串口编程中, 即使使用select复用IO
方式获取到内核态有数据可读,也可能无法满足你想的结果。比如算出需要读10个字节(理论按照私有协议有10字节才是一个完整包),有一定概率出现读小于10个字节的数据, 原因为
内核中用于套接字的缓冲区可能已达到了极限。
,此时所需的是调用者再次调用read或write函数,输入或输出剩余的字节。
因此针对该问题使用如下替代方案:
通过该方式可以满足读出想读的数据大小,如果读的数据小于n,那么说明串口通信另外一端丢包了,
只能将该包丢掉。
读:
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return(-1);
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
写:
ssize_t /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
/* end writen */
使用recv函数替代readn函数
未完待续…
思考
为什么readn和writenn函数都将void转为char指针?
在ANSI C标准中,不允许对void指针进行算术运算如pvoid++或pvoid+=1等,需要转换为char类型指针才能对指针进行加减操作。
2.c:17:8: error: invalid use of void expression *p = *pa;
root@ubuntu:/opt/socket/unpv13e/intro# cat 2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char a[10] = {0};
memcpy(a, "12345", strlen("12345"));
char *pa = a;
void *p = (void*)calloc(1, 10);
void *p1 = p; // notice
if(p)
{
for(; *pa != '\0'; pa++, p++) {
*p = *pa;
}
printf("p1:%s\n", p1);
free(p1); p1 = 0;
}
return 0;
}
root@ubuntu:/opt/socket/unpv13e/intro# gcc -o readn 2.c
2.c: In function ‘main’:
2.c:17:5: warning: dereferencing ‘void *’ pointer
*p = *pa;
^~
2.c:17:8: error: invalid use of void expression
*p = *pa;
^
2.c:23:15: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void *’ [-Wformat=]
printf("p1:%s\n", p1);