C语言BIO通信函数详解:实现高效可靠的串口/网络通信30


在C语言编程中,BIO(Blocking I/O,阻塞式I/O)模型是一种常见的通信方式,尤其在串口通信和网络编程中被广泛应用。 虽然相比NIO(Non-blocking I/O,非阻塞式I/O)和AIO(Asynchronous I/O,异步I/O)模型,BIO在并发处理能力上有所欠缺,但在一些对实时性要求较高、并发量较小的场景中,BIO仍然具有其独特的优势,例如代码简洁易懂,易于调试等。 本文将深入探讨C语言中实现BIO通信的常用函数,并结合实例分析其使用方法和注意事项。

一、串口通信中的BIO函数

在Linux系统中,进行串口通信通常需要用到termios结构体和一系列与之相关的函数。这些函数主要用于配置串口参数、读取和写入数据等。

1. open()函数: 用于打开串口设备。该函数的第一个参数是串口设备文件的路径,例如"/dev/ttyACM0"或"/dev/ttyUSB0"。第二个参数指定打开方式,例如O_RDWR (读写) 和 O_NOCTTY (不将串口设置为控制终端)。#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
perror("open");
exit(1);
}

2. tcgetattr()和tcsetattr()函数: 用于获取和设置串口属性。termios结构体包含了串口的各种参数,例如波特率、数据位、校验位、停止位等。tcgetattr()函数将当前串口属性复制到termios结构体中,tcsetattr()函数则将termios结构体中的属性应用到串口中。struct termios oldtio, newtio;
tcgetattr(fd, &oldtio); // 获取当前串口属性
newtio = oldtio; // 将旧属性复制到新属性
// 设置新属性
cfsetospeed(&newtio, B115200); // 设置输出波特率为115200bps
cfsetispeed(&newtio, B115200); // 设置输入波特率为115200bps
newtio.c_cflag &= ~PARENB; // 不使用奇偶校验
newtio.c_cflag &= ~CSTOPB; // 使用1位停止位
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8; // 使用8位数据位
newtio.c_cflag &= ~CRTSCTS; // 不使用硬件流控
tcsetattr(fd, TCSANOW, &newtio); // 应用新属性

3. read()和write()函数: 用于从串口读取数据和向串口写入数据。这两个函数都是阻塞式的,也就是说,如果串口没有数据可读或无法写入数据,则函数会一直阻塞,直到有数据可读或可写。char buffer[1024];
int n = read(fd, buffer, sizeof(buffer)); // 读取数据
if (n < 0) {
perror("read");
exit(1);
}
n = write(fd, "Hello, world!", 14); // 写入数据
if (n < 0) {
perror("write");
exit(1);
}

4. close()函数: 用于关闭串口。close(fd);


二、网络通信中的BIO函数

在网络编程中,BIO模型通常基于套接字(Socket)进行。常用的函数包括:

1. socket()函数: 创建一个套接字。

2. bind()函数: 将套接字绑定到指定的IP地址和端口号。

3. listen()函数 (对于服务器端): 开始监听连接请求。

4. accept()函数 (对于服务器端): 接受客户端的连接请求。

5. connect()函数 (对于客户端): 建立与服务器的连接。

6. recv()和send()函数: 用于接收和发送数据。 这些函数与串口通信中的read()和write()函数类似,也是阻塞式的。

7. close()函数: 关闭套接字。

下面是一个简单的BIO服务器端代码示例:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 51717;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("Here is the message: %s",buffer);
n = write(newsockfd,"I got your message",18);
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
close(newsockfd);
close(sockfd);
return 0;
}


三、BIO模型的优缺点

优点:
编程简单,易于理解和调试。
在低并发场景下效率较高。
对于实时性要求高的应用比较适用。

缺点:
阻塞式I/O,一个连接占用一个线程,并发能力有限。
在高并发场景下效率低下,容易造成资源浪费。

四、总结

本文详细介绍了C语言中BIO通信函数在串口和网络编程中的应用。 选择BIO、NIO还是AIO模型取决于具体的应用场景和性能需求。 对于低并发、实时性要求高的应用,BIO模型仍然是一个不错的选择。 然而,对于高并发应用,建议考虑使用NIO或AIO模型以提高系统的并发处理能力。

需要注意的是,在实际应用中,还需要考虑错误处理、异常情况处理等细节,以确保程序的健壮性和可靠性。 此外,还需要根据具体的硬件和操作系统平台进行相应的调整。

2025-04-23


上一篇:Windows C语言彩色控制台输出详解

下一篇:C语言实现学生成绩等级输出及优化策略