求一对多服务端的客户端非法下线处理办法(加分修正版)

kkkgho 2008-11-12 11:13:37
我写了一个实时聊天程序,这个程序分服务端和客户端
服务端记录在线人员和人数,中转聊天消息
客户端请求并显示在线列表,接收其他客户端的消息,向其他客户端发起对话

现在按照正常的退出,需要发送一个退出消息给服务端,服务端在线人数-1,并且将此客户端从在线列表除名

现在的问题是:当用户直接结束进程或断电时,会导致退出消息无法正常发出,这个客户就永远挂在了在线列表

我考虑过超时的解决办法,但是在效率上有很大的问题:
你得为每一个客户机对应一个超时累计时间,每一秒累计时间+1,这样假设如果有500个用户,CPU 1秒要做500次加运算
而且客户端需要5秒发送一个表示在线的包给服务端,服务端再把该客户端的超时累计时间清0
而且每1秒要判断500次累计超时时间是否大于60秒,如果大于再从在线列表除名
这样明显不可行,大家有什么更好的办法吗?

注:程序基于控制台+多线程
...全文
159 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
herman~~ 2008-11-15
  • 打赏
  • 举报
回复
客户端或者服务端发送心跳都可以
但要控制心跳的频率 例如:客户C1,C2每隔3分钟需要心跳到服务端,服务端对于超时未发送心跳包的客户做清理下线

由于心跳包一般比较小,只要频率控制合理 一般不会造成过大的负载
kkkgho 2008-11-13
  • 打赏
  • 举报
回复
准备采用心跳包,正在设计中……
xhs_lh04 2008-11-13
  • 打赏
  • 举报
回复
如果是长连接可采用心跳包机制

另外你的所有send,recv函数加上判断返回码
如果对端关闭,recv接收到的返回是0,判断返回为0,即作为客户端下线处理,关闭连接
帅得不敢出门 2008-11-13
  • 打赏
  • 举报
回复
用select来查询是否超时
引用一段

//如果是发送者,则不发送数据
fd_set fdsetWrite;
FD_ZERO(&fdsetWrite);
FD_SET(hSocket,&fdsetWrite);
TIMEVAL tv;
tv.tv_sec=0; tv.tv_usec=0;
int nNum=select(0,NULL,&fdsetWrite,NULL,&tv);
if(nNum == SOCKET_ERROR || 0 == nNum)
//已经退出
else
//socket好使;

Winsock貌似也有类似的功能 你没用就另当别论了.
having_hr 2008-11-13
  • 打赏
  • 举报
回复
拼音命名不很清晰
kkkgho 2008-11-13
  • 打赏
  • 举报
回复
还在调查。。。。很复杂搞不清楚
xhs_lh04 2008-11-13
  • 打赏
  • 举报
回复
为什么会丢包?
这不可能

如果丢包还是你程序的问题,TCP是可靠连接
kkkgho 2008-11-13
  • 打赏
  • 举报
回复
现在整个程序基本好了。。。但是就是丢包很严重,经常收不到数据
星羽 2008-11-12
  • 打赏
  • 举报
回复
可以通过发送心跳包确认某client是否掉线
kkkgho 2008-11-12
  • 打赏
  • 举报
回复
客户端代码

// 客户端.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Winsock2.h>
#include <stdio.h>
#include <iostream.h>
#include <process.h>
SOCKET sockClient;

void zikehu(void* para)
{
char neirong[1020];
while(1)
{
unsigned long ul = 1;
int iResult = ioctlsocket(sockClient,FIONBIO, (unsigned long *)&ul);
recv(sockClient,neirong,1020,0);
if(neirong[0]=='c'&&neirong[1]=='h'&&neirong[2]=='a'&&neirong[3]=='t')
{
neirong[0]=' ';
int tmp=0;
for(int i=4;neirong[i]!='\0';i++)
{
if(neirong[i]=='v'&&neirong[i+1]=='k'&&neirong[i+2]=='-')
{
if(tmp==0)
{
cout<<" 对你说:";
i=i+3;
tmp=1;
}
cout<<neirong[i];
}
else
{
cout<<neirong[i];
}
tmp=0;
}
cout<<endl;
}
Sleep(200);
}
}
void main()
{
char ip[50];
cout<<"请输入服务器的IP地址"<<endl;
cin>>ip;
WSADATA wsaData;
WORD banben;
banben=MAKEWORD(1,1);
WSAStartup(banben,&wsaData);
sockClient=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr(ip);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(5043);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//向服务器发出连接请求(connect)。

char recvBuf[100];//和服务器端进行通信(send/recv)。
recvBuf[0]='\0';
recv(sockClient,recvBuf,100,0);
if(recvBuf[0]=='\0')
{
printf("连接服务器失败!\n",NULL);
return;
}
printf("%s\n",recvBuf);
printf("请设定你的昵称\n",NULL);
char nicheng[100];
cin>>nicheng;
send(sockClient,nicheng,strlen(nicheng)+1,0);
int caozuo;
_beginthread(zikehu,0,NULL);
menu:
cout<<"----------请选择操作----------"<<endl;
cout<<"1.看看谁在线上"<<endl;
cout<<"2.开始对话"<<endl;
cout<<"3.退出"<<endl;
cin>>caozuo;
if(caozuo==1)
{
send(sockClient,"0",strlen("0")+1,0);
char tmp[2000];
tmp[0]='\0';
jieshou:
recv(sockClient,tmp,2000,0);
if(tmp[0]=='\0')
{
goto jieshou;
}
for(int i=0;i!=2000;i++)
{
if(tmp[i]==',')
{
cout<<endl;
}
else if(tmp[i]=='\0')
{
break;
}
else
{
cout<<tmp[i];
}
}
}
if(caozuo==2)
{
char name[100],name1[101],x[10];
cout<<"请输入你要对话的人的昵称"<<endl;
cin>>name;
name1[0]='1';
name1[1]='\0';
strcat(name1,name);
send(sockClient,name1,strlen(name1)+1,0);
recv(sockClient,x,10,0);
if(x[0]=='0')
{
cout<<"你输入的用户不存在,对话被拒绝"<<endl;
goto menu;
}
else
{
char chat[1000];
cout<<"你对 "<<name<<" 说:";
cin>>chat;
send(sockClient,chat,strlen(chat)+1,0);
}
}
if(caozuo==3)
{
send(sockClient,"exit",strlen("exit")+1,0);
cout<<"退出成功!"<<endl;
goto exit;
}
goto menu;
exit:
closesocket(sockClient);//关闭套接字。
WSACleanup();//必须调用这个函数清除参数
}
kkkgho 2008-11-12
  • 打赏
  • 举报
回复
服务端代码


#include "stdafx.h"

#include <Winsock2.h>//加裁头文件
#include <stdio.h>//加载标准输入输出头文件
#include <iostream.h>
#include <process.h>


struct LIST
{
char name[50];
SOCKET lianjie;
};

SOCKADDR_IN duihua[2000];
int biaoji,renshu=0;
LIST list[500];

void zikehu(void* para)
{
int biaozhi=biaoji;

char buf[100];
sprintf(buf,"%s 已经成功连接服务器",inet_ntoa(duihua[biaozhi].sin_addr));
send(list[biaozhi].lianjie,buf,strlen(buf)+1,0);

char buf2[100];
recv(list[biaozhi].lianjie,buf2,100,0);
printf("用户 %s 已经成功连接服务器\n",buf2);

strcat(list[renshu-1].name,buf2);

char buf3[100];
buf3[0]='\0';

while(1)
{
send(list[biaozhi].lianjie,"-",strlen("-")+1,0);
recv(list[biaozhi].lianjie,buf3,100,0);
if(buf3[0]=='0')
{
printf("用户 %s 请求在线用户列表\n",buf2);
buf3[0]='\0';
char zaixian[2000];
zaixian[0]='\0';
for(int i=0;i<renshu;i++)
{
if(list[i].name[0]=='\0')
continue;
strcat(zaixian,list[i].name);
strcat(zaixian,",");
}
cout<<"发送在线列表源数据:"<<zaixian<<endl;
send(list[biaozhi].lianjie,zaixian,strlen(zaixian)+1,0);
}
else if(buf3[0]=='1')
{
int x=0;
SOCKET tmp1;
buf3[0]=' ';
cout<<"用户 "<<buf2<<" 向"<<buf3<<" 发起对话"<<endl;
for(int j=0;j<=strlen(buf3);j++)
{
buf3[j]=buf3[j+1];
}
buf3[strlen(buf3)]='\0';

for(int i=0;i<=renshu;i++)
{
if(!strcmp(list[i].name,buf3))
{
x=1;
tmp1=list[i].lianjie;
}
}
if(x==0)
{
cout<<"警告:用户 "<<buf3<<" 不存在!"<<endl;
send(list[biaozhi].lianjie,"0",strlen("0")+1,0);
}
else
{
char fasong[1010];
char sed[1020];
send(list[biaozhi].lianjie,"1",strlen("0")+1,0);
recv(list[biaozhi].lianjie,fasong,1010,0);
sed[0]='\0';
strcat(sed,"chat");
strcat(sed,list[biaozhi].name);
strcat(sed,"vk-");
strcat(sed,fasong);
send(tmp1,sed,strlen(sed)+1,0);
cout<<"发送聊天源数据:"<<sed<<endl;
}
}
else if(buf3[0]=='e'&&buf3[1]=='x'&&buf3[2]=='i'&&buf3[3]=='t')
{
cout<<"用户 "<<list[biaozhi].name<<" 已经退出服务器!"<<endl;
list[biaozhi].name[0]='\0';
list[biaozhi].lianjie=0;
return;
}
buf3[0]='\0';
Sleep(300);
}
}

void main()
{
WSADATA wsaData;
WORD banben;
banben=MAKEWORD(1,1);

WSAStartup(banben,&wsaData);

SOCKET taolian;
taolian=socket(AF_INET,SOCK_STREAM,0);

SOCKADDR_IN dizhi;
dizhi.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
dizhi.sin_family=AF_INET;
dizhi.sin_port=htons(5043);

bind(taolian,(SOCKADDR*)&dizhi,sizeof(SOCKADDR));
listen(taolian,5);

int len=sizeof(SOCKADDR);

cout<<"服务器成功启动!"<<endl;

while(1)
{
biaoji=renshu;
list[renshu].lianjie=accept(taolian,(SOCKADDR*)&duihua[renshu],&len);//程序会暂停在此直到接收到客户端链接
_beginthread(zikehu,0,NULL);
Sleep(50);
renshu++;
}
}


代码转载自:https://pan.quark.cn/s/a4b39357ea24 基于VHDL语言的24进制多功能数字钟 FPGA多功能数字钟设计 安装要 本项目采用QuartusII9.0版本设计,非9.0版本打开可能会存在兼容性问题。 本项目选用FPGA器件为CycloneIII_EP3C40Q240C8 设计任务 设计一个24进制多功能数字电子钟,要具备以下功能: (1)时钟显示:能够以十进制在7段数码管上显示“时”、“分”、“秒”、“十分之一秒”。 (2)校表功能:能够对时钟进行校正。 (3)启动/暂停功能:能够控制时钟的启动和暂停。 (4)一键清零功能:能够将时钟清零。 (5)整点报时功能:能够在整点时发出报时信号。 (6)闹钟功能:能够在设置的时间到达时发出闹钟信号。 顶层设计原理图如下: image 设计方案/设计原理及总体框图 设计实现思路: (1)计时功能。 计时功能主要由四个计数器模块共同构成,其中十分之一秒计时器为十进制计数、分,秒计时器为六十进制计数、小时计时器为二十四进制计数。 计时器之间采用进位信号进行串联。 (2)十进制7端数码管显示功能。 显示功能由译码器模块实现。 译码器模块的两个输入端分别为刷新端和数据端。 七个译码器的刷新端连接十分之一秒的周期脉冲信号。 而数据端连接计数器的输出端,用以将4位输出BCD码译码为7端数码管的七位显示信号。 (3)校表功能。 校表功能由二选一模块和校时模式选择器模块构成。 其中二选一模块用于连接下一级计时器模块的进位信号和手动按钮脉冲信号。 当控制信号为“0”时,二选一模块输出计时器模块的进位信号。 而当控制信号为“1”时,二选一模块输出手动按钮信号。 控制信号由校时模式选择器模块输出,校时模式选择器输出端连...
内容概要:本文围绕基于交替方向乘子法(ADMM)的多主体综合能源系统分布式协同优化展开研究,提出一种可在保护各参与主体数据隐私的前提下实现全局协同优化的分布式算法框架。通过构建增广拉格朗日函数,将集中式优化问题分解为多个子问题,各主体并行迭代解并仅交换边界变量信息,从而在保证隐私安全的同时实现系统整体能效与经济性的提升。研究详细阐述了ADMM算法的数学建模过程、收敛性条件及其实现在多主体电-热-气综合能源系统中的应用,并通过Matlab代码进行仿真验证,展示了其在园区、微电网群等场景下的有效性与实用性。; 适合人群:具备一定电力系统优化、分布式算法理论基础及Matlab编程能力的研究生、科研人员以及从事综合能源系统、能源互联网、智能电网等相关领域的工程师;特别适合关注分布式优化、隐私保护协同调度等前沿方向的专业技术人员。; 使用场景及目标:①应用于多主体异构能源系统的协同调度,如工业园区、城市能源网络、微电网集群等复杂场景;②实现各利益主体在不共享原始负荷、成本等敏感数据的情况下完成联合优化,提升系统整体运行效率;③为ADMM算法在实际能源系统中的工程化部署提供可复现的代码实例与完整技术路径参考。; 阅读建议:建议读者结合提供的Matlab代码逐模块理解算法实现细节,重点掌握增广拉格朗日函数构造、对偶变量更新机制与收敛判据的设计;同时可进一步拓展学习一致性ADMM(C-ADMM)、乘子惩罚方法等改进算法,深化对分布式优化理论体系的理解。

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧