C语言实现RK3588(ubuntu)的串口和网口通信程序

摘要:拿到了rk3588的板子,安装系统为ubuntu(linux),初步上手嵌入式软件开发,在该板子上进行简要的串口和网口开发。

rk3588开发板简介

在这里插入图片描述

串口

代码

代码1 使用/dev/ttyS4串口, 发送一个数据,接收一个数据

#include <stdio.h>
#include <string.h>

// Linux headers
#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()

int main() {
  // Open the serial port. Change device path as needed (currently set to an standard FTDI USB-UART cable type device)
  int serial_port = open("/dev/ttyS4", O_RDWR);

  printf("serial = %d\n",serial_port);

  // Create new termios struct, we call it 'tty' for convention
  struct termios tty;

  // Read in existing settings, and handle any error
  if(tcgetattr(serial_port, &tty) != 0) {
      printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
      return 1;
  }

  tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common)
  tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
  tty.c_cflag &= ~CSIZE; // Clear all bits that set the data size
  tty.c_cflag |= CS8; // 8 bits per byte (most common)
  tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
  tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)

  tty.c_lflag &= ~ICANON;
  tty.c_lflag &= ~ECHO; // Disable echo
  tty.c_lflag &= ~ECHOE; // Disable erasure
  tty.c_lflag &= ~ECHONL; // Disable new-line echo
  tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
  tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

  tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
  tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
  // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT ON LINUX)
  // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT ON LINUX)

  tty.c_cc[VTIME] = 10;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
  tty.c_cc[VMIN] = 0;

  // Set in/out baud rate to be 9600
  cfsetispeed(&tty, B9600);
  cfsetospeed(&tty, B9600);

  // Save tty settings, also checking for error
  if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
      printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
      return 1;
  }

  // Write to serial port
  unsigned char msg[] = { 'H', 'e', 'l', 'l', 'o', '\r' };
  write(serial_port, msg, sizeof(msg));

  // Allocate memory for read buffer, set size according to your needs
  char read_buf [256];

  // Normally you wouldn't do this memset() call, but since we will just receive
  // ASCII data for this example, we'll set everything to 0 so we can
  // call printf() easily.
  memset(&read_buf, '\0', sizeof(read_buf));

  // Read bytes. The behaviour of read() (e.g. does it block?,
  // how long does it block for?) depends on the configuration
  // settings above, specifically VMIN and VTIME
  int num_bytes = read(serial_port, &read_buf, sizeof(read_buf));

  // n is the number of bytes read. n may be 0 if no bytes were received, and can also be -1 to signal an error.
  if (num_bytes < 0) {
      printf("Error reading: %s", strerror(errno));
      return 1;
  }

  // Here we assume we received ASCII data, but you might be sending raw bytes (in that case, don't try and
  // print it to the screen like this!)
  printf("Read %i bytes. Received message: %s \n", num_bytes, read_buf);

  close(serial_port);

  printf("success!!!\n");
  getchar();
  return 0; // success
};

代码2 使用/dev/ttyS4串口, 发送一个数据,接收一个数据

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *port = "/dev/ttyS4";  // 根据实际设备修改
    int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1) {
        perror("Error opening serial port");
        return -1;
    }

    // 配置串口参数
    struct termios options;
    tcgetattr(fd, &options);
    cfsetispeed(&options, B115200);  // 输入波特率115200
    cfsetospeed(&options, B115200);  // 输出波特率115200
    
    options.c_cflag &= ~PARENB;    // 无校验
    options.c_cflag &= ~CSTOPB;    // 1位停止位
    options.c_cflag &= ~CSIZE;     // 清除数据位掩码
    options.c_cflag |= CS8;        // 8位数据位
    options.c_cflag &= ~CRTSCTS;   // 禁用硬件流控
    options.c_cflag |= CREAD | CLOCAL;  // 启用接收,忽略调制解调器信号
    
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // 原始模式
    options.c_iflag &= ~(IXON | IXOFF | IXANY);          // 禁用软件流控
    options.c_oflag &= ~OPOST;                           // 原始输出

    tcsetattr(fd, TCSANOW, &options);  // 应用配置

    // 发送数据
    const char *msg = "Hello, RK3588!\n";
    write(fd, msg, strlen(msg));

    // 接收数据(示例:接收10字节)
    char buf[256];
    ssize_t n = read(fd, buf, sizeof(buf) - 1);
    if (n > 0) {
        buf[n] = '\0';
        printf("Received: %s\n", buf);
    }

    close(fd);
    printf("success!!!\n");
    getchar();
    return 0;
}

代码3 用/dev/ttyS4串口,循环发送数据,且循环接受数据(采用多线程+中断的方式实现)

#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>

#define SERIAL_PORT "/dev/ttyS4"  // RK3588常用串口设备
#define BAUD_RATE B115200         // 常用波特率
#define BUFFER_SIZE 256           // 接收缓冲区大小

int serial_fd = -1;               // 串口文件描述符
volatile int running = 1;          // 程序运行标志

// 信号处理函数,用于优雅退出
void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n程序终止中...\n");
        running = 0;
    }
}

// 配置串口参数
int configure_serial(int fd) {
    struct termios options;
    
    // 获取当前串口设置
    if (tcgetattr(fd, &options) ){
        perror("tcgetattr失败");
        return -1;
    }
    
    // 设置输入输出波特率
    cfsetispeed(&options, BAUD_RATE);
    cfsetospeed(&options, BAUD_RATE);
    
    // 8位数据位,无校验,1位停止位
    options.c_cflag &= ~PARENB;   // 无奇偶校验
    options.c_cflag &= ~CSTOPB;   // 1位停止位
    options.c_cflag &= ~CSIZE;    // 清除数据位掩码
    options.c_cflag |= CS8;       // 8位数据位
    
    // 禁用硬件流控
    options.c_cflag &= ~CRTSCTS;
    
    // 启用接收器,忽略调制解调器控制线
    options.c_cflag |= (CLOCAL | CREAD);
    
    // 原始输入模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    
    // 禁用软件流控
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    
    // 原始输出模式
    options.c_oflag &= ~OPOST;
    
    // 设置超时和最小读取字符数
    options.c_cc[VMIN] = 0;      // 非阻塞读取
    options.c_cc[VTIME] = 10;    // 1秒超时 (单位: 0.1秒)
    
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &options)) {
        perror("tcsetattr失败");
        return -1;
    }
    
    // 清空输入输出缓冲区
    tcflush(fd, TCIOFLUSH);
    
    return 0;
}

// 接收线程函数
void *receive_thread(void *arg) {
    char buffer[BUFFER_SIZE];
    
    printf("串口接收线程已启动,监听 %s...\n", SERIAL_PORT);
    
    while (running) {
        ssize_t n = read(serial_fd, buffer, sizeof(buffer) - 1);
        
        if (n > 0) {
            buffer[n] = '\0';
            printf("接收 [%ld字节]: %s\n", n, buffer);
        } else if (n < 0 && errno != EAGAIN) {
            perror("读取串口数据失败");
            break;
        }
        
        usleep(10000); // 10ms延时,避免过度占用CPU
    }
    
    return NULL;
}

int main() {
    // 注册信号处理
    signal(SIGINT, signal_handler);
    
    printf("RK3588 串口通信程序\n");
    printf("使用串口: %s\n", SERIAL_PORT);
    
    // 打开串口
    serial_fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_fd == -1) {
        perror("打开串口失败");
        fprintf(stderr, "请确保:\n");
        fprintf(stderr, "1. 串口设备 %s 存在\n", SERIAL_PORT);
        fprintf(stderr, "2. 您有访问权限 (尝试: sudo chmod 666 %s 或加入 dialout 组)\n", SERIAL_PORT);
        return EXIT_FAILURE;
    }
    
    // 配置串口
    if (configure_serial(serial_fd)) {
        close(serial_fd);
        return EXIT_FAILURE;
    }
    
    printf("串口配置成功: 波特率 %d, 8N1\n", BAUD_RATE);
    
    // 创建接收线程
    pthread_t rx_thread;
    if (pthread_create(&rx_thread, NULL, receive_thread, NULL)) {
        perror("创建接收线程失败");
        close(serial_fd);
        return EXIT_FAILURE;
    }
    
    // 主循环 - 发送数据
    printf("输入要发送的数据 (输入 'exit' 退出程序):\n");
    
    char input[BUFFER_SIZE];
    while (running) {
        printf("发送 > ");
        fflush(stdout);
        
        if (fgets(input, sizeof(input), stdin) ){
            // 移除换行符
            input[strcspn(input, "\n")] = '\0';
            
            // 检查退出命令
            if (strcmp(input, "exit") == 0) {
                running = 0;
                break;
            }
            
            // 发送数据
            ssize_t n = write(serial_fd, input, strlen(input));
            if (n < 0) {
                perror("写入串口失败");
            } else {
                printf("已发送 [%ld字节]: %s\n", n, input);
            }
        }
    }
    
    // 清理资源
    printf("关闭串口...\n");
    running = 0;
    pthread_join(rx_thread, NULL);
    close(serial_fd);
    
    printf("程序已退出\n");
    return EXIT_SUCCESS;
}

代码4 使用/dev/ttyS4和/dev/ttyS7串口互相连接,循环发送数据,且循环接受数据(采用多线程实现)***/


```c
#include <stdio.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>

// 串口配置
#define UART4_DEVICE "/dev/ttyS4"  // UART4设备文件 
#define UART7_DEVICE "/dev/ttyS7"  // UART7设备文件
#define BAUD_RATE B115200          // 波特率115200
#define BUFFER_SIZE 256            // 缓冲区大小

volatile int running = 1;           // 程序运行标志
int uart4_fd = -1;                  // UART4文件描述符
int uart7_fd = -1;                  // UART7文件描述符

// 信号处理函数
void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n程序终止中...\n");
        running = 0;
    }
}

// 配置串口参数
int configure_serial(int fd) {
    struct termios options;
    
    // 获取当前串口设置
    if (tcgetattr(fd, &options)) {
        perror("tcgetattr失败");
        return -1;
    }
    
    // 设置波特率
    cfsetispeed(&options, BAUD_RATE);
    cfsetospeed(&options, BAUD_RATE);
    
    // 8位数据位,无校验,1位停止位
    options.c_cflag &= ~PARENB;   // 无奇偶校验
    options.c_cflag &= ~CSTOPB;   // 1位停止位
    options.c_cflag &= ~CSIZE;    // 清除数据位掩码
    options.c_cflag |= CS8;       // 8位数据位
    
    // 禁用硬件流控
    options.c_cflag &= ~CRTSCTS;
    
    // 启用接收器,忽略调制解调器控制线
    options.c_cflag |= (CLOCAL | CREAD);
    
    // 原始输入模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    
    // 禁用软件流控
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    
    // 原始输出模式
    options.c_oflag &= ~OPOST;
    
    // 设置超时和最小读取字符数
    options.c_cc[VMIN] = 0;      // 非阻塞读取
    options.c_cc[VTIME] = 10;    // 1秒超时 (单位: 0.1秒)
    
    // 应用设置
    if (tcsetattr(fd, TCSANOW, &options)) {
        perror("tcsetattr失败");
        return -1;
    }
    
    // 清空输入输出缓冲区
    tcflush(fd, TCIOFLUSH);
    
    return 0;
}

// UART4 -> UART7 转发线程
void *uart4_to_uart7_thread(void *arg) {
    char buffer[BUFFER_SIZE];
    printf("UART4->UART7 转发线程已启动\n");

    while (running) {
        ssize_t n = read(uart4_fd, buffer, sizeof(buffer) - 1);
        
        if (n > 0) {
            buffer[n] = '\0';
            printf("UART4 接收 [%ld字节]: %s\n", n, buffer);
            
            // 转发到UART7
            ssize_t written = write(uart7_fd, buffer, n);
            if (written < 0) {
                perror("写入UART7失败");
            } else if (written != n) {
                printf("警告: 部分数据未发送 (期望: %ld, 实际: %ld)\n", n, written);
            } else {
                printf("UART4->UART7 转发 %ld字节\n", written);
            }
        } else if (n < 0 && errno != EAGAIN) {
            perror("读取UART4失败");
            break;
        }
        
        usleep(10000); // 10ms延时
    }
    
    return NULL;
}

// UART7 -> UART4 转发线程
void *uart7_to_uart4_thread(void *arg) {
    char buffer[BUFFER_SIZE];
    printf("UART7->UART4 转发线程已启动\n");
    
    while (running) {
        ssize_t n = read(uart7_fd, buffer, sizeof(buffer) - 1);
        
        if (n > 0) {
            buffer[n] = '\0';
            printf("UART7 接收 [%ld字节]: %s\n", n, buffer);
            
            // 转发到UART4
            ssize_t written = write(uart4_fd, buffer, n);
            if (written < 0) {
                perror("写入UART4失败");
            } else if (written != n) {
                printf("警告: 部分数据未发送 (期望: %ld, 实际: %ld)\n", n, written);
            } else {
                printf("UART7->UART4 转发 %ld字节\n", written);
            }
        } else if (n < 0 && errno != EAGAIN) {
            perror("读取UART7失败");
            break;
        }
        
        usleep(10000); // 10ms延时
    }
    
    return NULL;
}

// 测试数据发送线程
void *test_data_thread(void *arg) {
    int counter = 0;
    printf("测试数据发送线程已启动\n");
    
    while (running) {
        char message[100];
        int len = snprintf(message, sizeof(message), 
                          "测试消息 #%d 来自UART4 [时间: %ld]", 
                          ++counter, time(NULL));
        
        // 发送到UART4
        ssize_t n = write(uart4_fd, message, len);
        if (n < 0) {
            perror("测试数据写入UART4失败");
        } else {
            printf("已发送测试数据到UART4: %s\n", message);
        }
        
        sleep(2); // 每2秒发送一次
    }
    
    return NULL;
}

int main() {
    // 注册信号处理
    signal(SIGINT, signal_handler);
    
    printf("RK3588 双串口互连通信程序\n");
    printf("UART4: %s\n", UART4_DEVICE);
    printf("UART7: %s\n", UART7_DEVICE);
    
    // 打开UART4
    uart4_fd = open(UART4_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
    if (uart4_fd == -1) {
        perror("打开UART4失败");
        fprintf(stderr, "请检查设备是否存在: ls %s\n", UART4_DEVICE);
        fprintf(stderr, "权限问题: sudo chmod 666 %s\n", UART4_DEVICE);
        return EXIT_FAILURE;
    }
    
    // 打开UART7
    uart7_fd = open(UART7_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
    if (uart7_fd == -1) {
        perror("打开UART7失败");
        close(uart4_fd);
        return EXIT_FAILURE;
    }
    
    // 配置串口
    if (configure_serial(uart4_fd)) {
        close(uart4_fd);
        close(uart7_fd);
        return EXIT_FAILURE;
    }
    
    if (configure_serial(uart7_fd)) {
        close(uart4_fd);
        close(uart7_fd);
        return EXIT_FAILURE;
    }
    
    printf("串口配置成功: 波特率 %d, 8N1\n", BAUD_RATE);
    
    // 创建转发线程
    pthread_t thread1, thread2, test_thread;
    
    if (pthread_create(&thread1, NULL, uart4_to_uart7_thread, NULL)) {
        perror("创建UART4->UART7线程失败");
        close(uart4_fd);
        close(uart7_fd);
        return EXIT_FAILURE;
    }
    
    if (pthread_create(&thread2, NULL, uart7_to_uart4_thread, NULL)) {
        perror("创建UART7->UART4线程失败");
        running = 0;
        pthread_join(thread1, NULL);
        close(uart4_fd);
        close(uart7_fd);
        return EXIT_FAILURE;
    }
    
    // 创建测试数据发送线程(可选)
    if (pthread_create(&test_thread, NULL, test_data_thread, NULL)) {
        perror("创建测试线程失败");
        // 继续运行,只是没有测试数据
    }
    
    printf("双串口互连已启动,按Ctrl+C退出\n");
    
    // 主线程等待
    while (running) {
        sleep(1);
    }
    
    // 清理资源
    printf("关闭串口...\n");
    running = 0;
    
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(test_thread, NULL);
    
    close(uart4_fd);
    close(uart7_fd);
    
    printf("程序已退出\n");
    return EXIT_SUCCESS;
}

编译

采用gcc编译

gcc main.c -o uartDeme -pthread

上述代码中一定要修改成当前设备系统对应挂载的串口设备名号,查看linux系统挂着设备的方法。

cd /dev/
ls

或者

ls  /dev/  
 ls  /dev/ | grep tty  #可以通过指令查看 系统挂在的串口设备名

网口

代码一

main函数中包含服务器和客户端功能,启动程序时参数分辨启动客户端还是服务器功能。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <signal.h>

#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CLIENTS 10

volatile int running = 1;

// 信号处理函数
void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n程序终止中...\n");
        running = 0;
    }
}

// 获取本机IP地址
void get_local_ip(char *interface, char *ip_buffer) {
    int fd;
    struct ifreq ifr;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 指定网络接口
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, interface, IFNAMSIZ-1);
    
    ioctl(fd, SIOCGIFADDR, &ifr);
    close(fd);
    
    // 将IP地址转换为字符串
    struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
    inet_ntop(AF_INET, &ipaddr->sin_addr, ip_buffer, INET_ADDRSTRLEN);
}

// TCP服务器
void *tcp_server(void *arg) {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    
    // 创建TCP socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("TCP socket创建失败");
        return NULL;
    }
    
    // 设置socket选项
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt失败");
        return NULL;
    }
    
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    
    // 绑定socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("TCP绑定失败");
        return NULL;
    }
    
    // 监听连接
    if (listen(server_fd, MAX_CLIENTS) < 0) {
        perror("TCP监听失败");
        return NULL;
    }
    
    char ip_address[INET_ADDRSTRLEN];
    get_local_ip("eth0", ip_address);
    printf("TCP服务器启动: %s:%d\n", ip_address, PORT);
    
    while (running) {
        // 接受新连接
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            if (running) perror("TCP接受连接失败");
            continue;
        }
        
        // 显示客户端信息
        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &address.sin_addr, client_ip, INET_ADDRSTRLEN);
        printf("客户端连接: %s\n", client_ip);
        
        // 读取客户端数据
        ssize_t valread = read(new_socket, buffer, BUFFER_SIZE);
        if (valread > 0) {
            printf("收到消息: %s\n", buffer);
            
            // 发送响应
            const char *response = "来自server的响应";
            send(new_socket, response, strlen(response), 0);
        }
        
        close(new_socket);
    }
    
    close(server_fd);
    return NULL;
}

// TCP客户端
void *tcp_client(void *arg) {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("TCP客户端socket创建失败");
        return NULL;
    }
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    
    // 转换IP地址
    if (inet_pton(AF_INET, "192.168.1.10", &serv_addr.sin_addr) <= 0) {
        perror("IP地址无效");
        return NULL;
    }
    
    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("TCP连接失败");
        return NULL;
    }
    
    // 发送消息
    const char *message = "来自client的问候";
    send(sock, message, strlen(message), 0);
    printf("TCP消息已发送\n");
    
    // 读取响应
    ssize_t valread = read(sock, buffer, BUFFER_SIZE);
    if (valread > 0) {
        printf("服务器响应: %s\n", buffer);
    }
    
    close(sock);
    return NULL;
}

// UDP服务器
void *udp_server(void *arg) {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;
    
    // 创建UDP socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("UDP socket创建失败");
        return NULL;
    }
    
    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));
    
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);
    
    // 绑定socket
    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("UDP绑定失败");
        return NULL;
    }
    
    char ip_address[INET_ADDRSTRLEN];
    get_local_ip("eth0", ip_address);
    printf("UDP服务器启动: %s:%d\n", ip_address, PORT);
    
    while (running) {
        socklen_t len = sizeof(cliaddr);
        char buffer[BUFFER_SIZE] = {0};
        
        // 接收数据
        ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, 
                            MSG_WAITALL, (struct sockaddr *) &cliaddr, &len);
        if (n > 0) {
            buffer[n] = '\0';
            
            // 显示客户端信息
            char client_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &cliaddr.sin_addr, client_ip, INET_ADDRSTRLEN);
            printf("收到来自client( %s )的UDP消息: %s\n", client_ip, buffer);
            
            // 发送响应
            const char *response = "server的UDP响应";
            sendto(sockfd, response, strlen(response), 
                  MSG_CONFIRM, (const struct sockaddr *) &cliaddr, len);
        }
    }
    
    close(sockfd);
    return NULL;
}

// UDP客户端
void *udp_client(void *arg) {
    int sockfd;
    struct sockaddr_in servaddr;
    
    // 创建UDP socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("UDP客户端socket创建失败");
        return NULL;
    }
    
    memset(&servaddr, 0, sizeof(servaddr));
    
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    
    // 设置目标服务器地址
    if (inet_pton(AF_INET, "192.168.1.10", &servaddr.sin_addr) <= 0) {
        perror("目标IP地址无效");
        return NULL;
    }
    
    // 发送消息
    const char *message = "来自client的UDP问候";
    sendto(sockfd, message, strlen(message), 
          MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
    printf("UDP消息已发送\n");
    
    // 接收响应
    char buffer[BUFFER_SIZE] = {0};
    socklen_t len = sizeof(servaddr);
    ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 
                       MSG_WAITALL, (struct sockaddr *) &servaddr, &len);
    if (n > 0) {
        buffer[n] = '\0';
        printf("服务器响应: %s\n", buffer);
    }
    
    close(sockfd);
    return NULL;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("用法: %s [模式]\n", argv[0]);
        printf("模式:\n");
        printf("  server    - 启动TCP和UDP服务器\n");
        printf("  client    - 启动TCP和UDP客户端\n");
        printf("  tcp-s     - 只启动TCP服务器\n");
        printf("  tcp-c     - 只启动TCP客户端\n");
        printf("  udp-s     - 只启动UDP服务器\n");
        printf("  udp-c     - 只启动UDP客户端\n");
        return EXIT_FAILURE;
    }
    
    // 注册信号处理
    signal(SIGINT, signal_handler);
    
    // 显示网络接口信息
    printf("RK3588 网络通信程序\n");
    printf("--------------------------------\n");
    
    char ip_eth0[INET_ADDRSTRLEN], ip_eth1[INET_ADDRSTRLEN];
    get_local_ip("eth0", ip_eth0);
    get_local_ip("eth1", ip_eth1);
    
    printf("网络接口信息:\n");
    printf("  eth0 (千兆网口): %s\n", ip_eth0);
    printf("  eth1 (2.5G网口): %s\n", ip_eth1);
    printf("--------------------------------\n\n");
    
    pthread_t tcp_server_thread, tcp_client_thread;
    pthread_t udp_server_thread, udp_client_thread;
    
    if (strcmp(argv[1], "server") == 0) {
        printf("启动TCP和UDP服务器...\n");
        pthread_create(&tcp_server_thread, NULL, tcp_server, NULL);
        pthread_create(&udp_server_thread, NULL, udp_server, NULL);
        
        pthread_join(tcp_server_thread, NULL);
        pthread_join(udp_server_thread, NULL);
    }
    else if (strcmp(argv[1], "client") == 0) {
        printf("启动TCP和UDP客户端...\n");
        pthread_create(&tcp_client_thread, NULL, tcp_client, NULL);
        pthread_create(&udp_client_thread, NULL, udp_client, NULL);
        
        pthread_join(tcp_client_thread, NULL);
        pthread_join(udp_client_thread, NULL);
    }
    else if (strcmp(argv[1], "tcp-s") == 0) {
        printf("启动TCP服务器...\n");
        tcp_server(NULL);
    }
    else if (strcmp(argv[1], "tcp-c") == 0) {
        printf("启动TCP客户端...\n");
        tcp_client(NULL);
    }
    else if (strcmp(argv[1], "udp-s") == 0) {
        printf("启动UDP服务器...\n");
        udp_server(NULL);
    }
    else if (strcmp(argv[1], "udp-c") == 0) {
        printf("启动UDP客户端...\n");
        udp_client(NULL);
    }
    else {
        printf("未知模式: %s\n", argv[1]);
        return EXIT_FAILURE;
    }
    
    printf("程序已退出\n");
    return EXIT_SUCCESS;
}

编译

gcc main.c -o network -lpthread

运行

#在RK3588上一个命令行窗口运行,
./network server

#在RK3588上另一个命令行窗口运行 或在其他设备上运行  (目标服务器IP需修改)
./network client

如出现运行ip不对,是必须要在源码中修改为正确的ip地址;以及端口被占用时,需要杀死占用端口的进程(要有意识的判断这个进程是否可以被杀死,不会导致系统崩溃)。

#查看网络接口
ip addr

#配置eth0 (千兆网口)
sudo ip link set eth0 up
sudo ip addr add 192.168.1.10/24 dev eth0

#配置eth1 (2.5G网口)
sudo ip link set eth1 up
sudo ip addr add 192.168.2.10/24 dev eth1
#查看进程号
    ps -ef | grep network
#查看8080端口占用情况
    sudo netstat -tulnp | grep 8080
#使用kill指令杀死对应进程
    kill -9 pid

代码二

客户端和服务器功能分开实现。

服务器端代码为:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>

#define PORT 8080
#define BUFFER_SIZE 1024
#define MAX_CLIENTS 10

volatile int running = 1;

// 信号处理函数
void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n服务器终止中...\n");
        running = 0;
        exit(0);
    }
}

// 客户端处理线程
void *client_handler(void *arg) {
    int client_socket = *((int *)arg);
    free(arg); // 释放内存
    
    char buffer[BUFFER_SIZE];
    int round = 1;
    
    printf("客户端 %d 已连接\n", client_socket);
    
    // 发送欢迎消息
    const char *welcome_msg = "欢迎连接到TCP服务器!";
    send(client_socket, welcome_msg, strlen(welcome_msg), 0);
    
    while (running) {
        // 接收客户端数据
        memset(buffer, 0, BUFFER_SIZE);
        ssize_t bytes_received = recv(client_socket, buffer, BUFFER_SIZE - 1, 0);
        
        if (bytes_received <= 0) {
            if (bytes_received == 0) {
                printf("客户端 %d 断开连接\n", client_socket);
            } else {
                perror("接收错误");
            }
            break;
        }
        
        buffer[bytes_received] = '\0';
        printf("[客户端 %d] 第%d轮接收: %s\n", client_socket, round, buffer);
        
        // 检查退出命令
        if (strncmp(buffer, "exit", 4) == 0) {
            printf("客户端 %d 请求退出\n", client_socket);
            break;
        }
        
        // 准备响应数据
        char response[BUFFER_SIZE];
        snprintf(response, BUFFER_SIZE, "服务器响应 (第%d轮): 已接收 %ld 字节数据", round, bytes_received);
        
        // 发送响应
        ssize_t bytes_sent = send(client_socket, response, strlen(response), 0);
        if (bytes_sent <= 0) {
            perror("发送失败");
            break;
        }
        
        printf("[客户端 %d] 第%d轮发送: %ld 字节\n", client_socket, round, bytes_sent);
        
        round++;
    }
    
    close(client_socket);
    printf("客户端 %d 连接已关闭\n", client_socket);
    return NULL;
}

int main() {
    // 注册信号处理
    signal(SIGINT, signal_handler);
    
    int server_fd;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    
    printf("TCP服务器启动中...\n");
    
    // 创建TCP socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { /**AF_INET 代表IPV4 AF_INET6代表IPV6,AF=Address Family***/
        perror("socket创建失败");
        exit(EXIT_FAILURE);
    }
    
    // 设置socket选项
    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);
    
    // 绑定socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("绑定失败");
        exit(EXIT_FAILURE);
    }
    
    // 监听连接
    if (listen(server_fd, MAX_CLIENTS) < 0) {
        perror("监听失败");
        exit(EXIT_FAILURE);
    }
    
    printf("服务器已启动,监听端口 %d\n", PORT);
    printf("按 Ctrl+C 停止服务器\n");
    
    while (running) {
        int *client_socket = malloc(sizeof(int));
        if (!client_socket) {
            perror("内存分配失败");
            continue;
        }
        
        // 接受新连接
        *client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
        if (*client_socket < 0) {
            perror("接受连接失败");
            free(client_socket);
            continue;
        }
        
        // 创建客户端处理线程
        pthread_t thread_id;
        if (pthread_create(&thread_id, NULL, client_handler, (void*)client_socket) != 0) {
            perror("线程创建失败");
            close(*client_socket);
            free(client_socket);
        } else {
            pthread_detach(thread_id); // 分离线程,自动回收资源
        }
    }
    
    close(server_fd);
    printf("服务器已关闭\n");
    return 0;
}

客户端代码为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>

#define SERVER_IP "127.0.0.1"  // 默认本地环回
// #define SERVER_IP "192.168.1.10"  // eth0 的ip地址
#define PORT 8080
#define BUFFER_SIZE 1024

volatile int running = 1;

// 信号处理函数
void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n客户端终止中...\n");
        running = 0;
        exit(0);
    }
}

int main(int argc, char *argv[]) {
    // 注册信号处理
    signal(SIGINT, signal_handler);
    
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    char server_ip[16] = SERVER_IP;
    
    // 解析命令行参数
    if (argc > 1) {
        strncpy(server_ip, argv[1], 15);
        server_ip[15] = '\0';
    }
    
    printf("连接到服务器: %s:%d\n", server_ip, PORT);
    
    // 创建TCP socket
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket创建失败");
        exit(EXIT_FAILURE);
    }
    
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    
    // 转换IP地址
    if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {
        perror("IP地址无效");
        exit(EXIT_FAILURE);
    }
    
    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("连接失败");
        exit(EXIT_FAILURE);
    }
    
    printf("已成功连接到服务器\n");
    
    // 接收欢迎消息
    ssize_t bytes_received = recv(sock, buffer, BUFFER_SIZE - 1, 0);
    if (bytes_received > 0) {
        buffer[bytes_received] = '\0';
        printf("服务器: %s\n", buffer);
    }
    
    int round = 1;
    
    while (running) {
        // 用户输入数据
        printf("输入要发送的数据 (第%d轮, 输入'exit'退出): ", round);
        fflush(stdout);
        
        char input[BUFFER_SIZE];
        if (!fgets(input, BUFFER_SIZE, stdin)) {
            perror("输入错误");
            break;
        }
        
        // 移除换行符
        input[strcspn(input, "\n")] = '\0';
        
        // 检查退出命令
        if (strcmp(input, "exit") == 0) {
            printf("退出程序...\n");
            break;
        }
        
        // 发送数据
        ssize_t bytes_sent = send(sock, input, strlen(input), 0);
        if (bytes_sent <= 0) {
            perror("发送失败");
            break;
        }
        printf("第%d轮发送: %ld 字节\n", round, bytes_sent);
        
        // 接收服务器响应
        memset(buffer, 0, BUFFER_SIZE);
        bytes_received = recv(sock, buffer, BUFFER_SIZE - 1, 0);
        
        if (bytes_received <= 0) {
            if (bytes_received == 0) {
                printf("服务器已断开连接\n");
            } else {
                perror("接收错误");
            }
            break;
        }
        
        buffer[bytes_received] = '\0';
        printf("第%d轮接收: %s\n", round, buffer);
        
        round++;
    }
    
    close(sock);
    printf("连接已关闭\n");
    return 0;
}

编译

gcc tcp_server.c -o tcp_server -lpthread
gcc tcp_client.c -o tcp_client

运行

#在RK3588上一个命令行窗口运行服务端
./tcp_server
#在RK3588上另一个命令行窗口运行 或在其他设备上运行 (源码中药修改成目标服务器的IP)客户端
./tcp_client




#查看进程号
    ps -ef | grep network
#查看8080端口占用情况
    sudo netstat -tulnp | grep 8080
#使用kill指令杀死对应进程
    kill -9 pid

linux操作系统的SIGINT 和 SIGTSTP 信号介绍

在上述代码中有信号处理函数,处理SIGINT信号。

void signal_handler(int sig) {
    if (sig == SIGINT) {
        printf("\n服务器终止中...\n");
        running = 0;
        exit(0);
    }
}

现在简要介绍一下SIGINT和SIGSTP信号。SIGINT 和 SIGTSTP 是 UNIX /类UNIX 系统中的两种不同的信号,它们用于进程间通信和控制。

SIGINT(Signal Interrupt)

含义:中断信号
默认行为:终止进程
通常触发方式:用户在终端按下 Ctrl+C
信号编号:通常为 2
目的:用于请求程序立即终止

SIGTSTP(Signal Terminal Stop)

含义:终端停止信号
默认行为:暂停(挂起)进程
通常触发方式:用户在终端按下 Ctrl+Z
信号编号:通常为 20
目的:用于暂停进程的执行,但不终止进程

信号SIGINT和SIGSTP的区别

行为不同:
    SIGINT 通常会终止进程
    SIGTSTP 会暂停进程,但不终止它
恢复方式:
    SIGINT 终止进程后,无法直接恢复
    SIGTSTP 暂停进程后,可以通过 SIGCONT 信号恢复执行
用途:
    SIGINT 用于中断正在运行的程序
    SIGTSTP 用于暂时停止程序,稍后可以恢复
默认键盘快捷键:
    SIGINT 通常由 Ctrl+C 触发
    SIGTSTP 通常由 Ctrl+Z 触发
程序响应:
    对 SIGINT 的处理通常用于清理资源后优雅地退出
    对 SIGTSTP 的处理可能包括保存状态以便稍后恢复

linux网络编程基础流程

服务端:

    socket   创建套接字
    bind     绑定IP和端口
    listen   监听
    accept   接受客户端连接
    read     读取客户端发送的数据
    send/write    发送数据给客户端
    close    关闭套接字

客户端:

    socket   创建套接字
    connect  连接服务器
    send/write    发送数据给服务器
    read     读取服务器发送的数据
    close    关闭套接字
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值