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秒内发送到服务器的任何答复的,将被视为连接丢失甚至连接仍然成立。
答
设置超时套接字选项后,你的阅读循环做到这一点:
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()
返回-1
与errno
或EAGAIN
或EWOULDBLOCK
在一些不关闭连接的其他方式。
答
上面代码的问题是,如果客户端在30秒内没有发送任何答复到服务器,即使连接仍然建立,它将被视为连接丢失。
只是因为你试图区分错误条件。
- 如果你读超时,
errno == EAGAIN/EWOULDBLOCK
,连接还活着,你应该采取任何适当的动作来读超时。 - 如果从
recv()
,然后得到任何其他错误,您应该考虑连接断开。 - 如果你从
send()
得到任何错误,你应该也认为连接关闭,你根本没有做,你忽略它。
你的代码的另一个问题是你没有检测到流结束。如果recv()
返回零,则对等体已完全断开,您应关闭套接字并停止读取。
这太宽泛了。请缩小范围。也许显示你的服务器代码,你有麻烦执行此。也可以查看'select()'或'(e)poll()'来检测套接字连接上的读取超时。 –
我已经添加了我的服务器代码 – ZZZ