C++ TCP检测客户端是否已断开连接数秒

问题描述:

在C++中,如何检测客户端的连接是否丢失了几秒钟,比如说30秒。即使客户端不响应超过30秒(例如,服务器正在等待客户端回复),只要连接仍然建立,它就不会丢失连接。C++ TCP检测客户端是否已断开连接数秒

#include <string.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <iostream> 
#include <fstream> 
#include <strings.h> 
#include <stdlib.h> 
#include <string> 
#include <pthread.h> 
#include <unistd.h> 

#include <vector> 
#include <thread> 

using namespace std; 

static int connFd; 
void error(const char *msg){ 
    perror(msg); 
    exit(1); 
} 

void task1 (int connFd){ 
    //CEstablish timeut connection for client 
    struct timeval timeout;  
    timeout.tv_sec = 30; 
    timeout.tv_usec = 0; 

    if (setsockopt (connFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, 
       sizeof(timeout)) < 0) 
     error("setsockopt failed\n"); 

    if (setsockopt (connFd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, 
       sizeof(timeout)) < 0) 
     error("setsockopt failed\n"); 


    cout << "Thread No: " << pthread_self() << endl; 
    char test[256]; 
    bzero(test, 256); 
    bool loop = false; 
    int n = write(connFd,"I got your message",18); 
    if (n < 0) error("ERROR writing to socket"); 
    while(!loop){  
     bzero(test, 256);  
     int n = read(connFd, test, 255); 
     if (n < 0) { 
     //error("ERROR reading from socket"); 

     cout << " ERROR " << endl; 
     break; 
     } 
     printf("Here is the message: %s\n",test); 
     //n = write(connFd,"I got your message",18); 
     //if (n < 0) error("ERROR end to socket"); 
     //string tester (test); 
     //cout << tester << endl;  
     //if(tester == "exit") 
      //break; 
    } 
    cout << "\nClosing thread and conn" << endl; 
    close(connFd); 
} 

int main(int argc, char* argv[]){ 
    int pId, portNo, listenFd; 
    socklen_t len; //store size of the address 
    bool loop = false; 
    struct sockaddr_in svrAdd, clntAdd; 

    pthread_t threadA[3]; 
    std::vector<std::thread> threads; 
    if (argc < 2) 
    { 
     cerr << "Syntam : ./server <port>" << endl; 
     return 0; 
    } 

    portNo = atoi(argv[1]); 

    if((portNo > 65535) || (portNo < 2000)) 
    { 
     cerr << "Please enter a port number between 2000 - 65535" << endl; 
     return 0; 
    } 

    //create socket 
    listenFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 

    if(listenFd < 0) 
    { 
     cerr << "Cannot open socket" << endl; 
     return 0; 
    } 

    bzero((char*) &svrAdd, sizeof(svrAdd)); 

    svrAdd.sin_family = AF_INET; 
    svrAdd.sin_addr.s_addr = INADDR_ANY; 
    svrAdd.sin_port = htons(portNo); 

    //bind socket 
    if(bind(listenFd, (struct sockaddr *)&svrAdd, sizeof(svrAdd)) < 0) 
    { 
     cerr << "Cannot bind" << endl; 
     return 0; 
    } 

    listen(listenFd, 5); 

    int noThread = 0; 

    while (noThread < 3) 
    { 
     socklen_t len = sizeof(clntAdd); 
     cout << "Listening" << endl; 

     //this is where client connects. svr will hang in this mode  until client conn 
     connFd = accept(listenFd, (struct sockaddr *)&clntAdd, &len); 

     if (connFd < 0) { 
      cerr << "Cannot accept connection" << endl; 
      return 0; 
     } 
     else { 
      cout << "Connection successful" << endl; 
     } 
     ///thread t(&task1,connFd); 
     threads.push_back(std::thread(task1,connFd)); 
     //pthread_create(&threadA[noThread], NULL, task1, NULL); 
     noThread++; 
    } 

    for(auto && t : threads) 
     t.join(); 

    /*for(int i = 0; i < 3; i++) 
    { 
     pthread_join(threadA[i], NULL); 
    }*/ 


} 

上面代码的问题是,如果客户没有在30秒内发送到服务器的任何答复的,将被视为连接丢失甚至连接仍然成立。

+1

这太宽泛了。请缩小范围。也许显示你的服务器代码,你有麻烦执行此。也可以查看'select()'或'(e)poll()'来检测套接字连接上的读取超时。 –

+0

我已经添加了我的服务器代码 – ZZZ

设置超时套接字选项后,你的阅读循环做到这一点:

while(!loop){  
    bzero(test, 256);  
    int n = read(connFd, test, 255); 
    if (n < 0) { 
    cout << " ERROR " << endl; 
    break; 
    } 
    printf("Here is the message: %s\n",test); 
} 
cout << "\nClosing thread and conn" << endl; 
close(connFd); 

现在,超时read()将返回-1,让你打破和close(connFd);。这解释了你描述的问题:

上述代码的问题是,如果客户端在30秒内未向服务器发送任何答复,则即使连接仍然建立,它将被视为连接丢失。

这不是关闭连接的套接字库 - 它是您的close调用。相反,处理read()返回-1errnoEAGAINEWOULDBLOCK在一些不关闭连接的其他方式。

上面代码的问题是,如果客户端在30秒内没有发送任何答复到服务器,即使连接仍然建立,它将被视为连接丢失。

只是因为你试图区分错误条件。

  • 如果你读超时,errno == EAGAIN/EWOULDBLOCK,连接还活着,你应该采取任何适当的动作来读超时。
  • 如果从recv(),然后得到任何其他错误,您应该考虑连接断开。
  • 如果你从send()得到任何错误,你应该认为连接关闭,你根本没有做,你忽略它。

你的代码的另一个问题是你没有检测到流结束。如果recv()返回零,则对等体已完全断开,您应关闭套接字并停止读取。