C语言receivedata函数如何实现数据接收?

99ANYc3cd6
预计阅读时长 28 分钟
位置: 首页 C语言 正文

从网络套接字接收数据(最常见)

这是“接收数据”最典型的应用,通常指从TCP或UDP连接中读取信息。

c语言 receivedata
(图片来源网络,侵删)

核心函数:recv()read()

对于TCP套接字,最常用的函数是 recv()

#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数详解:

  • sockfd: 套接字描述符,这是通过 socket() 函数创建,并通过 connect() (客户端) 或 accept() (服务器) 建立连接后得到的文件描述符,它就像一个“通道号”,告诉操作系统你要从哪个连接收数据。
  • buf: 缓冲区,一个指向内存区域的指针,用于存放接收到的数据,你需要提前分配好这个空间。
  • len: 缓冲区长度,告诉操作系统你的缓冲区有多大,最多能接收多少字节的数据。
  • flags: 接收标志,通常设置为 0,表示使用默认的接收方式,其他常用标志有:
    • MSG_WAITALL: 等待直到请求的字节数都接收到,或者发生错误/连接关闭。
    • MSG_DONTWAIT: 非阻塞模式,如果数据不可用,函数会立即返回而不是等待。
    • MSG_PEEK: 查看数据但不从套接字缓冲区中移除它,下次 recv() 时还能读到同样的数据。

返回值:

  • 成功:返回实际接收到的字节数
  • 连接关闭:返回 0,这是客户端主动调用 close() 或服务器主动断开连接的标志。
  • 出错:返回 -1,并设置 errno 来表示具体的错误原因(EAGAINEWOULDBLOCK 表示非阻塞模式下暂时没有数据)。

一个完整的TCP服务器接收数据示例

下面是一个简单的TCP服务器,它监听端口,接受客户端连接,然后循环接收客户端发来的数据,直到客户端关闭连接。

c语言 receivedata
(图片来源网络,侵删)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // for close()
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h> // for errno
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    // 1. 创建套接字文件描述符
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    // 2. 设置套接字选项,允许地址重用
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的网络接口
    address.sin_port = htons(PORT);        // 将端口号从主机字节序转换为网络字节序
    // 3. 绑定套接字到指定端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    // 4. 开始监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    printf("Server listening on port %d...\n", PORT);
    // 5. 接受一个新连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    printf("Client connected. IP: %s, Port: %d\n", 
           inet_ntoa(address.sin_addr), ntohs(address.sin_port));
    // 6. 循环接收数据
    int valread;
    while ((valread = recv(new_socket, buffer, BUFFER_SIZE, 0)) > 0) {
        // 将接收到的数据原样发回给客户端 (回显服务器)
        send(new_socket, buffer, valread, 0);
        printf("Received %d bytes: %s", valread, buffer);
        // 清空缓冲区,为下一次接收做准备
        memset(buffer, 0, BUFFER_SIZE);
    }
    // 检查是否是客户端主动关闭了连接
    if (valread == 0) {
        printf("Client disconnected.\n");
    } else if (valread == -1) {
        perror("recv failed");
    }
    // 7. 关闭套接字
    close(new_socket);
    close(server_fd);
    return 0;
}

从文件接收数据(读取文件)

从文件中读取数据也常被泛称为“接收数据”,核心函数是 fread()read()

核心函数:fread() (高级I/O,带缓冲)

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr: 指向存储读取数据的缓冲区。
  • size: 每个数据项的大小(单位是字节)。
  • nmemb: 要读取的数据项的数量。
  • stream: FILE* 指针,由 fopen() 返回。
  • 返回值:成功返回实际读取到的数据项的数量(不是字节数),如果到达文件末尾或出错,可能返回小于 nmemb 的值,甚至 0

核心函数:read() (底层I/O,文件描述符)

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  • fd: 文件描述符,由 open() 返回。
  • buf: 缓冲区。
  • count: 要读取的字节数。
  • 返回值:成功返回读取的字节数,到达文件末尾返回 0,出错返回 -1

一个从文件读取数据的示例

#include <stdio.h>
#include <stdlib.h>
#define FILENAME "example.txt"
#define BUFFER_SIZE 256
int main() {
    FILE *fp;
    char buffer[BUFFER_SIZE];
    size_t items_read;
    // 使用 fopen 打开文件
    fp = fopen(FILENAME, "r"); // "r" 表示只读模式
    if (fp == NULL) {
        perror("Error opening file");
        return EXIT_FAILURE;
    }
    printf("Reading from file: %s\n", FILENAME);
    // 循环读取文件,直到文件结束
    while ((items_read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) {
        // fwrite 将读取到的数据打印到标准输出
        fwrite(buffer, 1, items_read, stdout);
    }
    // 检查是否是因错误而停止
    if (ferror(fp)) {
        perror("Error reading file");
    }
    // 关闭文件
    fclose(fp);
    printf("\nFile reading complete.\n");
    return EXIT_SUCCESS;
}

从串口接收数据

在嵌入式系统或与硬件设备通信时,会用到串口,串口在Linux下被视为一种特殊的文件,因此也使用 read() 函数来读取数据。 **

  1. 打开串口:使用 open() 函数,以读写模式打开设备文件(如 /dev/ttyS0/dev/ttyUSB0)。
  2. 配置串口:这是最关键的一步,需要使用 termios 结构体来设置波特率、数据位、停止位、校验位、流控等。
  3. 读取数据:使用 read() 函数从打开的文件描述符中读取数据。
  4. 关闭串口:使用 close() 函数。

一个伪代码示例(串口配置是重点)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h> // 串口设置的头文件
#include <errno.h>
#define SERIAL_PORT "/dev/ttyUSB0"
#define BAUD_RATE B9600
int setup_serial_port(int fd) {
    struct termios options;
    // 获取当前串口设置
    tcgetattr(fd, &options);
    // 设置波特率
    cfsetispeed(&options, BAUD_RATE);
    cfsetospeed(&options, BAUD_RATE);
    // 设置数据位为8位
    options.c_cflag &= ~CSIZE; // 清除数据位设置
    options.c_cflag |= CS8;    // 设置为8位数据位
    // 设置无奇偶校验
    options.c_cflag &= ~PARENB;
    // 设置1位停止位
    options.c_cflag &= ~CSTOPB;
    // 启用接收
    options.c_cflag |= (CLOCAL | CREAD);
    // 原始输入模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    // 禁用软件流控
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    // 原始输出模式
    options.c_oflag &= ~OPOST;
    // 设置超时:100ms (10 deciseconds)
    options.c_cc[VMIN]  = 0;  // 不阻塞,有数据就读,没有就返回0
    options.c_cc[VTIME] = 1;  // 等待0.1秒
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        perror("tcsetattr failed");
        return -1;
    }
    return 0;
}
int main() {
    int serial_fd;
    char buffer[256];
    // 1. 打开串口
    serial_fd = open(SERIAL_PORT, O_RDWR);
    if (serial_fd < 0) {
        perror("Error opening serial port");
        return EXIT_FAILURE;
    }
    // 2. 配置串口
    if (setup_serial_port(serial_fd) < 0) {
        close(serial_fd);
        return EXIT_FAILURE;
    }
    printf("Serial port %s opened and configured.\n", SERIAL_PORT);
    // 3. 循环读取数据
    while (1) {
        int bytes_read = read(serial_fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0'; // 确保字符串以空字符结尾
            printf("Received: %s\n", buffer);
        } else if (bytes_read < 0) {
            perror("read error");
            break;
        }
        // bytes_read == 0,表示超时,继续循环
    }
    // 4. 关闭串口
    close(serial_fd);
    return EXIT_SUCCESS;
}

总结与关键点

场景 核心函数/方法 关键概念 关键参数/结构体
网络 recv(), send() 套接字、TCP/UDP、字节序、IP地址、端口 socket(), bind(), listen(), accept(), struct sockaddr_in
文件 fread(), fwrite() 文件指针、缓冲区、文件结束 fopen(), fclose(), FILE*
串口 read(), write() 设备文件、波特率、数据位、停止位、校验位、流控 open(), close(), struct termios

无论哪种“接收数据”,其核心思想都是相似的:

  1. 建立通道:创建套接字、打开文件或设备。
  2. 准备缓冲区:在内存中预留一块空间来存放数据。
  3. 调用读取函数:将通道和缓冲区交给操作系统,让它填充数据。
  4. 处理返回值:根据返回的字节数判断数据是否成功接收,并处理可能的错误(如连接关闭、文件结束)。
  5. 清理资源:完成后,关闭通道,释放资源。

希望这个详细的解释能帮助你全面理解C语言中的“receivedata”操作!

c语言 receivedata
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
C语言formatString漏洞如何产生与防范?
« 上一篇 01-03
C语言,如何判断一个字符串是否为回文?
下一篇 » 01-03

相关文章

取消
微信二维码
支付宝二维码

目录[+]