2015-09-08 17 views
3

我想检查客户端是否发送了一些新的数据。这实际上告诉我,我总是有新的数据:即使没有新的数据,FD_ISSET总是正确的?

bool ClientHandle::hasData() 
{ 
    fd_set temp; 
    FD_ZERO(&temp); 
    FD_SET(m_sock, &temp); 

    //setup the timeout to 1000ms 
    struct timeval tv; 
    tv.tv_sec = 0; 
    tv.tv_usec = 1000; 
    //temp.fd_count possible? 
    if (select(m_sock+1, &temp, nullptr, nullptr, &tv) == -1) 
    { 
     return false; 
    } 

    if (FD_ISSET(m_sock, &temp)) 
     return true; 

    return false; 
} 

我用Java客户端连接,并发送我的构造函数的读取里面“连接”消息:

ClientHandle::ClientHandle(SOCKET s) : m_sock(s) 
{ 
    while (!hasData()) 
    { 
    } 
    char buffer[5]; 
    recv(m_sock, buffer, 4, NULL); 
    auto i = atoi(buffer); 
    LOG_INFO << "Byte to receive: " << i; 
    auto dataBuffer = new char[i + 1]{'\0'}; 
    recv(m_sock, dataBuffer, i, NULL); 
    LOG_INFO << dataBuffer; 
    //clean up 
    delete[] dataBuffer; 
} 

这似乎工作的权利。之后,我继续检查是否有新数据,即使java客户端不发送任何新数据,它总是真实的。

这里是java客户端。不要评价我只是为了检查连接。它不会像这样以char []的形式发送大小信息。

public static void main(String[] args) throws UnknownHostException, 
     IOException { 
    Socket soc = null; 

    soc = new Socket("localhost", 6060); 
    PrintWriter out = new PrintWriter(soc.getOutputStream(), true); 
    BufferedReader in = new BufferedReader(new InputStreamReader(
      soc.getInputStream())); 

    if (soc != null) 
     System.out.println("Connected"); 
    out.write("10\0"); 
    out.flush(); 
    out.write("newCon\0"); 
    out.flush();  
    out.close(); 
    in.close(); 
    soc.close(); 
} 

那么hasData FD_ISSET方法有什么问题?

回答

1

那么hasData FD_ISSET方法有什么问题?

其实没有。使用recv()时出现问题。

recv()将返回0如果客户端断开连接并将返回,直到您close套接字(服务器端)。你可以找到这个信息in the manual。 即使recv()返回0,它也会“触发”select()

知道了,很容易发现问题:您从不检查recv()的返回值,因此您无法说出客户端是否仍处于连接状态。不过,你还是用FD_SET加上吧!

#include <sys/types.h> // for ssize_t 
#include <stdio.h> // for perror() 
ClientHandle::ClientHandle(SOCKET s) : m_sock(s) 
{ 
    while (!hasData()) 
    { 
    } 
    char buffer[5]; 
    ssize_t ret = recv(m_sock, buffer, 4, NULL); 
    if (ret == -1) // error 
    { 
     perror("recv"); 
     return ; 
    } 
    else if (ret == 0) // m_sock disconnects 
    { 
     close(m_sock); 
     // DO NOT FD_SET m_sock since the socket is now closed 
    } 
    else 
    { 
     auto i = atoi(buffer); 
     LOG_INFO << "Byte to receive: " << i; 
     auto dataBuffer = new char[i + 1]{'\0'}; 
     recv(m_sock, dataBuffer, i, NULL); 
     LOG_INFO << dataBuffer; 
     //clean up 
     delete[] dataBuffer; 
    } 
} 
+0

时调用第二个recv,那么数据没有问题,但是当我看到其他答案时,它将在不同条件下返回true。 (不仅仅是它包含我认为的新数据),所以hasdata方法是不正确的。所以我需要补充一点。 – BennX

+0

@BennX'hasData()'几乎是正确的:你不应该在断开的套接字上使用'FD_SET'。 'recv()'返回一个正数,如果它获得数据,则返回'0',如果客户端断开连接,则返回'-1'。我更新了我的答案以添加更多解释。 – nouney

+0

所以要检查我是否有数据,我只需recv并处理它,如果有新的?我想我可以在选择数据之前用select来检查它。 – BennX

1

从史蒂芬的书UNIX网络编程:

套接字是读就绪如有下列四个条件为真:

  • 在插座数据的字节数接收缓冲区大于或等于套接字接收缓冲区的低水位标记的当前大小。对套接字的读操作不会被阻塞,并且会返回一个大于0的值(即准备被读取的数据)。我们可以使用SO_RCVLOWAT套接字选项来设置这个低水位标记。 TCP和UDP套接字的默认值为1。

  • 连接的读取一半被关闭(即,已经接收到FIN的TCP连接)。对套接字的读操作不会被阻塞并返回0(即EOF)。

  • 套接字是一个监听套接字,并且完成的连接数不为零。监听套接字上的accept通常不会被阻塞,尽管我们将在第16.6节中描述accept中可以阻塞的时序条件。

  • 套接字错误未决。对套接字的读取操作不会阻塞,并会在errno设置为特定错误条件时返回错误(-1)。这些挂起的错误也可以通过调用getsockopt并指定SO_ERROR套接字选项来获取和清除。

ISSET将在上述所有情况下返回true。在您的Java客户端关闭连接后,套接字将准备好在服务器中读取。

在ClientHandle :: ClientHandle中,您不检查recv的返回值以及是否返回任何数据。

第二次调用recv时阻塞了吗?

+0

我明白了,所以hasData实际上是不正确的。您是否可以发布检查客户是否有新数据的正确方法?它应该是阻塞,但因为我刷新这些调用它不会阻塞和正常工作。 – BennX

+1

@BennX,只需检查第一个recv的错误或0(零)数据的返回值。如果返回值为0并且读取0数据,则连接关闭。如果返回值是!= 0,请检查错误。只有在第一次成功读取> 0 Bytes – rodolk

1

您不检查返回值recv,并且您不处理接收的字节数少于您的要求。那么当连接关闭时你期望发生什么?

相关问题