【发布时间】:2019-09-25 14:17:39
【问题描述】:
我的程序需要每 10 毫秒读取一次传感器数据,并通过 TCP 套接字将其发送给客户端。该程序是用 C 语言编写的。 tcp 服务器和传感器读取代码在同一个 c 文件中。服务器不会定期发送数据。相反,它等待来自客户端的数据请求(每 50 毫秒)。一旦服务器收到请求,它就会向客户端发送数据。
我的问题是我需要每 10 毫秒读取一次传感器并将其保存在缓冲区中。服务器收到请求后,将缓冲区中的传感器数据发送给客户端。但是当tcp server在等待请求的时候,整个程序卡在了read函数:
n = read(newsockfd,buffer,31)
那么,我无法读取传感器。
我尝试使用线程来读取传感器和 tcp 服务器。它适用于读取传感器。但是,如果我在 tcp 服务器线程中添加任何 usleep(),例如 5 毫秒,则在向客户端发送数据之前将需要 5 毫秒。
我的程序需要 tcp 服务器在收到请求后立即向客户端发送数据。同时,每 10 毫秒读取一次传感器。谁有更好的解决方案?
tcp server和sensor读取的代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#define MAX_SIZE 256
#define SAMPLE_RATE
void read_sensor(void) {
printf("read sensors\n");
}
int main() {
int sockfd, newsockfd, portno = 10000, clilen;
char buffer[MAX_SIZE];
struct sockaddr_in serv_addr, cli_addr;
int n;
int data;
printf( "using port #%d\n", portno );
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
printf("ERROR opening socket\n");
bzero((char *) &serv_addr, sizeof(serv_addr));
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)
error( const_cast<char *>( "ERROR on binding" ) );
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
printf( "waiting for new client...\n" );
if ( ( newsockfd = accept( sockfd, (struct sockaddr *) &cli_addr, (socklen_t*) &clilen) ) < 0 )
printf("ERROR on accept");
printf( "opened new communication with client\n" );
clock_gettime(CLOCK_REALTIME, &gettime_now);
start_time = gettime_now.tv_nsec; //Get nS value
while (1) {
int n = 0;
if ( (n = read(newsockfd,buffer,31) ) < 0 ) //read data
printf("ERROR reading from socket");
buffer[n] = '\0';
if (n > 0)
printf("%s\n", buffer);
else {
break;
}
if ( (n = write( newsockfd, buffer, strlen(buffer) ) ) < 0 ) //send data
printf( "ERROR writing to socket\n");
buffer[n] = '\0';
}
close( newsockfd );
}
return 0;
}
【问题讨论】:
-
允许每个线程相互独立运行。传感器读取线程不需要与客户端请求线程交互。
-
您确定它需要“立即”进行传输吗?因为对于人眼来说,长达 100 毫秒的一切都可以被认为是“即时的”。
while(1)是一个不好的做法,做一个明显的退出条件,覆盖更高级别实用程序中的所有丑陋,以便问题“弹出”。一般可以使用select()之类的函数来查询是否有数据可用。在我最近的应用程序中,我实际上使用了 get_available_bytes 之类的函数。仔细考虑您的网络架构。 -
请注意,糟糕的网络连接可能会导致往返时间超过 10 毫秒。您真的需要传送所有个传感器数据包吗?如果有些被丢弃、延迟或乱序(并且可以被接收者丢弃),是否可以?然后考虑使用 UDP 而不是 TCP,因为这样您就可以发送传感器数据包而不必等待回复。
-
write( newsockfd, buffer, strlen(buffer) )也有问题,因为 TCP 连接是流,没有保留数据包边界。如果您每 10 毫秒发送一次,并且在连接上发生了一些事情,则数据包可能会在接收器读取它们之前被合并,并且来自多个数据包的数据合并到一个read()中。如果没有坏事发生,可以合并数据包。并且字符串也可以拆分成多个数据包。 -
一般来说,一些最佳实践是永远不要发送比接收更频繁,始终检查发送和接收值,始终接收固定大小的数据包,告诉您至少两件事 ID 和数据大小期望并且可能使用以可预测方式调度发送者数据的队列。当有可用数据时,您可以在线程或调用 async recv 命令的线程中阻止接收(注意超时)(参见例如
select())
标签: c multithreading sockets tcp