#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include "m0.h"
#include "camera.h"
#define DATALEN 36
#define BUFFER_SIZE 1024
#define IMAGE_HEADER_SIZE 20
int new_fd; // 全局变量用于线程间共享套接字
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 发送数据线程函数
void* send_thread(void* arg) {
int ret;
char write_buf[60] = {0};
char m0_read_buf[DATALEN] = {0};
float temp = 0;
float humi = 0;
while(1) {
// 加锁保护套接字写操作
pthread_mutex_lock(&mutex);
// 读取传感器数据
m0_read(m0_read_buf);
temp = m0_read_buf[5] + m0_read_buf[4] / 10.0;
humi = m0_read_buf[7] + m0_read_buf[6] / 10.0;
printf("\ntemp: %.2f度\nhumi: %.2f%%\n", temp, humi);
sprintf(write_buf, "temp:%.2f度\nhumi:%.2f%%\n", temp, humi);
//ret = write(new_fd, write_buf, strlen(write_buf));
pthread_mutex_unlock(&mutex);
/*
if(ret <= 0) {
perror("发送失败");
break;
}
*/
// 定时发送(每2秒)
sleep(2);
}
return NULL;
}
// 接收数据线程函数
void* recv_thread(void* arg) {
int ret;
char lead_buf[2] = {0}; // 增加缓冲区大小防止溢出
char m0_send_buf[DATALEN] = {
0x0, 0x1, 0x2, 0x3, 0x4,
0x5, 0x6, 0x7, 0x8
}; // 控制命令
while(1) {
// 加锁保护套接字读操作
pthread_mutex_lock(&mutex);
ret = read(new_fd, lead_buf, sizeof(lead_buf) - 1); // 保留一个字节给结束符
pthread_mutex_unlock(&mutex);
if(ret <= 0) {
perror("接收失败或连接关闭");
break;
}
lead_buf[ret] = '\0'; // 确保字符串终止
printf("接收到命令: %s\n", lead_buf);
int n = atoi(lead_buf);
if(n >= 0 && n <= 8) {
char buf = m0_send_buf[0];
ret = m0_write(buf);
if(ret < 0) {
perror("M0写入失败");
}
} else {
printf("无效命令: %d\n", n);
}
}
return NULL;
}
void* camera_thread()
{
int opt = 1;
char buffer[BUFFER_SIZE] = {0};
Camera_Init();
while (1) {
// 7. 执行摄像头程序捕获图像
printf("正在捕获图像...\n");
capture_frame();
// 8. 读取捕获的图片
FILE *img_file = fopen("captured.jpg", "rb");
if (!img_file) {
perror("打开图片失败");
close(new_fd);
continue;
}
// 获取文件大小
fseek(img_file, 0, SEEK_END);
long img_size = ftell(img_file);
fseek(img_file, 0, SEEK_SET);
// 加锁保护套接字读操作
pthread_mutex_lock(&mutex);
// 9. 发送图片大小头信息
char header[IMAGE_HEADER_SIZE];
snprintf(header, IMAGE_HEADER_SIZE, "%019ld", img_size); // 19位数字+1个结束符
if (send(new_fd, header, IMAGE_HEADER_SIZE, 0) != IMAGE_HEADER_SIZE) {
perror("发送头信息失败");
fclose(img_file);
close(new_fd);
continue;
}
printf("发送图片大小: %ld 字节\n", img_size);
//发送图片数据
size_t total_sent = 0;
while (total_sent < (size_t)img_size) {
size_t bytes_read = fread(buffer, 1, BUFFER_SIZE, img_file);
if (bytes_read <= 0) break;
ssize_t bytes_sent = send(new_fd, buffer, bytes_read, 0);
if (bytes_sent <= 0) {
perror("发送图片失败");
break;
}
total_sent += bytes_sent;
printf("已发送: %zd/%ld 字节 (%.1f%%)\r",
total_sent, img_size, (double)total_sent/img_size*100);
fflush(stdout);
}
pthread_mutex_unlock(&mutex);
printf("\n图片发送完成\n");
fclose(img_file);
}
}
int main(void) {
int fd, ret;
struct sockaddr_in addr;
// 创建套接字
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0) {
perror("创建套接字失败");
return fd;
}
printf("创建套接字成功\n");
// 设置地址重用
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
addr.sin_family = AF_INET;
addr.sin_port = htons(60000);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0) {
perror("绑定失败");
close(fd);
return ret;
}
printf("绑定成功\n");
// 监听
ret = listen(fd, 5);
if(ret < 0) {
perror("监听失败");
close(fd);
return ret;
}
printf("等待客户端连接...\n");
// 接受连接
socklen_t addrlen = sizeof(addr);
new_fd = accept(fd, (struct sockaddr*)NULL, NULL);
if(new_fd < 0) {
perror("接受连接失败");
close(fd);
return new_fd;
}
printf("客户端连接成功\n");
// 创建发送和接收线程
pthread_t send_tid, recv_tid,camera_tid;
if(pthread_create(&send_tid, NULL, send_thread, NULL) != 0) {
perror("发送线程创建失败");
close(new_fd);
close(fd);
return -1;
}
if(pthread_create(&recv_tid, NULL, recv_thread, NULL) != 0) {
perror("接收线程创建失败");
close(new_fd);
close(fd);
return -1;
}
if(pthread_create(&camera_tid, NULL, camera_thread, NULL) != 0) {
perror("摄像线程创建失败");
close(new_fd);
close(fd);
return -1;
}
// 等待线程结束
pthread_join(send_tid, NULL);
pthread_join(camera_tid,NULL);
pthread_join(recv_tid, NULL);
// 清理资源
close(new_fd);
close(fd);
pthread_mutex_destroy(&mutex);
return 0;
}
这是一段TCP服务端的代码,帮我把三个任务改写为互不阻塞,以达到客户端定时更新接收到服务端数据的同时可以即时发送数据给服务端,还要整个服务端函数不会阻塞在read接收客户端数据的时候,即类似单片机中断的原理,同时在send_thread()和camera_thread()中增加帧头,以达到发送给数据时客户端可以分辨是哪种数据