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

串口
代码
代码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 关闭套接字
233

被折叠的 条评论
为什么被折叠?



