socket编程:客户端掉线,如何让服务端知道?

时间:2021-08-19 22:16:18
长连接,服务端只允许有一个TCP连接

为了防止断网,我在客户端加了重连机制,但是每当客户端断网时,服务端仍保持着连接
导致客户端重连时,服务端会提示"连接已存在",只有重启服务端才能重连成功

关键是,服务端还不能更改

请教各位高手,在这种情况下,客户端有什么办法能通知服务端网络已断开啊????

52 个解决方案

#1


本帖最后由 VisualEleven 于 2011-01-25 09:09:34 编辑
if(connect(my_socket,(SOCKADDR *)&target,sizeof(target))==SOCKET_ERROR )
{
 AfxMessageBox("服务器未启动");
 WSACleanup();
return 0;
}

#2


心跳检测
当服务端检测到连接已经断开后,关闭句柄就可以再次连接了

#3


不能通过客户端解决
通常的做法就是服务器发送心跳包

#4


WSAAsyncSelect()
FD_CLOSE 通知码

#5


客户端发送心跳待服务端确认,增加这么一个过程

#6


客户端发送心跳包,服务器用定时器判断客户端多少时间没有数据,没数据则断开

#7


服务端需要有心跳机制等来维护连接,当客户端断开就有超时,服务端来判断连接断开

#8




KeepAlive机制

#9


上面都说了,利用心跳机制来检测

#10


通过心跳消息检测

#11


服务端还不能更改不好解决。除非客户端能用raw发close(fd)的包到服务器。不过应该比较难,想想而已。

#12


心跳包超时检测

#13


都断网了还能通知各屁啊

#14


如果不修改服务端是很难做到的。心跳机制需要S/C双方协调控制。

#15


如果不修改服务端是很难做到的。心跳机制需要S/C双方协调控制。

#16


WSAAsyncSelect()
FD_CLOSE 通知码

这是很简单的方法,但是缺点就是在断线后差不多要10秒左右才能收到,不能及时

#17


添加心跳消息。

#18


如果是tcp的,recv()返回0时就说明连接关闭了,当然这是正常关闭,最好还是用心跳

#19


该回复于2011-01-26 17:25:46被版主删除

#20


如果服务断开了,客户端的recv会返回SOCKET_ERROR

#21


服务端你不能更改你怎么改客户端?

#22


心跳最准确 其他的方法难免会有些附加条件(如正常关闭)

#23


哈哈,心跳是王道!

#24


假如你客户端一直发送消息到服务器端的话,还有一招简单的。
就是在服务器端定义个int变量,设定个计时器,没有消息就+1,
来了消息就把变量清0,假如好比变量>10时还没有消息,那就删除socket,重新创建监听socket

#25


服务器端不能改,水平有限。算我没说

#26


最好用心跳机制

#27


从服务端角度看可以在应用层增加心跳机制,或者用keepalive检测。
服务端无法修改,那还真没有什么好的办法。
如果楼主的这种情况时常发生,那要从网络上看看是否存在什么问题。

#28


我是从来不用心跳,影响效率,起一个线程去select就行了

int select(
  __in          int nfds,
  __in_out      fd_set* readfds,
  __in_out      fd_set* writefds,
  __in_out      fd_set* exceptfds,
  __in          const struct timeval* timeout
);


第4个参数exceptfds中就是所有异常的socket,当然也包括连接已经断开的。无论是异常还是连接断开,都需要重连。
用心跳的话,假如有1000个客户端连着服务器,那么光处理心跳包就要耗费多少资源?光看题目,没打开帖子之前,我就猜到80%的人会回答心跳,结果和预想的一样。其实很多人都没有实践过,只是人云亦云

#29


楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。

#30


引用 29 楼 aaadddzxc 的回复:
楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。


没有谁规定select只能用于模型中,把它作为一个api来使用就行了,任何时候都可以用

#31


引用 13 楼 covsno 的回复:
都断网了还能通知各屁啊

说有对`服务器必须主动去测它才能知道
而不是客户端掉了后才去通知
掉了就通知不了了`
心跳机制还是蛮好的
你可以定时发送包要是收不到就说明掉了
也可以测本地网络当网速差时发个包说明网络不好也行
要是客户拔线了`必需得用服务器去测

#32


任何连接都需要增加心跳机制。

#33


对socket启用keepalive机制,把心跳包机制交给系统来处理

void TcpConnection::SetKeepAlive(bool bOnOff, DWORD nKeepAliveTime, DWORD nKeepAliveInterval)
{
DWORD dwError = 0L;
DWORD dwBytes = 0L;
tcp_keepalive sKA_Settings = {0}, sReturned = {0} ;
sKA_Settings.onoff = bOnOff;
sKA_Settings.keepalivetime = nKeepAliveTime;  sKA_Settings.keepaliveinterval = nKeepAliveInterval; 
if (WSAIoctl(m_Socket, SIO_KEEPALIVE_VALS, &sKA_Settings,
sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes,
NULL, NULL) != 0)
{
dwError = WSAGetLastError() ;
}

}

#34


心跳检测
当服务端检测到连接已经断开后,关闭句柄就可以再次连接了

#35


每次,client与server建立连接时,记录ip与tcp的相关信息.
当重连机制运行时,先根据上次的连接时,所记录的信息,伪造一个tcp的包,发给server,这样,server就会认为这是client的退出数据包.然后就可以重连了..
呵呵,不知是否有用,可以试试!!

#36


引用 30 楼 jackson35296 的回复:
引用 29 楼 aaadddzxc 的回复:
楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。


没有谁规定select只能用于模型中,把它作为一个api来使用就行了,任何时候都可以用


恩 在补充下
如果你只考虑重新连接 可以看看这代码

if(isConnect)//发现连接已建立,而调用者没有主动断开就退出程序,自动断开连接,释放资源
{
shutdown(ClientSock,0x02);
closesocket(ClientSock);
isOnlyConnect = false;
}

#37


写一个hook dll注入到server中,增加一个控制程序,如果客户端断开了,再在recv里面尝试一些解决办法,比如给一个端口协议包

#38


“关键是,服务端还不能更改”

服务器端不改,一切皆无可能。
原始机制的问题。

#39


当客户端连接断开的时候,server会收到状态码的啊

#40


"关键是,服务端还不能更改"
服务器代码不能更改吗?
如果不能改的话,只修改客户端,就比较麻烦,

#41


该回复于2011-02-17 08:41:48被版主删除

#42


该回复于2011-02-17 08:41:48被版主删除

#43


引用 38 楼 baoyz 的回复:
“关键是,服务端还不能更改”

服务器端不改,一切皆无可能。
原始机制的问题。

类似QQ断线重连的

#44


对于你的问题几乎无解,服务器自己没处理好,又不能改,还能怎么办

#45


这么多人回复,楼主也不结贴的

#46


注意 lz说的是S端不能更改,那么S端的机制LZ能知道吧?
一定要了解S端的机制,如果能知道就好办,只有根据它的机制来确定C/S两端如何配合。

#47


心跳才是王道

#48


这个问题 我也遇见过,我的服务端是一个网络打印机  所以不能改动它的程序,
我客户端这边也不好弄,  要怪 只怪TCP协议的问题

#49


引用 28 楼  的回复:
我是从来不用心跳,影响效率,起一个线程去select就行了
C/C++ code

int select(
  __in          int nfds,
  __in_out      fd_set* readfds,
  __in_out      fd_set* writefds,
  __in_out      fd_set* exceptfds,
  __in         ……


这个不行的,具体你得另外查资料。
好像说当时设计tcp的时候考虑很多什么的,网络断开最长可以有几个小时你都收不到任何有用的事件一切都是正常,甚至几个小时以后网络恢复,这条链路还是可用的。
这个可以通过设置socket一个什么参数来决定。

#50


1.KeepAlive机制 
2.心跳

#1


本帖最后由 VisualEleven 于 2011-01-25 09:09:34 编辑
if(connect(my_socket,(SOCKADDR *)&target,sizeof(target))==SOCKET_ERROR )
{
 AfxMessageBox("服务器未启动");
 WSACleanup();
return 0;
}

#2


心跳检测
当服务端检测到连接已经断开后,关闭句柄就可以再次连接了

#3


不能通过客户端解决
通常的做法就是服务器发送心跳包

#4


WSAAsyncSelect()
FD_CLOSE 通知码

#5


客户端发送心跳待服务端确认,增加这么一个过程

#6


客户端发送心跳包,服务器用定时器判断客户端多少时间没有数据,没数据则断开

#7


服务端需要有心跳机制等来维护连接,当客户端断开就有超时,服务端来判断连接断开

#8




KeepAlive机制

#9


上面都说了,利用心跳机制来检测

#10


通过心跳消息检测

#11


服务端还不能更改不好解决。除非客户端能用raw发close(fd)的包到服务器。不过应该比较难,想想而已。

#12


心跳包超时检测

#13


都断网了还能通知各屁啊

#14


如果不修改服务端是很难做到的。心跳机制需要S/C双方协调控制。

#15


如果不修改服务端是很难做到的。心跳机制需要S/C双方协调控制。

#16


WSAAsyncSelect()
FD_CLOSE 通知码

这是很简单的方法,但是缺点就是在断线后差不多要10秒左右才能收到,不能及时

#17


添加心跳消息。

#18


如果是tcp的,recv()返回0时就说明连接关闭了,当然这是正常关闭,最好还是用心跳

#19


该回复于2011-01-26 17:25:46被版主删除

#20


如果服务断开了,客户端的recv会返回SOCKET_ERROR

#21


服务端你不能更改你怎么改客户端?

#22


心跳最准确 其他的方法难免会有些附加条件(如正常关闭)

#23


哈哈,心跳是王道!

#24


假如你客户端一直发送消息到服务器端的话,还有一招简单的。
就是在服务器端定义个int变量,设定个计时器,没有消息就+1,
来了消息就把变量清0,假如好比变量>10时还没有消息,那就删除socket,重新创建监听socket

#25


服务器端不能改,水平有限。算我没说

#26


最好用心跳机制

#27


从服务端角度看可以在应用层增加心跳机制,或者用keepalive检测。
服务端无法修改,那还真没有什么好的办法。
如果楼主的这种情况时常发生,那要从网络上看看是否存在什么问题。

#28


我是从来不用心跳,影响效率,起一个线程去select就行了

int select(
  __in          int nfds,
  __in_out      fd_set* readfds,
  __in_out      fd_set* writefds,
  __in_out      fd_set* exceptfds,
  __in          const struct timeval* timeout
);


第4个参数exceptfds中就是所有异常的socket,当然也包括连接已经断开的。无论是异常还是连接断开,都需要重连。
用心跳的话,假如有1000个客户端连着服务器,那么光处理心跳包就要耗费多少资源?光看题目,没打开帖子之前,我就猜到80%的人会回答心跳,结果和预想的一样。其实很多人都没有实践过,只是人云亦云

#29


楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。

#30


引用 29 楼 aaadddzxc 的回复:
楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。


没有谁规定select只能用于模型中,把它作为一个api来使用就行了,任何时候都可以用

#31


引用 13 楼 covsno 的回复:
都断网了还能通知各屁啊

说有对`服务器必须主动去测它才能知道
而不是客户端掉了后才去通知
掉了就通知不了了`
心跳机制还是蛮好的
你可以定时发送包要是收不到就说明掉了
也可以测本地网络当网速差时发个包说明网络不好也行
要是客户拔线了`必需得用服务器去测

#32


任何连接都需要增加心跳机制。

#33


对socket启用keepalive机制,把心跳包机制交给系统来处理

void TcpConnection::SetKeepAlive(bool bOnOff, DWORD nKeepAliveTime, DWORD nKeepAliveInterval)
{
DWORD dwError = 0L;
DWORD dwBytes = 0L;
tcp_keepalive sKA_Settings = {0}, sReturned = {0} ;
sKA_Settings.onoff = bOnOff;
sKA_Settings.keepalivetime = nKeepAliveTime;  sKA_Settings.keepaliveinterval = nKeepAliveInterval; 
if (WSAIoctl(m_Socket, SIO_KEEPALIVE_VALS, &sKA_Settings,
sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes,
NULL, NULL) != 0)
{
dwError = WSAGetLastError() ;
}

}

#34


心跳检测
当服务端检测到连接已经断开后,关闭句柄就可以再次连接了

#35


每次,client与server建立连接时,记录ip与tcp的相关信息.
当重连机制运行时,先根据上次的连接时,所记录的信息,伪造一个tcp的包,发给server,这样,server就会认为这是client的退出数据包.然后就可以重连了..
呵呵,不知是否有用,可以试试!!

#36


引用 30 楼 jackson35296 的回复:
引用 29 楼 aaadddzxc 的回复:
楼上的这方法还是不大实用。真是环境模型肯定只有一种,IOCP或者SELECT!。目前并没有更好的解决方案,正常掉线情况下 recv 可以收到断开提示。


没有谁规定select只能用于模型中,把它作为一个api来使用就行了,任何时候都可以用


恩 在补充下
如果你只考虑重新连接 可以看看这代码

if(isConnect)//发现连接已建立,而调用者没有主动断开就退出程序,自动断开连接,释放资源
{
shutdown(ClientSock,0x02);
closesocket(ClientSock);
isOnlyConnect = false;
}

#37


写一个hook dll注入到server中,增加一个控制程序,如果客户端断开了,再在recv里面尝试一些解决办法,比如给一个端口协议包

#38


“关键是,服务端还不能更改”

服务器端不改,一切皆无可能。
原始机制的问题。

#39


当客户端连接断开的时候,server会收到状态码的啊

#40


"关键是,服务端还不能更改"
服务器代码不能更改吗?
如果不能改的话,只修改客户端,就比较麻烦,

#41


该回复于2011-02-17 08:41:48被版主删除

#42


该回复于2011-02-17 08:41:48被版主删除

#43


引用 38 楼 baoyz 的回复:
“关键是,服务端还不能更改”

服务器端不改,一切皆无可能。
原始机制的问题。

类似QQ断线重连的

#44


对于你的问题几乎无解,服务器自己没处理好,又不能改,还能怎么办

#45


这么多人回复,楼主也不结贴的

#46


注意 lz说的是S端不能更改,那么S端的机制LZ能知道吧?
一定要了解S端的机制,如果能知道就好办,只有根据它的机制来确定C/S两端如何配合。

#47


心跳才是王道

#48


这个问题 我也遇见过,我的服务端是一个网络打印机  所以不能改动它的程序,
我客户端这边也不好弄,  要怪 只怪TCP协议的问题

#49


引用 28 楼  的回复:
我是从来不用心跳,影响效率,起一个线程去select就行了
C/C++ code

int select(
  __in          int nfds,
  __in_out      fd_set* readfds,
  __in_out      fd_set* writefds,
  __in_out      fd_set* exceptfds,
  __in         ……


这个不行的,具体你得另外查资料。
好像说当时设计tcp的时候考虑很多什么的,网络断开最长可以有几个小时你都收不到任何有用的事件一切都是正常,甚至几个小时以后网络恢复,这条链路还是可用的。
这个可以通过设置socket一个什么参数来决定。

#50


1.KeepAlive机制 
2.心跳