免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: yulihua49
打印 上一主题 下一主题

[C] 多线程服务器,发现个问题,请高人解惑 [复制链接]

论坛徽章:
0
21 [报告]
发表于 2010-07-06 13:39 |只看该作者
在线程处理函数里要 close( ((T_Connect *)param)->Socket ). 然后再置-1. 如果不关闭的话,总的连接数会不断上升的.

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
22 [报告]
发表于 2010-07-06 13:43 |只看该作者
另外你的情况应该有两个问题,第一个问题就是我说的你没有设置线程属性,自行释放资源.还有一个问题是在客户端 ...
samlumengjun 发表于 2010-07-05 18:49



    不管发生什么情况,工作线程退出时都要close(socket);

现在发现客户端连接后,交换密钥(客户发一包,收一包,服务器收一包,发一包)正常,紧接着客户端发登录串。
服务器没收到,select夭折,errno=84,随后关闭socket,退出线程。
客户端select返回socket可读,接着read,死住。
以上情况在密集访问时偶然发生。其他环节从未发生问题。登录成功的连接无论收发多少包都没问题。

论坛徽章:
0
23 [报告]
发表于 2010-07-06 13:49 |只看该作者
回复 22# yulihua49


    此socket非彼socket,实际上就是你传入的s,是accept以后新的描述符.你在主程序里关闭的是你监听的socket,不是同一个概念.除非你没有把线程处理函数贴全,要不然我没有看到你在线程里关闭socket呀,所以你的客户端就死等了.

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
24 [报告]
发表于 2010-07-06 13:56 |只看该作者
本帖最后由 yulihua49 于 2010-07-06 14:31 编辑
回复  yulihua49


    此socket非彼socket,实际上就是你传入的s,是accept以后新的描述符.你在主程序里 ...
samlumengjun 发表于 2010-07-06 13:49



    关闭的是新socket,因为原始的socket从来没有送到线程。
s不能关闭,正常情况是线程客户端连接,还用的。
客户端服务器都调这个个函数接收:
  1. int RecvNet(int s,char *buf,int n,int timeout)
  2. {
  3. int bcount,br,ret;
  4. struct timeval tm;
  5. //timeout for second
  6. int RecvNet(int s,char *buf,int n,int timeout)
  7. {
  8. int bcount,br,ret;
  9. struct timeval tm;
  10. fd_set fds,efds;

  11.         if(timeout) {
  12.                 FD_ZERO(&fds);
  13.                 FD_ZERO(&efds);
  14.                 FD_SET(s, &fds);
  15.                 FD_SET(s, &efds);
  16.                 tm.tv_sec=timeout;
  17.                 tm.tv_usec=0;
  18.                 ret=select(s+1,&fds,NULL,&efds,&tm);
  19.                 if(ret<0) return ret;
  20.                 if(0==ret) return TIMEOUTERR;

  21.         if( FD_ISSET(s, &fds))
  22.                 ShowLog(5,"%s:aft select ret=%d fds tobe set  n=%d,sock=%d,sec=%ld,usec=%ld",
  23.                         __FUNCTION__,ret,n,s,tm.tv_sec,tm.tv_usec);
  24.         }
  25.         bcount=0;
  26.         br=0;

  27.         while(bcount<n){
  28.                 if((br=read(s,buf,n-bcount))>0){
  29.                         bcount+=br;
  30.                         buf+=br;
  31.                         continue;
  32.                 }
  33.                 if(br<0){
  34.                     return -1;
  35.                 }
  36.                 if(!br){
  37.                         usleep(5000);
  38.                         continue;
  39.                 }
  40.         }
  41.         if(bcount<=0)return -1;
  42.         return bcount;
  43. }

复制代码
23行:if( FD_ISSET(s, &efds))永远没结果,即使s已经被服务器关闭。
取消select,直接read,照旧会死。
并发960个客户端,会死2-5个。

发现问题了,出在39-41行,不断的死循环。改成失败100次退出,基本可以了。

论坛徽章:
0
25 [报告]
发表于 2010-07-06 14:04 |只看该作者
已经害怕多线程+select 密集

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
26 [报告]
发表于 2010-07-06 14:07 |只看该作者
本帖最后由 yulihua49 于 2010-07-06 14:10 编辑
已经害怕多线程+select 密集
zimang 发表于 2010-07-06 14:04



    是啊,我都晕倒了。
可是,有办法吗?用什么代替?
取消select也不行,多线程+read密集也不行。
现在好在服务器不死了,内存也基本稳住了,有时大点,有时小点。

980个客户端一哄而上,总有三五个憋死的。

论坛徽章:
0
27 [报告]
发表于 2010-07-06 14:11 |只看该作者
回复 26# yulihua49


   不是长连接的话 就一个worker线程  呵呵

可以试试 epoll  感觉要明了点

论坛徽章:
0
28 [报告]
发表于 2010-07-06 14:18 |只看该作者
把你的线程处理函数全部贴出来,我看一下. select没什么问题,相比epoll要简单.

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
29 [报告]
发表于 2010-07-06 14:36 |只看该作者
本帖最后由 yulihua49 于 2010-07-06 15:14 编辑
把你的线程处理函数全部贴出来,我看一下. select没什么问题,相比epoll要简单.
samlumengjun 发表于 2010-07-06 14:18


代码在24楼,把39行的死循环限制100次,客户端可以异常退出了。
问题还是出在服务器,多线程select,虽然基本可以正常工作,但忙起来还是会挤掉个别连接。估计也不一定是select,因为正常连接是挤不掉的,只有未登录的连接会被挤掉,看来毛病在我。
在46行挤掉的。
线程工作函数:
  1. #include <sdbc.h>
  2. #include <bignum.h>

  3. extern srvfunc Function[]; //user program.

  4. static void * thread_work(void *param)
  5. {
  6. T_Connect Conn=*(T_Connect *)param;
  7. T_NetHead Head;
  8. int ret,logined=0;
  9. T_SRV_Var ctx;
  10. srvfunc *fp;
  11. int svcnum=0;
  12. int (*init)(T_Connect *conn,T_NetHead *head);

  13. #ifdef __GNUC__
  14. char gda[Conn.SendLen+1];//本线程的全局数据区必须在此分配。
  15. #else
  16. char *gda=alloca(Conn.SendLen+1);
  17. #endif

  18.         ((T_Connect *)param)->Socket=-1;//通知主线程
  19.         if(Conn.SendLen>0) ctx.var=gda;
  20.         else ctx.var=0;
  21.         Conn.SendLen=0;
  22.         ctx.tid=pthread_self();//标志多线程服务
  23.         ctx.poolno=0;
  24.         ___SQL_Init_SQL_Connect(&ctx.SQL_Connect);
  25.         Conn.Var=&ctx;
  26.         init=Conn.only_do;
  27.         Conn.only_do=0;
  28. //借用only_do存放函数地址 conn_init
  29.         if(init) init(&Conn,&Head);
  30.         if(!Conn.only_do) for(fp=Function;fp->funcaddr!=0;fp++) svcnum++;

  31.         ShowLog(2,"%s:tid=%lu,sock=%d",__FUNCTION__,ctx.tid,Conn.Socket);
  32. // 协商密钥
  33.         Conn.CryptFlg=mk_clikey(Conn.Socket,&Conn.t,Conn.family);
  34.         if(Conn.CryptFlg<0) { //协商密钥失败
  35.                 ShowLog(1,"%s:tid=%lu 协商密钥失败!",__FUNCTION__,ctx.tid);
  36.                 freeconnect(&Conn);
  37.                 return NULL;
  38.         }

  39.         while(1) {
  40.                 ret=RecvPack(&Conn,&Head);
  41.                 if(ret<0) {
  42.                         ShowLog(1,"%s:tid=%lu,接收结束,sock=%d,status=%d,%s",
  43.                                 __FUNCTION__,ctx.tid,Conn.Socket,errno,strerror(errno));
  44.                         break;
  45.                 }
  46.                 ShowLog(4,"%s: tid=%lu,PROTO_NUM:%d len:%d",__FUNCTION__,ctx.tid,
  47.                         Head.PROTO_NUM,Head.PKG_LEN);
  48.                 if(Head.PROTO_NUM==1){
  49.                         Echo(&Conn,&Head);
  50.                         continue;
  51.                 }
  52.                 if(Head.PROTO_NUM==0xFFFF){
  53.                         ShowLog(0,"%s:Disconnect by client,tid=%lu",__FUNCTION__,ctx.tid);
  54.                         break;
  55.                 }
  56.                 if(!Head.PROTO_NUM) {
  57.                         if(!logined) {//还是一个有状态服务
  58.                                 logined=Function[0].funcaddr(&Conn,&Head);
  59.                                 if(logined==-1) break;
  60.                         } else {
  61.                                 if(!Conn.only_do) ret=get_srvname(&Conn,&Head);
  62.                                 else ret=Conn.only_do(&Conn,&Head);
  63.                                 continue;
  64.                         }
  65.                 } else if(Conn.only_do) {
  66.                         ret=Conn.only_do(&Conn,&Head);
  67.                         continue;
  68.                 } else {
  69.                         if(Head.PROTO_NUM>svcnum) {
  70.                                 ShowLog(1,"%s:没有这个服务号 %s",__FUNCTION__,Head.PROTO_NUM);
  71.                                 break;
  72.                         }
  73.                         ret=Function[Head.PROTO_NUM].funcaddr(&Conn,&Head);
  74.                         if(ret==-1) {
  75.                                 ShowLog(0,"%s:Disconnect by server PROTO_NUM=%d,ret=%d",
  76.                                         __FUNCTION__,Head.PROTO_NUM,ret);
  77.                                 break;
  78.                         }
  79.                 }
  80.         }
  81.         freeconnect(&Conn);//关闭连接
  82.         mthr_showid_del(ctx.tid);
  83.         return NULL;
  84. }

复制代码

论坛徽章:
0
30 [报告]
发表于 2010-07-06 15:30 |只看该作者
我不清楚你的业务逻辑,但是45行到87行的while循环里,server端的continue语句应该会有问题.server没有发给客户端任何消息,然后直接读,难道第一次没有读完?这样的话server就会循环去读,什么也读不到,而客户端死等.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP