【问题标题】:How can I make the program read sensors every 10 milliseconds while the tcp server is waiting for data在 tcp 服务器等待数据时,如何让程序每 10 毫秒读取一次传感器
【发布时间】: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


【解决方案1】:

您可以使用selectpoll 等待数据到达,并设置超时时间。

例如,如果您需要在 5 毫秒后读取传感器,您可以调用 pollselect 并设置 5 毫秒超时。如果套接字接收到数据,它会立即返回并告诉你。否则会在 5 毫秒后返回,你就知道没有数据了。

poll 为例:

struct pollfd pollfd = {.fd = socket_fd, .events = POLLIN};
int err = poll(&pollfd, 1, milliseconds_timeout);
// error check here; if err is negative then the error code is in errno
if (pollfd.revents & POLLIN) {
    // Socket received data
}
// Otherwise it didn't.
// Note that if data was received, it might not be time to read the sensors yet.
// You should keep poll()ing until it's the right time to read the sensors, using clock_gettime to check the time.

【讨论】:

  • 嗨 immibis,快速提问 - 如果套接字在 5 毫秒前获取数据,poll() 会继续直到达到 5 毫秒还是开始新的计数?
  • 都不返回。如果需要更精确的超时,也可以使用 ppoll。
猜你喜欢
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 2018-01-02
  • 1970-01-01
相关资源
最近更新 更多