2016-05-30 46 views
2

我在非阻塞模式下有一个连接的TCP套接字。发送和接收数据的工作正常。接收被实现为专用线程上的循环,因此:select()有时会阻塞超过所提供的超时时间

fd_set fdset; 
FD_ZERO(&fdset); 
FD_SET(internal->m_socket, &fdset); 
struct timeval timeout; 
timeout.tv_sec = timeout_ms/1000; 
timeout.tv_usec = (timeout_ms % 1000) * 1000; 
int ret = select(internal->m_socket + 1, &fdset, nullptr, nullptr, &timeout); 
if (ret == 0) { 
    // handle timeout then loop 
} else if (ret < 0) { 
    // handle error then abort 
} else { 
    ret = recv(...); // Read data available on socket then loop 
} 

TCP连接从运行OS X 10.11.5的MacBookPro转到WiFi。当连接建立时关闭WiFi时,一些后续select()调用可能需要比指定的超时值更长的时间。

这里加入日志调用的证明(INOUT之前分别印刷select()在超时值传递与实际测量的经过时间后):

(WiFi is ON, thread is continuously receiving from socket) 

08:31:15 -------- IN = 15000 
08:31:30 -------- OUT = 14999 
08:31:30 -------- IN = 15000 
08:31:45 -------- OUT = 15000 
08:31:45 -------- IN = 15000 
08:32:00 -------- OUT = 15000 
08:32:00 -------- IN = 15000 
08:32:15 -------- OUT = 15002 
08:32:15 -------- IN = 1000 
08:32:15 -------- OUT = 87 <--- data received 
08:32:15 -------- IN = 4913 
08:32:15 -------- OUT = 0 <--- data received 
08:32:15 -------- IN = 15000 

(WiFi is turned OFF at this point in the middle of a `select()` call) 

08:32:30 -------- OUT = 15004 <--- ok 
08:32:30 -------- IN = 1000 
08:32:31 -------- OUT = 1003 <--- ok 
08:32:31 -------- IN = 1000 
08:32:32 -------- OUT = 1006 <--- ok 
08:32:32 -------- IN = 1000 
08:32:43 -------- OUT = 10999 <--- NOT OK: 10X longer 
08:32:43 -------- IN = 1000 
08:32:48 -------- OUT = 4965 <--- NOT OK: 5X longer 

(truncated) 

注意,没有明显模式:有时候这个错误似乎不会发生,但大多数情况下确实如此。在关闭WiFi之后,它可以是紧接着的select()呼叫,该WiFi需要比指定的超时更长的时间,或者之后的一个或更多时间。

+0

您的选择超时只有一秒?为什么? – EJP

+0

这段代码上面有很多代码,如果接收超时,其他逻辑运行。例如,我默认使用15秒超时,但如果应用程序认为连接不工作,它会向服务器发送一个Ping,然后将超时减少到1秒,等待Pong几秒钟,并终止连接没收到。 – Pol

+1

这可能是App Nap。查看活动监视器的“能量”选项卡是否显示您的应用程序在问题发生时处于暂停状态。 –

回答

0

我假设你有一个专用于运行你的select循环的线程。将线程放入实时调度课程中。以下是苹果公司的示例代码:

#include <mach/mach.h> 
#include <mach/mach_time.h> 
#include <pthread.h> 

void move_pthread_to_realtime_scheduling_class(pthread_t pthread) 
{ 
    mach_timebase_info_data_t timebase_info; 
    mach_timebase_info(&timebase_info); 

    const uint64_t NANOS_PER_MSEC = 1000000ULL; 
    double clock2abs = ((double)timebase_info.denom/(double)timebase_info.numer) * NANOS_PER_MSEC; 

    thread_time_constraint_policy_data_t policy; 
    policy.period  = 0; 
    policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work 
    policy.constraint = (uint32_t)(10 * clock2abs); 
    policy.preemptible = FALSE; 

    int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()), 
        THREAD_TIME_CONSTRAINT_POLICY, 
        (thread_policy_t)&policy, 
        THREAD_TIME_CONSTRAINT_POLICY_COUNT); 
    if (kr != KERN_SUCCESS) { 
     mach_error("thread_policy_set:", kr); 
     exit(1); 
    } 
} 

了解更多Technical Note TN2169: High Precision Timers in iOS/OS X

+1

这会产生什么影响?考虑到这里的粒度是秒,而不是微秒,这似乎是过度的。文档中说:“实时调度类中的线程获得了一流的处理能力,只要需要运行,它们就会排在前面。”我的MacBookPro有4个物理内核,在发生错误时空闲。没有理由我想'select()'会挂起额外的9整秒,因为其他线程会优先。 – Pol

+0

我不知道它是否会有所作为。我认为这是值得一试的,所以我发布了它。如果你尝试了它并且没有帮助,请告诉我,我会提出另一个建议。 –