Indy的TCPServer到底能支持多少个连接

最近一个项目,最开始使用IdTcpServer,在大压力测试的时候,只连接了800个多一点的客户端(每个客户端连接上之后每秒钟发送一个几十字节的报文,服务器应答)。但是持续的时间不会超过10分钟,服务器就会挂掉(经常是服务器突然关闭消失,任何提示都没有)。后来优化了互斥量之后,可以连接到1000多个客户端。但是服务器消失的问题依然存在。
    今天再一台双CPU,4G内存的服务器上试验了下,居然最也只能连接到2000多个客户端。然后换了Indy10.1.5服务器只做简单的连接和应答,客户端连接之后只发送一个报文,还是一样,服务器最多只能连接2000多个客户端。
    然后下载了Indy10的官方帮助文件(http://www.projectindy.org/downloads/IndyDocs_10.1.5.0_HtmlHelp.zip)它的TCPServer帮助里面有一段话: 
    But there are performance and resource limitations imposed by the platform or Operating System on the number of threads that can be created. On Windows, for example, the number of threads is limited by the available virtual memory. By default, every thread gets one megabyte of stack space and implies a theoretical limit of 2028 threads. Reducing the default stack size will allow more threads to be created, but will adversely impact system performance. 
    意思理论上Windows的操作系统最多支持2028个线程。于是昏倒了。因为我们的客户端最少也会有5000个呀。
    后来有换了delphi的异步非阻塞TCPServer,测试连接了15000个客户端都没有啥问题。
    难道Indy真的只能支持到2000多个客户端连接?
    各位大侠有用过Indy的TCP服务器的,你们的服务器可以连接多少客户端呀?
 
 
没做过这中压力测试得呢
 
真有这么大量的连接,建议做IOCP+WinSock API,别用控件的
 
Windows的操作系统不一只支持2028个线程的!
用CreateThread测试,数万个都OK的,只是越来越慢而已
 
大量的连接非常注重内存泄漏等问题的,并需要修改源码的
 
Indy10支持IOCP和纤程,不过要安装SuperCore包(默认没有),并设置Server的IOHandle,否则Indy10的效能在Windows和Indy9一样
 
减少线程的stack空间可以增加线程数量,
或者在xp+(只是xp+,不包括win2k)的boot.ini中加上/3GB选项也可以增加进程可用的地址空间。
但是不管怎么说,

在32-bit的Windows上,最多的同步线程在2000~3000这个水平。

支持更多的用户可以使用分布式处理,即你的主服务器把试图连接的客户端分配到其他服务器去。象QQ、hotmail之类的都是这么处理的,
每次你连接的都是www.hotmail.com,但是具体完成服务的可能是xxxxx.xxxx.hotmail.msn.com之类的。
 
一台服务器能够处理2000个连接已经不错了,多搞几台服务器吧。
 
To ly_liuyang,THX,看了IOCP,还没有开始做就昏了。明天继续学习。
To getit911,THX,我怎么安装不了SuperCore的包呀,编译通不过,你是怎么安装的?我也看到Indy的帮助说纤程,当时也奇怪,怎么他的IOHandler里面没有这个东西,都是线程的。
 
再顶一下,明天来结分。
 
indy的tcpserver使用的io模型是多线程阻塞socket,对超过1000的并发连接,并不是好的选择,indy10并不支持iocp,里面的纤程是针对unix传统的多进程模型在windows上的移植.这样的情况,建议使用iocp,看看msdn,自己写吧,也不是很复杂
 
用一个线程绑定一个客户连接,效率比较差了,
 
对于密集型的应用,不能用indy这样的控件,他太复杂了,反而没有效率了。
 
对于这样的应用,只能采用非阻塞的方式了,虽然比较麻烦,但是只有这个办法,让一个线程为多个客户服务。
 
同时,要使用线程池,不要客户断开就释放线程,线程可以挂起留着再用。
 
 
分布式处理是最佳途径。
多增加几台服务器的硬件价格是微不足道的,
即使你使用更复杂的模型,完成端口+线程池,
所能增加的用户数量也是有限的,也就5%吧。
而且这种模式也是难以移植的。
 
 
我又想到了一个提高负载的办法。
就是创建多个进程,单一进程的线程数量是受到限制的,但是,使用多个进程就能避免这个结果。
 
可以根据负载情况,灵活的增加进程数量,但是,一个进程只能打开一个同一个端口的句柄,好像有一个复制句柄的方式,让别的进程共享同一个Socket句柄。

如果实现这个,就不受线程数量的限制了,不过,我不是很清楚windows在实现阻塞Socket的方式,阻塞的好像是循环的,不能释放线程的,效率低下,应该使用异步Socket,这样才能让等待线程挂起。
 
 
楼上的,不可能。
一个系统中总的线程数量是有限的,这是因为
每一个线程都要分配一个TCB,4KB,这个是不能交换到虚存的,另外还要分配局部stack。
内存空间的限制决定了线程的数量不可能很多。
而且活跃线程增加到一定程序,系统的响应速度就严重降低了。
 
 
多个进程远远比多线程更耗资源..
如果是多用户长连接,可以考虑使用IOCP,用少量线程服务大量客户.
至于线程池,倒是一般都用到的..主要能在大量短连接通讯时候起作用.
Woo~看来讨论挺激烈呀。再加些分。多讨论下。
 
用纯Winsock API做了一个简单的TcpServer,没有IOCP
采用线程方式,一个服务器最多也只能创建2000个左右的连接线程,之后出现10053(软件造成连接取消)错误。证明不是Indy控件的问题。
 
接下来再试试异步方式。
 
Winsock API的程序代码如下:
 
program WinSockSvr;
 
{$APPTYPE CONSOLE}
 
(*-------------------------------------------------------------------
说明:TCP/IP 服务器演示程序 Ver0.1
 
      采用标准WinSock2.2 API函数
 
      采用线程堵塞模式,每个客户端连接创建一个收发线程。
 
作者:Zuni
 
时间:2006-3-20
 
使用到的函数说明:
 
      WSAStartup     //初始化WinSock
      WSACleanup     //释放WinSock引用
 
      socket         //创建Socket,对服务器来说,就是创建侦听Socket
      bind           //绑定侦听套接字到本机
      listen         //开始侦听
      accept         //接受客户端连接
      recv           //从套接字接受数据
      send           //向套结字发送数据
      closesocket    //关闭套接字
 
 
      getpeername    //获取套接字的客户端的TSockAddr结构
      inet_ntoa      //把IPv4网络地址转成点分地址
      ntohs          //网络字节序转换成主机字节序
      htons          //把主机字节序转成网络字节序
 
-------------------------------------------------------------------*)
 
uses
 WinSock, System, SysUtils, StrUtils, ScktComp, Windows;
 
const
 DEF_BUF = 4095;
 
var
 iPort : Integer = 2004;
 bEcho : Boolean = True;
 iCount : Integer = 0;
 
procedure Useage;
begin
 WriteLn(‘usage: WinSockSvr [-p:x] [-o]‘);
 WriteLn(‘       -p:x Port number to listen on‘);
 WriteLn(‘       -n:   Do not echo the receved world‘);
 WriteLn(‘‘);
 Halt;
end;
 
procedure DoArgs;
var
 i : Integer;
 s : string;
 sv : string;
begin
// if ParamCount<1 then
//    Useage;
 for i:=1 to ParamCount do
 begin
    s := ParamStr(i);
    sv := RightStr(s, Length(s)-3);
    if (s[1]=‘-‘) or (s[1]=‘/‘) then
    begin
      try
        case UpCase(s[2]) of
          ‘P‘: iPort := StrToInt(sv);
          ‘N‘: bEcho := False;
        end;
      except
        Useage;
      end;
    end else
      Useage;
 end;
end;
 
function ClientThread(Param: Pointer): Integer; stdcall;
var
 iRet : Integer;
 s : TSocket;
 saPeer : TSockAddr;
 cBuf : array[0..DEF_BUF] of Char;
 sBuf : string;
 iLeft : Integer;
 idx : Integer;
 i : Integer;
begin
 s := TSocket(Param);
 i := SizeOf(saPeer);
 getpeername(s, saPeer, i);
 
 while True do
 begin
    iRet := recv(s, cBuf, DEF_BUF+1, 0);
    if iRet=0 then
    begin
      WriteLn(Format(‘Client(%s:%d) is close gracefully‘, [inet_ntoa(saPeer.sin_addr), saPeer.sin_port]));
      closesocket(s);
      DEC(iCount);
      Break;
    end;
    if iRet=SOCKET_ERROR then
    begin
      WriteLn(‘recv() function failed, Error code‘, WSAGetLastError);
      closesocket(s);
      DEC(iCount);
      Break;
    end else
    begin
      cBuf[iRet] := #0;
      Write(Format(‘Get message from %s:%d ‘, [inet_ntoa(saPeer.sin_addr), saPeer.sin_port]));
      SetLength(sBuf, iRet);
      sBuf := cBuf;
      WriteLn(sBuf);
    end;
    //发送反馈
    if bEcho then
    begin
      sBuf := ‘You sent message: ‘+ sBuf;
      iLeft := Length(sBuf)+1; //加1是为了多加个换行符
      for i:=0 to iLeft-2 do
      begin
        cBuf[i] := sBuf[i+1];
      end;
      cBuf[iLeft-1] := #10;
      idx := 0;
      
      while (iLeft>0) do
      begin
        iRet := send(s, cBuf[idx], iLeft, 0);
        if iRet=0 then
        begin
          WriteLn(Format(‘Client(%s:%d) is close gracefully‘, [inet_ntoa(saPeer.sin_addr), saPeer.sin_port]));
          closesocket(s);
          DEC(iCount);
          Break;
        end;
        if iRet=SOCKET_ERROR then
        begin
          WriteLn(‘send() function failed, Error code‘, WSAGetLastError);
          closesocket(s);
          DEC(iCount);
          Break;
        end;
        iLeft := iLeft - iRet;
        idx := idx + iRet;
      end;
    end;
 end;
 Result := 1;
end;
 
var
 wsd : TWSAData;
 saSrv : TSockAddr;
 saClt : TSockAddr;
 scktListen : TSocket;
 scktClient : TSocket;
 iLen : Integer;
 hThread : THandle;
 dwThreadId : DWORD;
 
begin
 DoArgs;
 //初始化WinSock,版本号2.2
 if (WSAStartup($0202, wsd)<>0) then
 begin
    WriteLn(‘Fail to load WinSock2.2‘);
    Exit;
 end else
    WriteLn(‘Step 1: Load WinSock Succ, Current Version: ‘, IntToHex(wsd.wHighVersion,4));
 
 //创建侦听Socket
 scktListen := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 if scktListen=INVALID_SOCKET then
 begin
    WriteLn(‘Fail to create a listen socket, Error code: ‘, WSAGetLastError);
    WSACleanup;
    Exit;
 end else
    WriteLn(‘Step 2: Creat listen socket Succ‘);
 
 //绑定Listen Socket到本机
 saSrv.sin_family := AF_INET;
 saSrv.sin_port := htons(iPort);
 saSrv.sin_addr.S_addr := htonl(INADDR_ANY);
 if bind(scktListen, saSrv, SizeOf(saSrv))=SOCKET_ERROR then
 begin
    WriteLn(‘Fail to bind the listen socket, Error code: ‘, WSAGetLastError);
    Closesocket(scktListen);
    WSACleanup;
    Exit;
 end else
    WriteLn(‘Step 3: Bind Succ, Listen port is ‘ , saSrv.sin_port );
 
 //开始侦听
 if listen(scktListen, SOMAXCONN)=SOCKET_ERROR then
 begin
    WriteLn(‘Fail to listen, Error code: ‘, WSAGetLastError);
    Closesocket(scktListen);
    WSACleanup;
    Exit;
 end else
    WriteLn(‘Step 4: Listening......‘);
 
 WriteLn(‘‘);
 
 while True do
 begin
    //接受客户端连接
    iLen := SizeOf(saClt);
    scktClient := accept(scktListen, @saClt, @iLen);
    if scktClient=INVALID_SOCKET then
    begin
      WriteLn(‘Fail to accept a client connect, Error code: ‘, WSAGetLastError);
      Break;
    end else
      WriteLn(‘Accept client: ‘, inet_ntoa(saClt.sin_addr), ‘:‘, ntohs(saClt.sin_port));
    //创建客户端通讯线程
    hThread := CreateThread(nil, 0, @ClientThread, Pointer(scktClient), 0, dwThreadId);
    if hThread=0 then
    begin
      WriteLn(‘Fail to create client thread, Error code: ‘, GetLastError);
      Break;
    end;
    INC(iCount);
    WriteLn(Format(‘.....................................Count: %d‘,[iCount]));
    CloseHandle(hThread);
 end;
 
 closesocket(scktListen);
 WSACleanup;
end.

http://blog.csdn.net/tercel99/article/details/46689445

时间: 2024-10-12 23:50:57

Indy的TCPServer到底能支持多少个连接的相关文章

MySQL到底能支持多大的数据量?

MySQL是中小型网站普遍使用的数据库之一,然而,很多人并不清楚MySQL到底能支持多大的数据量,再加上某些国内CMS厂商把数据承载量的责任推给它,导致很多不了解MySQL的站长对它产生了很多误解,那么,MySQL的数据量到底能支持多少呢?其实MySQL单表的上限,主要与操作系统支持的最大文件大小有关.我们来看一下官方的介绍. MySQL表最大能达到多少? MySQL 3.22 限制的表大小为4GB.由于在MySQL 3.23 中使用了MyISAM 存储引擎,最大表尺寸增加到了65536TB(2

ThinkPHP学习(三)配置PHP5支持MySQL,连接MySQL数据库

上一篇:ThinkPHP学习(二)理清ThinkPHP的目录结构及访问规则,创建第一个控制器 配置PHP5支持MySQL 打开PHP配置文件“F:\PHP\php5328Win32\php.ini”,查找“extension=php_mysql.dll”, 把前面的分号去掉,保存文件,启动服务: 却提示“PHP startup: Unable to load dynamic library :F:\PHP\php_mysql.dll”错误, 解决办法,打开PHP配置文件“F:\PHP\php53

Mina 在 Spring 中配置同时支持 SSL 加密连接和非 SSL 连接

iOS 的 socket ,那是在很久以前用过,当时编写确实费了一番周折,要忍受 SDK 的不完善. 之后,一直是 Web 服务器支撑着 http 与 https 实现 RestFul 或... 那么好吧,有需要,就行动,重新写一遍,也好过找那三块笔记本硬盘里成堆的工程代码, 目前 SSL 的证书转换学没搞定,需要将 Mina 中使用 keystore 转换成 iOS 可识别的格式,苹果的 APNS 确实使用的不是标准的东西,不能作为参考了, 所以,在研究出来之前,还是得确保链接正常使用,确保业

解决kvm虚机不支持virsh console连接的问题

以下三步解决问题: 在虚拟机中执行如下步骤 echo "ttyS0" >> /etc/securetty 在/etc/grub.conf中添加内容 title CentOS 6 (2.6.32-504.el6.x86_64) root (hd0,0) kernel /boot/vmlinuz-2.6.32-504.el6.x86_64 ro root=UUID=ee25ae67-9474-4364-8b10-0aeb4e8d6758 rd_NO_LUKS  KEYBOARD

简单表的 分页存储过程 (不支持多表连接)

CREATE PROCEDURE [dbo].[P_GridViewPager] ( @recordTotal INT OUTPUT, --输出记录总数 @viewName VARCHAR(800), --表名 @fieldName VARCHAR(800) = '*', --查询字段 @keyName VARCHAR(200) = 'Id', --索引字段 @pageSize INT = 20, --每页记录数 @pageNo INT =1, --当前页 @orderString VARCHA

Android - SimpleAdapter适配器支持的组件及Listview模拟下载

本来这周想写三篇的,结果这第一篇就不知道该如何起笔.语言表达能力真的需要提高啊.其实有好多想写的,最近这几天又接触到了以前听过但是没有去考虑的一些点.这篇的起因曾经做过一道题,我当时很不理解,我看有评论还是很多跟我当时想法一样的,一直没来得及去追究,终于还是放心不下,去看了一下,发现我错了.原题如下: 使用SimpleAdapter作为 ListView的适配器,行布局中支持下列哪些组件? TextView ProgressBar CompoundButton ImageView 当时我毫不犹豫

实现kbmmw web server 支持https

在以前的文章里面介绍过kbmmw 做web server. 前几天红鱼儿非要我给他做一个支持https 的web server. 其实kbmmw 支持https 有好几种方法: 1. 使用isapi 部署到iis 或者是apache 上,使用iis 或apache 的https 功能: 2.通过代理方式,使用使用iis 或apache 的https 功能. 今天通过kbmmw 的原生indy 方式实现一下https 支持. 由于是测试,不可能使用真正的证书服务.要应用到商业使用,要申请商用证书.

Mysql 之 添加innodb支持

在对mysql进行编译安装时,当安装完成后有时会发现不支持innodb存储引擎,这是因为编译安装时缺少支持innodb的参数: --with-plugins=PLUGIN[,PLUGIN..] Plugins to include in mysqld. (default is: none) Must be a configuration name or a comma separated list of plugins. Available configurations are: none ma

高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少

阅读(81374) | 评论(9)收藏16 淘帖1 赞3 JackJiang Lv.9    1 年前 | 前言 曾几何时我们还在寻求网络编程中C10K问题(有关C10K问题请见文章<The C10K problem(英文在线阅读.英文PDF版下载.中文译文)>)的解决方案,但是现在从硬件和操作系统支持来看单台服务器支持上万并发连接已经没有多少挑战性了. 我们先假设单台服务器最多只能支持万级并发连接,其实对绝大多数应用来说已经远远足够了,但是对于一些拥有很大用户基数的互联网公司,往往面临的并发