关于用DELPHI开发服务器软件中的一些经验

 
保存 评价
游戏规则 免费注册 资料下载 关于本站
问题分类 编程问题 非技术题 富翁列表 我的信息 提出问题 在线富翁 富翁日历 笔记列表 我的笔记 写作笔记 全文检索 《专家门诊》
/delphibbs/dispq.asp lid=2342105
问题:关于用DELPHI开发服务器软件中的一些经验 ( 积分:100, 回复:137, 阅读:6014 )
分类:Internet/TCPIP ( 版主:luyear, robertcool )
来自:element, 时间:2003-12-8 20:52:00, ID:2342105[显示:小字体 | 大字体]

总结了一些经验,现贴出于大家讨论。
1、构件的使用
   开始我一直使用Indy,但最近在开发一个100-350并发用户的服务器端时发现了Indy问题,由于用户的访问非常繁重,服务器工作两周后出现了70多的废连接。导致服务器响应变的慢。而且我观察到有时INDY能自动释放这些废连接,有时多到200多都不释放。后来我改DXSock 3.0构件,代码没有作任何改动,服务从6月1日到今天没有重启一次而且没有一个废连接,
   我花了一周看DXSock的代码,改了些错误,也学习了很多,DXSock构成思想很好。它的连接响应线程不是动态创建的,而是你设置多少就一次性创建多少。节省了创建、释放的大量时间。
   所以再开发服务器端软件我推进使用DXSOCK构件。

   2、数据库使用
      很多朋友在讨论多线程中使用数据库的问题,这在网络服务器开发中这是常常需要考虑的问题,很多朋友都采用一个线程建立一个数据库的连接,我认为这种方法在小规模、小并发应用中是可以的,但是一旦负荷上去、并发一多这样就很多问题了,比如资源消耗(数据库)、时间消耗。这种方式我用过,结果是运行一段时间后数据库再也连接不上了。
      我再这方面有两个解决办法:
      a、采用ASTA构件的方法,根据分析负荷一次性创建一个ADOCONNEC连接池,靠一个同步模块来管理,客户端请求-》构造SQL-》让同步管理模块来分配一个ADOCONNECT联机-》执行SQL,返回结果,如果这时ADOCONNECT满,此SQL等待。代码如下:
      b、对于不需要实时返回数据库信息的应用,客户端请求来-》服务器创建SQL语句和一个TAG-》SQL语句送入队列,然后一条一条的执行,TAG返回客户端,过一段时间客户端再通过这个TAG来服务端获得结果。
********************************************队列管理代码***********************
Unit uitQueueManage;

Interface

Uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Contnrs;

Type
  TSyncManage = Class(TObject)
  Protected
    FHandle: THandle;
  Public
    Property Handle: THandle Read FHandle;
    Constructor Create(Const Name: String; InitCount, MaxBuffer: Integer);
    Destructor Destroy; Override;
    Function Release: Boolean; Overload;
    Function WaitFor(TimeOut: Integer): Boolean;
  End;

Type
  PQueueItem = ^TQueueItem;
  TQueueItem = Record
    FRequestCount: Integer;
    FCommand: Integer;
    FDataStr: String;
    FDataInt: Integer;
    FDataBool: Boolean;
  End;
  TProgressEvent = Procedure(Sender: TObject; aQueueItem: TQueueItem) Of Object;
  TQueueManage = Class(TObject)
  Private
    FQueue: TQueue;
    FSyncManage: TSyncManage;
    FOnProgress: TProgressEvent;
    FLock: TRTLCriticalSection;
  Public
    Constructor Create(aQueueName: String; aBuffer: Integer);
    Destructor Destroy; Override;
    Procedure PushQueue(aQueueItem: TQueueItem);
  Published
    Property OnProgress: TProgressEvent Read FOnProgress Write FOnProgress;
  End;

Type
  TQueueThread = Class(TThread)
  Private
    hSyncManage: TSyncManage;
    hOwner: TQueueManage;
    hQueue: TQueue;
    Procedure DoProgress;
  Protected
    Procedure Execute; Override;
  Public
    Constructor Create(aOwner: TQueueManage; aQueue: TQueue; aSyncManage: TSyncManage); Virtual;
  End;

Implementation

//***********************************************************TSyncThread**************************

Constructor TSyncManage.Create(Const Name: String; InitCount, MaxBuffer: Integer);
Begin
  FHandle := CreateSemaphore(Nil, InitCount, MaxBuffer, Pchar(Name));
  If FHandle = 0 Then abort;
End;

Destructor TSyncManage.Destroy;
Begin
  If FHandle <> 0 Then CloseHandle(FHandle);
End;

Function TSyncManage.WaitFor(TimeOut: Integer): Boolean;
Begin
  Result := WaitForSingleObject(FHandle, TimeOut) = WAIT_OBJECT_0;
End;

Function TSyncManage.Release: Boolean;
Begin
  Result := ReleaseSemaphore(FHandle, 1, Nil);
End;

//***********************************************************TQueueThread**************************

Constructor TQueueThread.Create(aOwner: TQueueManage; aQueue: TQueue; aSyncManage: TSyncManage);
Begin
  Inherited Create(True);
  hOwner := aOwner;
  hQueue := aQueue;
  hSyncManage := aSyncManage;
  Priority := tpHigher;
  Resume;
End;

Procedure TQueueThread.Execute;
Begin
  While Not Terminated Do
  Begin
    hSyncManage.WaitFor(Integer(INFINITE)); //无限等
    If Terminated Then Exit;
    DoProgress;
  End;
End;

Procedure TQueueThread.DoProgress;
Var
  mQueueItem: PQueueItem;
Begin
  mQueueItem := hQueue.Pop;
  If Assigned(hOwner.FOnProgress) Then hOwner.FOnProgress(hOwner, mQueueItem^);
  Dispose(mQueueItem);
End;

//***********************************************************TQueueManage*************************

Var
  FQueueThread: TQueueThread;

Constructor TQueueManage.Create(aQueueName: String; aBuffer: Integer);
Begin
  Inherited Create;
  InitializeCriticalSection(FLock);
  FQueue := TObjectQueue.Create;
  FSyncManage := TSyncManage.Create(aQueueName, 0, aBuffer); //缓冲区大小
  FQueueThread := TQueueThread.Create(Self, FQueue, FSyncManage);
End;

Destructor TQueueManage.Destroy;
Begin
  EnterCriticalSection(FLock);
  Try
    FQueueThread.Terminate;
    FSyncManage.Release;
    FreeAndNil(FQueueThread);
    FreeAndNil(FSyncManage);
    FreeAndNil(FQueue);
    Inherited Destroy;
  Finally
    LeaveCriticalSection(FLock);
    DeleteCriticalSection(FLock);
  End;
End;

Procedure TQueueManage.PushQueue(aQueueItem: TQueueItem);
Var
  mQueueItem: PQueueItem;
Begin
  New(mQueueItem);
  mQueueItem^ := aQueueItem;
  EnterCriticalSection(FLock);
  FQueue.Push(mQueueItem);
  LeaveCriticalSection(FLock);
  FSyncManage.Release;
End;

End.
********************************************队列管理代码***********************
ADO连接同步管理我稍后放上来,欢迎大家讨论。  

来自: chenshaizi, 时间:2003-12-8 20:58:00, ID:2342117
学习ing  

来自: guilinxie, 时间:2003-12-8 21:12:00, ID:2342139
这样的贴子很有学习意义。
UP  

来自: fjjb, 时间:2003-12-8 22:03:00, ID:2342226
学习,期待ADO连接同步管理  

来自: tongdanfeng, 时间:2003-12-9 9:16:00, ID:2342528
什么地方有DXSock .30下载?  

来自: 唐佐平, 时间:2003-12-9 9:28:00, ID:2342571
很好,谢谢了!  

来自: QSmile, 时间:2003-12-9 9:52:00, ID:2342663
什么地方有DXSock .30下载?  

楼主真他妈是个好人。
我选楼主为 2003年大富翁杰出青年  

来自: strgold, 时间:2003-12-9 10:22:00, ID:2342766
kl  

来自: kkg, 时间:2003-12-9 10:46:00, ID:2342839
顶///  

来自: 张无忌, 时间:2003-12-9 12:37:00, ID:2343217
呵呵,200~300个连接压力不够哦,DXSocket也就一般,
 而且DELPHI自己的TQueue类效率很低,我自己改写了一个速度比他快8倍,看来
做服务器程序最好都自己从头写,用现成的始终不是最好的解决方法,而且用别
人的破戒的东西有偷盗之嫌。  

来自: lynu, 时间:2003-12-9 12:45:00, ID:2343250
很不错.
当然大一点的系统,建议还是不要用长连接,这样多数系统能服务的客户端就可以高出很多,一般的系统并发机率可能只有5-15%,我想很少会有系统超过30%以上.不过非长连接的话就要考虑维护会话状态的成本.
indy中大量使用类似如下的代码
  ms := TMemorystream.create;
  ms.write(....)...
这些是很没效率的,如果不为TMemorystream一次性分配足够空间,那么TMemorystream在分配的空间不够用的时候,就需要重新分配,这是一个极没效率的操作.频繁的重新分配,拷贝原数据等,使得TMemorystream在某些时间甚至比硬盘读写都要慢几倍.所以用indy的话就要分析一下系统的实际情况了.

  

来自: element, 时间:2003-12-9 12:47:00, ID:2343256
一切都为了最求快,一切都要自己写,那就失去使用DELPHI的意义了。
服务器实际负荷=并发数*4.3。  

来自: pingshx, 时间:2003-12-9 12:47:00, ID:2343258
楼上的你能否你写的Query给我学习,pingshx@163.com ,THX
楼住能否发你用的DXSocket给我,THX!  

来自: 张无忌, 时间:2003-12-9 12:49:00, ID:2343262
DELPHI一样可以从头来做的,只要你有耐心。  

来自: 张无忌, 时间:2003-12-9 12:51:00, ID:2343268
element:
   你这话我听很多用VC的这么说,他们的意思就是DELPHI只能做一般的普通应用,
难的都要VC做。  

来自: element, 时间:2003-12-9 12:51:00, ID:2343269
ADO连接同步管理代码,我是从应用中剥离出来的,对不起不能提供全部。供大家讨论
Unit uitSyncConnect;

Interface

Uses
  Windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Contnrs,
  Activex,
  ADODB,
  DB;

Const
  csMaxQueueThread = 9;

Type
  TSyncManage = Class(TObject)
  Protected
    FHandle: THandle;
  Public
    Property Handle: THandle Read FHandle;
    Constructor Create(Const Name: String; InitCount, MaxBuffer: Integer);
    Destructor Destroy; Override;
    Function Release: Boolean; Overload;
    Function WaitFor(TimeOut: Integer): Boolean;
  End;

Type
  PQueueItem = ^TQueueItem;
  TQueueItem = Record
    rThreadTag: Integer;
    rEvent: THandle;
    rADOQuery: TADOQuery;
    rIsCommand: Boolean;
    rIsError: Boolean;
    rMessage: String[255];
  End;
  TSyncConnect = Class(TObject)
  Private
    FThreadCount: Integer;
    FQueue: TQueue;
    FSyncManage: TSyncManage;
    FLock: TRTLCriticalSection;
  Public
    Constructor Create(Const aQueueName: String; Const aConnectStr: String; Const aBuffer: Integer = 1024; Const aThreadCount: Integer = 5);
    Destructor Destroy; Override;
    Procedure PushQueue(Var aQueueItem: TQueueItem);
  Published

  End;

Type
  TQueueThread = Class(TThread)
  Private
    hSyncManage: TSyncManage;
    hQueue: TQueue;
    hTag: Integer;
    hADOConnect: TADOConnection;
  Protected
    Procedure Execute; Override;
  Public
    Constructor Create(Const aConnStr: String; Const aTag: Integer; aQueue: TQueue; aSyncManage: TSyncManage); Virtual;
    Destructor Destroy; Override;
  End;

Implementation

//***********************************************************TSyncThread**************************

Constructor TSyncManage.Create(Const Name: String; InitCount, MaxBuffer: Integer);
Begin
  FHandle := CreateSemaphore(Nil, InitCount, MaxBuffer, PChar(Name));
  If FHandle = 0 Then abort;
End;

Destructor TSyncManage.Destroy;
Begin
  If FHandle <> 0 Then CloseHandle(FHandle);
End;

Function TSyncManage.WaitFor(TimeOut: Integer): Boolean;
Begin
  Result := WaitForSingleObject(FHandle, TimeOut) = WAIT_OBJECT_0;
End;

Function TSyncManage.Release: Boolean;
Begin
  Result := ReleaseSemaphore(FHandle, 1, Nil);
End;

//***********************************************************TQueueThread**************************

Constructor TQueueThread.Create(Const aConnStr: String; Const aTag: Integer; aQueue: TQueue; aSyncManage: TSyncManage);
Begin
  Inherited Create(True);
  hQueue := aQueue;
  hSyncManage := aSyncManage;
  Priority := tpHigher;
  hADOConnect := TADOConnection.Create(Nil);
  hADOConnect.ConnectionString := aConnStr;
  hTag := aTag;
  Resume;
End;

Destructor TQueueThread.Destroy;
Begin
  Terminate;
  WaitFor;
  FreeAndNil(hADOConnect);
  Inherited Destroy;
End;

Procedure TQueueThread.Execute;
Var
  mQueueItem: PQueueItem;
Begin
  While Not Terminated Do
  Begin
    hSyncManage.WaitFor(Integer(INFINITE)); //ÎÞÏÞµÈ
    If Terminated Then Exit;
    If hQueue.Count <= 0 Then Exit;
    mQueueItem := hQueue.Pop;
    mQueueItem.rThreadTag := hTag;
    mQueueItem.rIsError := False;
    CoInitialize(Nil);
    If Assigned(mQueueItem.rADOQuery) Then
    Begin
      Try
        mQueueItem.rADOQuery.Connection := hADOConnect;
        If mQueueItem.rIsCommand Then
          mQueueItem.rADOQuery.ExecSQL
        Else
          mQueueItem.rADOQuery.Open;
      Except
        On E: Exception Do
        Begin
          mQueueItem.rIsError := True;
          mQueueItem.rMessage := E.Message;
        End;
      End;
    End;
    CoUninitialize;
    SetEvent(mQueueItem^.rEvent);
  End;
End;

//***********************************************************TSyncConnect*************************

Var
  FQueueThread: Array[0..csMaxQueueThread] Of TQueueThread;

Constructor TSyncConnect.Create(Const aQueueName: String; Const aConnectStr: String; Const aBuffer: Integer = 1024; Const aThreadCount: Integer = 5);
Var
  i: Integer;
Begin
  Inherited Create;
  InitializeCriticalSection(FLock);
  FQueue := TObjectQueue.Create;
  FSyncManage := TSyncManage.Create(aQueueName, 0, aBuffer);
  If aThreadCount - 1 > csMaxQueueThread Then FThreadCount := csMaxQueueThread Else FThreadCount := aThreadCount - 1;
  For i := 0 To FThreadCount Do
    FQueueThread[i] := TQueueThread.Create(aConnectStr, i, FQueue, FSyncManage);
End;

Destructor TSyncConnect.Destroy;
Var
  i: Integer;
Begin
  EnterCriticalSection(FLock);
  Try
    For i := 0 To FThreadCount Do
    Begin
      FQueueThread[i].Terminate;
      FSyncManage.Release;
      FreeAndNil(FQueueThread[i]);
    End;
    FreeAndNil(FSyncManage);
    FreeAndNil(FQueue);
  Finally
    LeaveCriticalSection(FLock);
    DeleteCriticalSection(FLock);
    Inherited Destroy;
  End;
End;

Procedure TSyncConnect.PushQueue(Var aQueueItem: TQueueItem);
Var
  mQueueItem: PQueueItem;
Begin
  New(mQueueItem);
  mQueueItem^ := aQueueItem;
  mQueueItem^.rEvent := CreateEvent(Nil, False, False, Nil);
  EnterCriticalSection(FLock);
  FQueue.Push(mQueueItem);
  LeaveCriticalSection(FLock);
  FSyncManage.Release;
  WaitForSingleObject(mQueueItem^.rEvent, 60000);
  ResetEvent(mQueueItem^.rEvent);
  CloseHandle(mQueueItem^.rEvent);
  aQueueItem := mQueueItem^;
  Dispose(mQueueItem);
End;

End.  

来自: 张无忌, 时间:2003-12-9 12:56:00, ID:2343281
我感觉你的代码里有很多的New Dispose,很多地方改缓冲的资源没有缓冲,需要提高一些效率。  

来自: element, 时间:2003-12-9 13:00:00, ID:2343287
张无忌:
   你好,首先我要声明我是一个忠实的DELPHI使用者,我就是看到很多VC的论坛在讨论服务器技术,所以我才想我什么DELPHI不能写出同样的系统呢?
   我希望这只是个开端,希望大家多多讨论DELPHI这方面的应用。
   下一步我还想写一些关于DELPHI在基础协议方面的文字,
   比如:
   Radius Server的编写
   802.1x协议的DELPHI 的实现
   PCANDIS的DELPHI封装API等等  

来自: element, 时间:2003-12-9 13:01:00, ID:2343291
请张无忌兄多多指教。  

来自: 张无忌, 时间:2003-12-9 13:05:00, ID:2343299
Procedure TSyncConnect.PushQueue(Var aQueueItem: TQueueItem);
Var
 mQueueItem: PQueueItem;
Begin
 New(mQueueItem);
 mQueueItem^ := aQueueItem;
 mQueueItem^.rEvent := CreateEvent(Nil, False, False, Nil);
 EnterCriticalSection(FLock);
 FQueue.Push(mQueueItem);
 LeaveCriticalSection(FLock);
 FSyncManage.Release;
 WaitForSingleObject(mQueueItem^.rEvent, 60000);
 ResetEvent(mQueueItem^.rEvent);
 CloseHandle(mQueueItem^.rEvent);
 aQueueItem := mQueueItem^;
 Dispose(mQueueItem);
End;
//这个里面你可以一次创建足够的队列结构个数,比如队列最大长度1000个结构,循环使用
这些结构。1000个足够了,我自己的服务器处理并发1000个连接也没有到这个数,我测试过最大128个。频繁的调用New Dispose会找成内存脆片,。不利于稳定性
Procedure TQueueThread.Execute;
Var
 mQueueItem: PQueueItem;
Begin
  CoInitialize(Nil);//放在这,不用每次解析请求都要初始化,同时我感觉你这样的ADO处理可能有点问题

 While Not Terminated Do
 Begin
   hSyncManage.WaitFor(Integer(INFINITE)); //ÎÞÏÞµÈ
   If Terminated Then Exit;
   If hQueue.Count <= 0 Then Exit;
   mQueueItem := hQueue.Pop;
   mQueueItem.rThreadTag := hTag;
   mQueueItem.rIsError := False;
 //  CoInitialize(Nil);

  

来自: yu_ting, 时间:2003-12-9 13:08:00, ID:2343312
收藏  

来自: 张无忌, 时间:2003-12-9 13:10:00, ID:2343321
不是我鸡蛋里挑骨头,我认为这个方面大家可以多交流,专门讨论控件使用不应该成
大富翁的主流。  

来自: element, 时间:2003-12-9 13:13:00, ID:2343333
我用意你的观点。  

来自: 张无忌, 时间:2003-12-9 13:15:00, ID:2343339
总之我的观点是服务器程序开发是能开的内存最好一次都开上,循环使用,尽量避免
用string和动态分配内存,能缓冲的资源尽量缓冲。  

来自: delphifaq, 时间:2003-12-9 13:21:00, ID:2343359
好  

来自: lynu, 时间:2003-12-11 9:38:00, ID:2346824
 张无已说的尽量少用字符串(String)我想应该说的是尽量少用String进行连接运算,比如大量的String + String,因为String是自管理内存的数据类型,本质是动态数组,每次使用一个新String时其实就会另外分配一次空间,只是其在引用为0自动释放而已,大量的字符串连接会产生大量的内存分配/释放操作,象String+String我认为至少要分配三次内存.就极可能象
张无已说的产生大量内存碎片.
 但我非常喜欢String的另类用法,从String的结构来看,它前面包含了引用计数和长度字段,
用来分配内存和操作内存数据是很适合的,我经常这样使用.
 SetLength(S,1024);
 然后就可以方便地以各种方式操作S了,包括以指针方式访问,以数组方式访问,以String,pchar指针等,如
pbyte(Integer(S)+100)^ = $F3;
Pbytearray(s)[i] :=  $F3;
S[100] := #31;
MemoryStream.WriteBuffer(Pointer(S)^,1024);
 可以说是方便之极,要写入的数据根本不一定限于字符串.

 优点,String内部应该也是用GetMem分配内存,而String可以方便地分配内存和操作数据,万一忘记SetLength(S,0),字符串也可以自己释放内存(使用对象的地方就实现接口,以接口操作对象也有一定自动回收能力).使用的要点是是一次性分配足够长度.避免出现重新分配大小,另外尽量少用字符串连接运算等.
 
  

来自: 张无忌, 时间:2003-12-11 14:09:00, ID:2347645
to lynu:
这里讨论的是服务器软件的编码,不是做个人软件,所以很多因素考虑的比较仔细。
DELPHI对string类型的一些操作进行了类似操作符号重载的控制,
比如 str:string;
setlength(str,12);
str._addref; //编译器内部做了处理
try
finally
str._Release//减少引用记数,
end;
这么来看,在服务器的代码里多了很多垃圾代码,影响服务器性能,所以string在Client部分是没有什么的,但是在服务器在大规模并发的时候消耗的时间可不是单用户时的那么小的消耗。同理,对于动态数组也不适合在服务器的编码理出现。  

来自: lynu, 时间:2003-12-12 12:54:00, ID:2349887
 呵呵,当然道理是这样的了,象画一个点0.001s与0.0001s感觉没分别,画一个屏的点差别就是很大的数字了,这个当然大家都知道.
  举例说,有一段代码需要在多个地方调用,多数时候大家都会写一个公用函数,但函数调用时的入栈出栈也是需要执行一些指令的,特别是调用次数很大的情况,是很不菲的成本.究竟要不要写一个公用函数,我觉得就不是简单的结论了.
  再如象OOP中的类,肯定会有额外的代码,以及"多余"的内存占用,起码vcl组件是基本上不要用的,属性包括published的事件,不但RTTI占用资源,使用的效率也会有问题...
  个人觉得有些多余的代码也是没有办法的,如果一定要过于追求这个,那最后肯定会得出只
能用汇编的结论.因为用高级语言,或多或少就会产生一些"多余"的代码,我认为如何把握,最终还是要根据实际情况分析效益与代价.
  我们用delphi,主要原因肯定包括delphi的快速高效开发,非常好用的VCL组件框架等,但这些肯定也一样有代价.但是我们继续在用,就是因为这个代价换来的效益值.如果不值的时候,我想我们就会用一个"值"的方案.
  

来自: 张无忌, 时间:2003-12-12 13:02:00, ID:2349924
lynu:
  大家在这讨论服务器软件,不是讨论普通软件的编码,所以你上面的东西基本都是没用的
都是空谈,在服务器的编码里不太适合用动态分配的数据类型,不管你说多大的帽子,找出
N多理由还是不适合。就想搞算法方面的开发的时候没用人用oop方式,你看acm比赛谁用
C++的开发方式,不适合就是不适合。我们做的就是精量减少在性能方面的消耗,不是走极端,就像你说的用汇编,即使只要编译器优化的好,少用一些消耗性能比较高的编码方式
经过优化出来的代码不一定比纯汇编的代码效率低。你上面所说的我上面早就说过了,只
适合写Client程序。  

来自: yanyandt2, 时间:2003-12-12 13:23:00, ID:2349965
说了这么多,谁能贡献一些网络编程的基础教程啊?
在书店的教程都是教你怎么用组件的。

来自:张无忌, 时间:2003-12-9 13:10:00, ID:2343321
不是我鸡蛋里挑骨头,我认为这个方面大家可以多交流,专门讨论控件使用不应该成
大富翁的主流。

这个是同意的,可是大部分人对这个方面还是不懂,关键是各位大侠能带领
小弟们入门才对,这样dfw才能越来越好,越来越吸引人  

来自: lynu, 时间:2003-12-12 13:44:00, ID:2350027
 张无已水平很高,delphibbs上大家都是知道了,没有人怀疑.我个人也是很佩服的.
 但是,比如这位用DXSock做服务器开发的楼主,他就未必一定需要自己从最底层开始写个框架完成TCP/IP通讯服务器,因为DXSock可以满足他的需求.自己从头写当然可以获得一些性能的提升,但代价就不知多少了.
 其实我用String的理由我前面说过了,我个人喜欢用String来分配和操作内存,也尽量在服务器端使用接口,因为我做的系统都不大(大的也不用Delphi来写服务器),而我一直就非常喜欢Java特有的拉圾回收机制,以为是服务器端开发中很有用的.虽然Delphi没有这种机制(或者需要自己实现,我想可能有可能用特殊线程来实现,但我想没有多少人愿意这样做,起码到现在,没有看到这样的框架),但使用动态数组和接口部分达到一些目的.
  我跑题了,不说了.另外我个人还提醒一下,做比较大一点的系统,好象用Java方案的比较多.Java超级吃内存的东东,还需要虚拟机,很多用Java做的系统速度也比较慢.但用的系统就是多,为什么?因为有很多现成的框架和产品供我们使用,这就是权衡成本与效益后的结果.
 [:D][:D][:D]不要扔砖头,其实我一直也在研究适合中小分布式Internet应用的系统框架,因为我个人十分看好这类应用以后的前景.不过我就从不想什么都自己从头写.
  

来自: EdwinYeah, 时间:2003-12-12 13:58:00, ID:2350068
:)  

来自: qince, 时间:2003-12-12 14:00:00, ID:2350078
学习ing
  

来自: 张无忌, 时间:2003-12-12 14:14:00, ID:2350133
TO :lynu
这里是讨论DELPHI开发,呵呵,不是讨论JAVA开发的,拿JAVA做例子不太适合哦
而且DELPHI没有成熟的框架做这个方面的开发,所以在这个方面开发的时候当然
要尽量避免使用一些比较影响性能的东西东西。而不是把DELPHI当JAVA用,什么
方便用什么,我以前用string当缓冲,在500多个并发连接大压力测试的时候,时
间不长,就出内存异常了,Delphi的动态内存管理是不能和JAVA的,你看GetMem.inc
就知道了,写的太简单了,根本不能和Java的复杂的垃圾回收机制做比较,两者
差别太大所以在服务器里进行自己的内存管理是很有必要的。  

来自: fly2003, 时间:2003-12-12 15:03:00, ID:2350284
张sir,高手!还有搂住叶可以,当然还有许多,你们是我的偶像!谢谢你们的东西让我学习。  

来自: wfzha, 时间:2003-12-12 15:18:00, ID:2350324
欢迎继续讨论,我来听课[:D]  

来自: softdog, 时间:2003-12-12 15:47:00, ID:2350400
看大家讨论得热闹,也发个帖子来凑凑数![:D]
很同意张无忌的说法,写服务器类程序的时候能不用vcl就不用(不是说TButton之类),一些关键业务的处理还是从底层开始写为好。
说个我们项目中碰到的例子:
一个全省性的大项目,基于分布式C/S构架,各地的数据通过专网实时传输到业务处理中心,传输本身的业务很简单,就是把各地数据库中的记录打包,然后通过socket传到中心,再插库。以前服务端的插库都是用BDE(oracle数据库),小数据量的时候还可以接收,但随着客户端的增多,数据量的增大,问题越来越明显:插库速度越来越慢,经常报内存读写错等等。
这次程序升级,我们把服务端做了这样的改造:
1、所有客户端传输过来的数据先存在本地的一个文件队列中,再从文件队列中取值进行插库,这样就大大增加了数据吞吐量。
2、扔掉BDE,直接使用oci插库,这个方法极大地提高了效率,原来BDE插库的峰值速率大概是50r/s左右,现在达到了200r/s左右(30多个字段的表,有唯一性索引),最重要的是稳定性极大的提高,运行到现在没有出现过内存访问错。
3、去掉所有动态内存分配的东西,队列服务器中(包括插库)没有一句new、getmem之类的语句,连string几乎都没有,全部采用array.

另外说个题外话,就是“通用”和“效率”的矛盾,我个人感觉在写服务器类的程序时不要考虑通用性,不要为了所谓的升级维护方便去增加一些没用的东西,也尽量少用COM这些东西,因为我觉得COM这个东西效率不高,而且稳定性也值得怀疑,关键代码中最好连类都不要去用。还有就是服务器类的程序也不必考虑界面好看不好看的问题,最好是什么界面也没有。

  

来自: smiledayly, 时间:2003-12-12 20:49:00, ID:2351044
studying  

来自: ego, 时间:2003-12-12 22:03:00, ID:2351120
softdog,鼓掌!说得精彩!
lynu的string另类用法也很有意思。
无忌兄,你也详细点吧 [:D]  

来自: lich, 时间:2003-12-13 13:28:00, ID:2351832
>>我个人喜欢用String来分配和操作内存,也尽量在服务器端使用接口
实际上Delphi的字符串String类型比C_String有更高的效率,
而且很"傻瓜", 使用方便,
在多线程中当作共享变量也没有问题的,
但是,还是要同步对它的访问,否则,结果是不可预料的

张无忌说的使用字符串出错, 我觉得是使用的方法不对,
而不应该在证实Delphi的字符串的稳定性有问题之前,就把责任归于它的身上  

来自: 张无忌, 时间:2003-12-13 13:49:00, ID:2351880
至于为什么不使用string的原因我前面已经说过了,这个softdog也是同意的,至于你说
我是建议你做一些大规模的压力测试才知道string的问题,呵呵,先把帖子读仔细了,再
回帖子把  

来自: lynu, 时间:2003-12-13 17:48:00, ID:2352215
 张无忌做的是并发负荷很大的系统(听闻1000以上,换算到客户端数量肯定在几万以上,而weblogic这样昂贵的产品,它的servlet容器,单个servlet承受数千发访问都是很需要考量考量的,至于EJB就不太清楚),我想他考虑的出发点就比我们高得多.他考虑的方式,对他作的系统来说,的确是十分要注重的,甚至是绝对要那样做的.
 使用String肯定会有代价的,这个应该不用怀疑,不说他执行的增加/删除引用计数这样额外的执行代码,他本身就多占了一个引用计数(8字节)和一个长度(4字节),算起来一个字符串起码就多分配了12字节.这个是不是很浪费?说不好的,就好比小马过河,老鼠说河很深,老说河很浅,究竟是深是浅,要相对小马来说才正确.同样是做服务器开发,有的很大的系统,有的是很相对较小,有的系统硬件和普通架构足够应付,而十分注重业务实现,有的业务相对简单,但却极重系统的稳定性,可能考虑的时候就各有出发点.
 又说了一通废话,不过我现在觉得,程序员代码水平固然是很重要,另一方面的水平则更重要,就是分析代价和所得,说穿了吗,就是用最少的力气谋取最大的利益,咱们写程序,无论说得多好听,爱好写程序啊,为了理想啊,为中国软件崛起啊,都是假话,其实都是谋生赚钱.所以具体到软件架构,也肯定要考虑一下.也不是说一定要都用现成的东东或者简单的东东,服务器系统程序除了开发成本,也有维护成本等等.比如大家都知道用.net开发,开发速度快,开发成本就很低,但用这个架构的东东目前就比较少,其中少不了维护成本的考虑.
  我个人没有做超大系统的机会,所以我考虑问题就远不如张无已要求那么高.如果我有机会做大系统,我一定向张无已多多讨教学习.
  我个人看贴子,并不会随便反对别人的意见,因为我觉得每个人提出的意见,都肯定是有过思考的,这个贴子中每个人的贴子都有道理,都有学习的地方,只是大家出发点并不尽相同.
  大家继续,我好多学习学习,特别是我没有条件亲手去体会的东东....
  

来自: lynu, 时间:2003-12-13 17:56:00, ID:2352232
  另外推荐用delphi做中小分布式系统的delphi朋友有空试试remobjects,我目前正在试玩,感觉有点小意思,他的服务器端支持isapi dll,cgi,apache dso,dxsock /indy独立服务器,windows messages...
 Asta有一个bug,他的客户端数据集ApplyUpdate有个逻辑问题,在生成的sql未更新到原记录时(where匹配不到原记录,也就是原记录已被修改),客户端仅仅刷新记录,不抛发异常,关键应用的话是不能容忍的.而datasnap可以准确地抛发一个Data update by another异常.
 其实我知道我玩的都是些旁门左道,不是主流,玩这些是为个人用的.我在公司用的则是java做服务器,目前应用是部署在weblogic上,客户端是用delphi.使用webservices进行远程调用.有兴趣的朋友可以交流交流.  

来自: iDevCN, 时间:2003-12-14 1:19:00, ID:2352580
不知道各位注意没有,现在大部分的服务器软件都是用VC写的,我不清楚是为什么?
唯一的一个是Serv-U是用Borland的C++写的。
  

来自: 张无忌, 时间:2003-12-14 10:51:00, ID:2352774
我感觉出现这种情况是大部分用DELPHI的开发人员对细节和效率不重视,什么方便用什么
而且大部分习惯用第三方控件,没有一定的低层开发能力所导致的结果,其实还是有一些
服务器软件是用D写的,我看做FOXMAIL的张小龙公司的一些EMAIL服务器就是用D写的,
而且全部都是用API写的,包括一些GDI界面都是API写的,  

来自: yanyandt2, 时间:2003-12-14 11:20:00, ID:2352807
用 indy 写服务器程序,然后运行在 linux 下
是不是也是个方案呢?
因为很难看到windows服务器可以一个月不重起的,
而 linux 却可以一年运行而不重起。  

来自: tseug, 时间:2003-12-14 13:16:00, ID:2352931
http://expert.csdn.net/Expert/topic/2467/2467236.xml?temp=.2988245
这个也是类似的
其实说来说去只要一句话就可以解决, 那就是具体问题具体分析

2003-12-12 20:00:25 张无忌
http://www.delphibbs.com/delphibbs/dispq.asp?LID=2342105

2003-12-12 20:08:22 张无忌
老大,看完没?

2003-12-12 20:07:52 tseug
快了
[↑]

2003-12-12 20:08:41 张无忌
恩,

2003-12-12 20:08:06 tseug
玩了
[↑]

2003-12-12 20:08:53 张无忌
好快

2003-12-12 20:08:35 tseug
说来说去都是一个度的问题
[↑]

2003-12-12 20:09:30 张无忌
,如何是个度的问题?

2003-12-12 20:09:45 tseug
我是做工程的,在工程上进度是一个和质量同样重量的概念,尤其是大系统工程
[↑]

2003-12-12 20:11:35 张无忌
恩,很多时候项目完成速度快不一定能满足设计

2003-12-12 20:11:02 tseug
有的时候为了保证进度, 对于不是很把握的是事情都有备用方案, 对于软件, 有可能花大价钱买商用的
[↑]

2003-12-12 20:12:15 张无忌
恩,看来做一些这个方面的东西还是有好处的,哈,

2003-12-12 20:11:44 tseug
对, 在工程上所以有一个整改和缺陷责任期的概念
[↑]

2003-12-12 20:12:50 张无忌
具体如何,说来听听

2003-12-12 20:13:21 tseug
整改就是在基本功能保证的前提下有一定的修改时间,是更换,或者重新做什么的
[↑]

2003-12-12 20:14:26 张无忌
恩,我们这边也有这个概念

2003-12-12 20:14:08 tseug
缺陷责任期是在项目验收合格之后有1年-2年的免费维护期间
[↑]

2003-12-12 20:15:20 张无忌
恩,这个都是这样的,我们这边是一年的免费维护以后收钱

2003-12-12 20:14:42 tseug
对,这个就是从大的制度上来保证项目成功的方式
[↑]

2003-12-12 20:15:44 张无忌
这个和哪个帖子有什么关系了?

2003-12-12 20:15:26 tseug
所以, 快速的拿出产品或者完成项目是主要的
[↑]

2003-12-12 20:16:41 张无忌
但是东西不定期死机可是比较讨厌的

2003-12-12 20:16:17 tseug
对于不同的人, 技术水平不同, 完成同样的功能会采取不同的手段, 这些都是合理的选择
[↑]

2003-12-12 20:17:12 张无忌
B也对

2003-12-12 20:16:42 tseug
呵呵, 就连有的路由器都有这个毛病
[↑]

2003-12-12 20:17:34 张无忌
哦,具体说来听听

2003-12-12 20:17:13 tseug
其实, 设计本来就是一个折衷的过程
[↑]

2003-12-12 20:18:01 张无忌
恩,也对

2003-12-12 20:17:34 tseug
路由器也是一个小的服务器程序阿
[↑]

2003-12-12 20:18:25 张无忌
也对

2003-12-12 20:18:32 tseug
那么在不确定的情况下的选择只能是尽量往好的地方努力
[↑]

2003-12-12 20:19:32 张无忌
恩,不过稳定的程序难做啊,

2003-12-12 20:19:40 tseug
比如发现自己开发可能会延误进度,影响下一个项目的投标, 那就有可能花更多的钱来买一套
[↑]

2003-12-12 20:19:55 tseug
对, 需要时间,
[↑]

2003-12-12 20:20:35 tseug
自己代码里的问题容易解决,但是某些库函数的问题很麻烦
[↑]

2003-12-12 20:21:32 张无忌
而且需要时间测试,我现在的服务器程序可以保证开一个月不出问题

2003-12-12 20:21:22 tseug
呵呵,利害,
[↑]

2003-12-12 20:22:49 张无忌
我都是用API函数包括字符串处理,压力测试的时候我是1000个并发,

2003-12-12 20:22:45 tseug
恩,不过如果代码不是很复杂可以考虑白盒测试
[↑]

2003-12-12 20:23:59 张无忌
我在一个关键位置用string表示,结果才10分钟就内存异常了,哈

2003-12-12 20:23:47 tseug
不会吧,String没那么脆弱,不是你用错了吧
[↑]

2003-12-12 20:25:04 张无忌
没有我只是用Format函数

2003-12-12 20:24:54 tseug
不会吧,怎么写的?
[↑]

2003-12-12 20:26:17 张无忌
哪个压力很大的啊,1000个客户线程,才半个多小时,就发送了6G数据

2003-12-12 20:26:04 tseug
你的String是局部变量吧?
[↑]

2003-12-12 20:26:55 张无忌
就是
write(F,Format('%d类型消息',[itype]));

2003-12-12 20:26:40 tseug
呵呵,碎片太多了
[↑]

2003-12-12 20:28:27 张无忌
是啊,我后来改用StrFmt就一切OK了

2003-12-12 20:28:39 tseug
呵呵,Format有很多动态分配的东西
[↑]

2003-12-12 20:30:00 张无忌
是啊,里面开始是分配一个String然后在里面有重复分配的东西,  

  

来自: lich, 时间:2003-12-14 20:24:00, ID:2353480
没有发现String类型不稳定或不可靠的地方,
我进行了一个简单的测试,用的就是Format函数
开了40个线程(机子速度有限,开多了会特别慢)
连续运行2小时41分钟(这个是CPU总占用时间),
没有任何错误,
内存使用也没有丝毫的增加,

即使Format函数有问题,也不能说String类型不可靠
也不能因为一个函数内部多次分配释放空间就说这个函数有问题

所以,我认为无忌兄所说的问题太过于武断,
难免会误导大众(毕竟张兄是大富翁颇有名气的人物),
说话还是要注意影响的,

即使有100个人同意你的看法,
但是,我想,你还是要拿出直接的证明来
才容易让人信服啊!
  

来自: 未来黑客, 时间:2004-5-5 13:27:29, ID:2353651
.  

来自: 未来黑客, 时间:2004-5-5 13:28:03, ID:2353655
 .  

来自: gongxingg, 时间:2003-12-14 23:17:00, ID:2353669
麻烦问一下,如何进行大连接测试的?  

来自: fox816, 时间:2003-12-15 0:20:00, ID:2353691
驱动程序、游戏、算法动态库(比如h263)--------------------->vc
数据库、通用软件 ----------------------------------------->delphi
服务器、特别复杂的客户软件-------------------------------->c++builder
web程序或老板要求----------------------------------------->java、c#
第一个学习的语言、加密、速度及接口要求-------------------->汇编

vc的不足:mfc
delphi的不足:api的转换,STL库
c++builder的不足:系统庞大,编译速度稍慢
java、c#的不足:非机器原生代码,不可以自己控制所有事情,不适合通用和系统软件。

  

来自: 张无忌, 时间:2003-12-15 8:29:00, ID:2353775
TO lich:
我当时的测试是在服务器运行了很长时间以后进行的,Format函数的主要问题是容易产生
大量的脆片,你看他的原代码就知道了,我当时的测试是大量并发连接都调用一个Format
函数,这个函数里面有字符串处理和数字显示,同时把这个string写到文件里去,可能是
生成的字符串是变长的,导致产生了大量的脆片最后程序崩溃了。  

来自: 张无忌, 时间:2003-12-15 8:33:00, ID:2353782
同时,在一般情况下Format没有什么问题,我的测试服务器平时开了10多天都没事情的,
每天最多并发也就几十个人,但是压力一大,时间一长就出了问题。  

来自: yanyandt2, 时间:2003-12-15 9:38:00, ID:2353909
我想使用线程池可以避免重复分配资源,那 string 就不会总是重复分配内存了
在 indy 里,我在服务器端的线程里分配很多东西,如果不用线程池,那么就非常慢,
用了线程池,就没有任何问题了。

不好意思,刚刚接触一点网络编程,想通过这个帖子学习一下 :)  

来自: 未来黑客, 时间:2004-5-5 13:29:37, ID:2353915
.   

来自: 张无忌, 时间:2003-12-15 10:21:00, ID:2354071
楼上的,我为什么要告诉你?[:D]  

来自: 未来黑客, 时间:2004-5-5 13:28:37, ID:2355495
.   

来自: app2001, 时间:2003-12-15 17:41:00, ID:2355555
咋的了,这是,说着说着就斗起来了,没意思哩,技术上见真章呗,有啥想不开呢?
憋气的话就想想人家萨达姆,俺们比起他来,要幸福多了呢。  

来自: 张无忌, 时间:2003-12-15 18:54:00, ID:2355704
无知者无惧!
我佩服![:(]  

来自: lich, 时间:2003-12-15 19:02:00, ID:2355716
权利和义务是双方面的
张无忌没有从你这里拿到任何的报酬和好处,
当然也就没有帮助你的义务,
他帮助别人,帮助哪一个人,完全是处于自己的意愿,
一个人如果是诚心的帮助别人,是不求回报的,
但是你没有权利要求一个人必须帮助你
  

来自: ego, 时间:2003-12-15 19:07:00, ID:2355727
前两天看见这张贴的时候,心里很高兴,毕竟DFW上好久没有这么热烈讨论服务器编程的了,于是每天都来顶[:D],结果自从来了只苍蝇后,这张贴恐怕也将到此为止了......  

来自: 未来黑客, 时间:2004-5-5 13:29:05, ID:2356032
 .   

来自: lich, 时间:2003-12-15 22:43:00, ID:2356055
是的,很多事情是没有为什么的,
我做我喜欢做的事情,
做了,不喜欢,还难受的事情,做它干什么

>>能力越大,责任也应该越大。
谁规定的啊? 干吗要听他的
  

来自: 未来黑客, 时间:2003-12-15 23:37:00, ID:2356079
   

来自: 张无忌, 时间:2003-12-16 8:49:00, ID:2356248
我仔细考虑了我那部分出问题的代码,问题出在我写调试日志文件的时候,
而不是主程序里面,主程序里面我都是用array [0..MAXLEN]of char的,
由于输出到日志里为了方便,我采用了TextFile,每次写一条语句我都
先关闭文件,然后再WriteLn(),问题出现在一条关闭文件函数和
WriteLen(TextF,Format(,[]))函数之间,在大压力测试的时候而且每
次的情况都不同,有的时候没有任何提示就自动退出,有的时候报指针
无效。只要我注释掉这条语句就没任何问题.
所以我怀疑有二。
1,DELPHI的文件读写库函数(AssignFile、CloseFile)有问题。
2,Format函数不稳定。
相比较而言,我更相信后者有问题。因为偶以前再做一些程序里,偶尔
也出现一些string出问题的情况,比如在快速点击操作TreeView和
ListView的时候,一跟踪经常就到了一个Item.Caption=''的地方。
而且这个错误不常出现,很讨嫌。  

来自: lich, 时间:2003-12-16 9:06:00, ID:2356305
其实,你写日志文件的时候可以采用共享模式,
允许别人读,不允许别人写,写完之后还需要Flush一下
这样就能用其它文本程序看到最新的内容

而不必要不停的关闭和打开文件的
(很占用时间的)
  

来自: element, 时间:2003-12-16 9:19:00, ID:2356346
我的初衷是讨论技术而非争吵。  

来自: yanyandt2, 时间:2003-12-16 9:39:00, ID:2356423
能力越大,责任也应该越大。
恩,这句话我是赞同的。

来自:yanyandt2, 时间:2003-12-15 9:38:00, ID:2353909 | 编辑
我想使用线程池可以避免重复分配资源,那 string 就不会总是重复分配内存了
在 indy 里,我在服务器端的线程里分配很多东西,如果不用线程池,那么就非常慢,
用了线程池,就没有任何问题了。

两位高手,指点一下啊?难道服务器端使用线程池仍然不能满足需要吗?  

来自: LeeChange, 时间:2003-12-16 9:47:00, ID:2356444
呵呵,又见着无忌跟人斗嘴。[:D]  

来自: 张无忌, 时间:2003-12-16 9:49:00, ID:2356448
to LeeChange:
有交流才有火花吗,我和lich他们是讨论问题。[:D]  

来自: baifeng, 时间:2003-12-16 9:56:00, ID:2356475
cao  

来自: HAZL, 时间:2003-12-16 9:59:00, ID:2356488
to 张无机
你水平也许高,不过你每次发了贴都要损人家两句,这好象不是真正高手的作风。武功高,更要武德高。  

来自: 张无忌, 时间:2003-12-16 10:13:00, ID:2356534
我可不是高手,我想干吗就干吗,我不想考虑别人如何,这个帖子里哪个什么鸟黑客回
的帖子偶根本就不想理他,我回复的也没有骂他的话。这里是自由交流不是商业公司,
我没有必要每个人的帖子都要回复,我没那么多事件也没有哪个精力。我对于实在看不
顺眼的,损几句也没什么,谁叫那些人太恶心了。我是有什么说什么的,不喜欢的你可以
不看,对于真心讨论问题的,我一向是很佩服的。  

来自: mywyn, 时间:2003-12-16 10:41:00, ID:2356633
这麽热闹!我来转帖一篇CSDN的文章,大家可以看一下。不要局限于DELPHI和WIN,关键是
思想!最后,CSDN的回复是由下往上的,看错了顺序你会不知所云的。


这是正在写的一本书的大纲,先发出来,大家提点意见

 

高性能服务器软件开发

 

关于这本书的简单介绍:

注意,在这里我们讨论的高性能服务器软件设计并不是通常意义上的高性能Web程序设计,比如说在J2EE、.NET框架下如何使用线程池提高性能,如何优化ASP,PHP程序,或者如何调整Apache,IIS等Web服务器以获得更好的性能等等。

这里讨论的是真正高性能的从硬件到操作系统底层,然后到IO模型、应用模型的服务器设计,当然,作为一家之言,里面同样充满了误解、偏见和无知,但是在指责我之前,请仔细思考你指责的理由,进行仔细的测试之后把你的完整意见告诉我,我会很感谢这种建设性的指责而不是其他。

 

我们面临的问题

 

目前已经存在,而且将会越来越多的大量网络应用,它们包括:

即时聊天服务器

FTP服务器

基于互联网的媒体应用

大型购物网站

大型门户网站

在线网络游戏

 

而在实际使用的时候,我们经常碰到下面的情况:

连接数过多,网站无法访问。

无法下载文件,或者下载速度非常缓慢

同时连接的数量受到很大限制。

站点非常脆弱,经常受到这样那样的攻击而瘫痪。

 

对于这些情况,通常的解决方案是:

增加硬件的性能

使用服务器集群和负载均衡技术

更大的带宽

 

这些真的需要吗?

我们有没有仔细的思考我们的硬件本身的限制,操作系统的限制,我们的应用程序的限制,真正的瓶颈在哪里?

 

让我们首先来看一看基本的c/s网络应用。

 

基本的客户/服务器网络应用系统:

 

第一类  非基于连接的系统

电子邮件,百万级的,使用的频率有限,同时访问的用户有限,连接的数量受到限制

即时聊天、视频或者其他在线媒体

这些系统有自己的功能限制。

 

基于连接的静态系统

ftp下载

http访问

 

基于连接的动态系统

包括动态页面的Web 站点,动态的内容和用户无关

包括用户的概念以及用户相关数据的系统,经常有一个后台数据库。

包括业务逻辑的系统,例如电子购物站点,信息港,以及最近流行的在线游戏。

 

根本的问题是什么

 

如何以尽可能少的CPU 时间,内存占用,支持更多的网络连接,发送/接收更多的数据。

 

从下往上系统的根本构造包括:

1.         硬件设施:包括CPU 的计算能力,硬盘、总线的带宽,网络设施的吞吐能力。

2.         操作系统:操作系统对并发连接的支持和开销,操作系统的IO开销

3.         网络应用模型:基本的网络IO模型,事件通知的机制

4.         应用模型:结合实际应用的应用系统设计

5.         编码和实现:好的编码和差的实现之间的差别是非常巨大的,在这里我提出一些可能有偏见或者偏激的看法。

 

1,2硬件设施和操作系统的开销比较。

 

我们将1,2结合起来进行比较:

 

选择下面一些基本的硬件平台:

典型的笔记本电脑,P4M平台,100M网卡,512M内存

典型的台式机,P4平台,100M网卡,512内存

典型的服务器,xeon平台,千兆网卡,1G内存

典型的AMD64位服务器,千兆网卡,1G内存

 

软件系统包括:

Windows系统:

Windows2000 Profession

Windows2000 Advance Server

 

自由的Unix类系统:

Linux 2.4内核

Linux 2.6内核

FreeBSD5.1内核

 

主要的性能评估包括:

 

影响服务器性能的操作系统的基本调用评估

l         分配/释放内存的开销

l         创建/中止线程/进程的开销

l         互斥锁的开销(上下文切换的开销)

l         内存映像文件的创建/读写开销

 

操作系统网络性能的基本评估

l         套接字的创建/释放开销

l         套接字的绑定开销

l         建立网络连接的开销

l         发送数据/接收数据的吞吐量

l         一次标准HTTP请求/响应的延时和开销。

 

初步的结果表明,硬件方面,Xeon系统显然超过了通常的笔记本和台式机,而AMD 的64位系统在较低的主频下有非常好的性能表现(我们仍然使用32位软件进行测试)。而操作系统方面,所有的类Unix系统都全面超过Windows平台,而Linux2.6内核是各个平台中表现最好的。

 

网络IO模型的设计和评估:

 

基本概念,操作系统采用何种方式通知应用软件应该去某一个套接字上获取数据或者发送数据。

 

最简单的办法,采用轮询的机制循环检查套接字的状态,在很多时候,这种方式的效率反而最高。

轮询方式具体的应用范围:

 

其次,使用每个连接一个线程的方式,这种方式可行性决定于

操作系统可以使用线程的数量

操作系统线程创建和线程间切换的开销。(初步的测试表明,linux2.6内核每秒可以创建5000以上的线程)

 

两种基本的消息触发方式:

条件触发和边沿触发

常见的select就是条件触发。

 

Window平台:

包括常见的Select模型

event select模型

IO完成端口模型,实际上就是一种边沿触发的机制。

 

Linux平台

基本的select模型,最大的问题在于寻找哪一个socket上面发生的事件(socket的本质是文件句柄,所以数量巨大)

边沿触发:

   2.4内核推荐的实时信号模型

   2.6内核推荐的epoll模型

 

FreeBSD

基本的select模型

边沿触发的kqueue模型

 

性能评估:

基本的测试表明,在同时并发连接数量,每个连接的延时和吞吐量上,Linux 2.6内核都是胜利者,其性能显著超过了2.4内核以及FreeBSD。所有的类Unix系统都将Windows系统远远抛在后面。在各种IO模型的比较下, linux2.6内核的epoll同样成为胜利者,而IO完成端口仍然是最后一名,显然,对易用性和图形性能的要求使得Windows并不适合作为一个服务器端的操作系统。

 

应用系统的模型:

 

基本的Web服务器:

 

1静态页面的提供:

内存映像文件

直接文件发送,减少了文件的数据从核心拷贝到应用层然后再拷贝到核心的开销

在Windows和Linux环境下都有相应的系统调用。

核心服务器,直接在核心完成基本的HTTP服务:

khttpd

 

2动态内容的提供:

基本的硬编码方式提供动态页面

使用简单的应用程序密切相关的脚本语言,

使用通用的脚本语言:

高性能的LUA

高性能但是过于庞大的Java

其它脚本语言PHP,ASP等等。

 

3 数据库系统

本地存储还是通过网络访问数据库服务器?

Oracle、Sybase、SQL Server和其他的数据库服务器

mySQL、mSQL、Postgres等open source数据库服务器

 

本地:

Berkely DB

如果不需要复杂的事务处理和恢复功能,可以使用简单高性能的,基本B *Tree存储机制。

 

4 J2EE和.NET框架,复杂性的好处和代价。

 

 

其他应用服务器:

数据库应用

在线游戏

流媒体播放,RTP和其他并不广为人知的协议

 

下一代应用?P2P和BT

新的思路,带来的好处,当然也会由此带来新的问题:

可能对整个互联网带宽和流量的影响

可能带来的法律问题

 

下面该看看应用系统了,好的设计同样是非常重要的:

真正高性能的程序设计

 

more small, more fast

more simple, more fast

更小的代码更快

更简单的代码更快

 

现代计算机的体系,速度往往取决于CPU cache 的命中情况。

因此,更小、更简单的代码往往会获得更好的性能。

 

l         删除冗余代码,和一般理解相反,放在那里不动的代码即使没有使用也会影响系统的性能。

l         不要低估或者高估编译器的优化,除了代码本身所能提供的信息以外,编译器永远不知道你到底要做什么。

l         不要梦想复用,在代码级重复使用你代码的可能性接近于0

l         层层的封装是效率的杀手

l         动态运行时解析,类型….带来的问题远远大于所承诺的好处。

l         专用、专用再专用,把你的代码限制在一个非常具体的场景中,通用的代码往往意味着低效、潜在的误用和其他错误。

l         不要做期待之外的事情,所有的bonus都是要付出代价的!

l         优化最常见的情况,而不是最糟糕的情况。

l         内存分配和释放是非常昂贵的操作(从时间上,稳定性上都是)

l         不要想象,使用工具观察你的代码,vTune或者gProf等等,只有这样才能发现真正的瓶颈所在。

 

 

 
 

对该文的评论 人气:9545  
      hsia (2003-12-14 16:16:56)  

我同意楼主的看法。 6、7年前,就曾有人说,机器性能在不断提高,不要在乎那一点效率。事实上,到今天,格局仍然依旧。如果不借助pro C,数据库的应用仍然慢如牛车。举一个例子,某省的连通公司,每月计算用户资费,需要3天多的时间,几百万的用户数,涉及到的数据充其量也不会超过10亿条,亏他们能做得出来。数据库仍然是很慢的,于是,所谓3T架构流行起来,似乎加上线程池、加上集群,就能解决问题。殊不知,结构的复杂只能是效率的进一步下降。简单想一下,原来是两个人对话,现在中间加上一个人(翻译),对话的效率是提高了还是降低了?不要美国人说C/S好,大家都C/S;美国人说B/S/D,那么OK大家都来B/S/D。做软件的人,有几个?什么时候?用你自己的头脑思考呢?
 
      chenhaono1 (2003-12-13 14:15:52)  

倘若按照SnowFalcon的 "如果应用是需要需要毫秒级的定时,修改系统调用(在kernel/timer.c中),使其定时单位变为毫秒。也就是相当于让一个进程变成一个分片式的任务调度系统,这样的好处是你可以控制某个任务需要调度多少时间。" 那么程序的运行时间不照样由操作系统控制, 尽管可以设置程序的调度优先级,可以mlock()内存. 但是进程的信号是在内核快返回时检查进程的信号位来实现的, 这样一来就免不了要有上下文的切换. 众所周知进程上下文的切换是昂贵的, 而线程的切换相对开销小. 那仅仅为了在自己的控制下采用上述做法是不合适的.
 
      minzheng (2003-12-12 13:22:24)  

我觉得楼主的测试应该有问题,我用MSDN的例子在我的机器上测试,1500个连接,cpu还是很低的。通讯性能我没有跟Linux比较过,当我个人认为相差应该不大吧。当然如果楼主能够提供个你测试例子的代码,我们大家看看,如何?我对这个帖子的题目很有兴趣,大家可以一起讨论。
 
      davemin (2003-12-12 10:48:18)  

要不大家去改写一下linux内核代码吧。 这样会更理解一些的。
 
      davemin (2003-12-12 10:46:33)  

msn 服务器的架构看看就行了。fuck. 事实为依据, IOCQ 服务器是什么架构的?
 
      SteroWang (2003-12-11 17:46:38)  

诸位有没有采用ACE并使用Proactor模式的例子呢?我粗看了它的源代码,Win32平台的Proactor实现用了IO Compoliation Port,很想知道在程序怎么调用,诸位有的话可不可以把地址贴出来或发给我。先行谢过了!
 
      davidprg (2003-12-11 15:35:20)  

我做的server在普通p4 ,256mb ram下的测试结果tcp的并发达到8000仍然运行良好,压力测试未做;另外我正在开发的一个game server并发过万运行也比较良好,也是p4机器,win2000下,不过我相信linux下可以获得更高性能。
 
      whidy (2003-12-11 14:25:36)  

国内有windows下开发的服务器吗?支持大容量的
 
      wyzegg (2003-12-10 14:10:11)  

是这样的吗,服务器的稳定性呢
 
      biny (2003-12-10 10:33:32)  

呵呵,很欣赏 SnowFalcon 的话,很多话说的太对了 说到心坎里去了,哈哈
 
      cxu123 (2003-12-10 6:28:47)  

Linux中性能最好的FTP服务器 Pureftpd 和 vsftpd 都只为每个FTP连接生成一个线程 =========================================================================== 我写错了,这里应该是每个FTP连接生成一个新进程
 
      cxu123 (2003-12-10 6:21:27)  

建议作者看看 http://www.kegel.com/dkftpbench/的文章,主要讨论FTP Benchmark。 http://www.kegel.com/c10k.html。The C10K problem,主要讨论各种UNIX 和Windows SOCKET编程模式的不同,e-poll,kqueue 和Window IO Complete Port都有提及,不过好像差别没那么大。 LINUX 线程切换速度很快,所以e-poll用处没有那么大。Linux中性能最好的FTP服务器 Pureftpd 和 vsftpd 都只为每个FTP连接生成一个线程.e-poll,kqueue 虽然看着不错,但是有一个不同UNIX平台兼容性的问题,很少有服务器只在一个UNIX平台或版本上工作的,考虑到这个原因大部分开源的服务器程序还是不用e-poll,kqueue。不同版本兼容性是UNIX 不同Socket模型的最大问题。 实际工作中Window IO Complete Port和Linux差别没有那么大W单看indow IO Complete Port很多情况性能更好,但是Windows 操作系统的其他原因最后抵消掉IO Complete Port 的优势。例如Windows Spleep()只能精度只能到毫秒,线程切换和调度也只能到毫秒,这一点很影响效率,而UNIX下可以到纳秒。因为这个原因Windows线程切换开销好像比LINUX进程切换开销大,在Windows里一个连接一个线程肯定是不行的。
 
      cxu123 (2003-12-10 6:20:15)  

建议作者看看 http://www.kegel.com/dkftpbench/的文章,主要讨论FTP Benchmark。 http://www.kegel.com/c10k.html。The C10K problem,主要讨论各种UNIX 和Windows SOCKET编程模式的不同,e-poll,kqueue 和Window IO Complete Port都有提及,不过好像差别没那么大。 LINUX 线程切换速度很快,所以e-poll用处没有那么大。Linux中性能最好的FTP服务器 Pureftpd 和 vsftpd 都只为每个FTP连接生成一个线程.e-poll,kqueue 虽然看着不错,但是有一个不同UNIX平台兼容性的问题,很少有服务器只在一个UNIX平台或版本上工作的,考虑到这个原因大部分开源的服务器程序还是不用e-poll,kqueue。不同版本兼容性是UNIX 不同Socket模型的最大问题。 实际工作中Window IO Complete Port和Linux差别没有那么大W单看indow IO Complete Port很多情况性能更好,但是Windows 操作系统的其他原因最后抵消掉IO Complete Port 的优势。例如Windows Spleep()只能精度只能到毫秒,线程切换和调度也只能到毫秒,这一点很影响效率,而UNIX下可以到纳秒。因为这个原因Windows线程切换开销好像比LINUX进程切换开销大,在Windows里一个连接一个线程肯定是不行的。
 
      xiaha3 (2003-12-9 23:02:24)  

有一个想法;那就是如果应用程序尽量不使用操作系统提供的功能,同样的硬件平台,不同的操作系统上的效率可能一样;同样可以实现高性能
 
      lesstif (2003-12-9 11:38:06)  

我欣赏有研究精神的同志!!!好,其实,我个人也在进行研究,虽然跟你的方向不一样,祝成功。 顺便说一句,如果你写成书出版,我一定买!!!
 
      emilchan6k (2003-12-9 7:54:12)  

SnowFalcon是强人~
 
      CSDN010 (2003-12-8 15:01:47)  

微软系统一直是效率低下,不能登大雅之堂的形象。随着近年来越来越多的国外报道,才知道微软也是电信、军队、银行、政府等的重要软件提供商。下例是Windows2000系统为美国著名计费软件厂商提供超过2500万用户系统容量计费系统的报道,在电信领域微软取得成绩之迅速,不得不令人为之侧目。 全球领先的计费和用户管理软件供应商美国 Portal 软件公司与微软公司日前宣布携手合作,确保世界各地电信运营商把服务、设备迅速地提供给顾客,并进行高效准确的管理和计费。双方将把 Portal 获奖的计费和用户管理软件平台和微软的.NET 技术整合起来,为通信业提供 .NET 连接解决方案,使服务提供商能够迅速提供新的语音、数据和内容服务,同时为传统和 B2B 集成提供卓越的支持,协助缩短时间,降低成本。 服务供应商致力于向客户提供全新服务,例如多媒体短信,IP电话以及提供更强大的互联网互动能力的设备。但是,它们往往都隐藏在组成其运营及业务支持系统的网络元素的后面。由于共同认识到 Web 服务将如何影响业内应用,微软和 Portal 开始联手提供 BillingAgility 解决方案。该解决方案可以使服务提供商对新的服务进行计费,同时可避免升级或开发新代码以增强原有功能时带来的重复成本。 使用 Web 服务,BillingAgility 是一种可扩展的高性能解决方案,专为满足服务供应商对计费和用户管理的要求而设计,同时亦支持未来的新服务。这种解决方案将 Portal 的 Infranet 软件和微软的 Windows Server 2003、SQL Server 2000 和 BizTalk Server 结合起来,向客户提供无与伦比的灵活性和出众的性能以及更低的总体拥有成本。 · 无与伦比的灵活性。BillingAgility 具有无与伦比的灵活性,客户可使用第三方应用软件轻松地更改复杂的计费系统和界面。BillingAgility 采用 Web 服务标准,比如 XML 和 SOAP, 使之可以与其它传统应用和系统更快速,更简单地集成,并且可以方便服务供应商迅速建立内容合作伙伴网络。例如,CRM 或一般分帐户系统与 BillingAgility 的无缝数据连接只要求先前应用程序50%的资源。 · 一流的性能。BillingAgility 是一个建立在微软 Windows Server 2003(64位)操作系统之上的计费和客户管理解决方案,采用 SQL Server 2000 企业版(64位)。服务提供商使用 64 位架构和 BillingAgility 将可以支持超过 2500 万用户并加快计费进程。这将大幅减少支持运行和管理任务关键型应用程序所需硬件的数量,满足世界一级运营商的处理要求。 · 降低总体拥有成本。Portal 基于产品的解决方案模式和微软基于技术的计算平台相结合,进一步为客户降低总体拥有成本。BillingAgility 通过缩短实施时间并降低维护成本为电信运营商带来极大的竞争优势。 Portal 软件公司简介 Portal 软件公司的客户包括中国电信、中国吉通、中国移动、沃达丰、中国铁通、路透社、美国在线时代华纳、香港电讯盈科、日本电报电话公司(NTT)、台湾远东传信、Telstra、Sprint 和法国电信等。 Portal 软件公司总部位于美国加利福尼亚州的 Cupertino,目前在亚太地区的北京、香港、东京、悉尼、新加坡、汉城、台北和墨尔本等地设有办事处。
 
      ddddh (2003-12-8 9:54:55)  

性能评估: 基本的测试表明,在同时并发连接数量,每个连接的延时和吞吐量上,Linux 2.6内核都是胜利者,其性能显著超过了2.4内核以及FreeBSD。所有的类Unix系统都将Windows系统远远抛在后面。在各种IO模型的比较下, linux2.6内核的epoll同样成为胜利者,而IO完成端口仍然是最后一名,显然,对易用性和图形性能的要求使得Windows并不适合作为一个服务器端的操作系统。 ===================== 这是怎么回事?没有看到测试方案,没有看到测试数据,但是看到了测试结论! -------------------------- 俗称是“托”?
 
      xkak2 (2003-12-8 9:37:11)  

性能评估: 基本的测试表明,在同时并发连接数量,每个连接的延时和吞吐量上,Linux 2.6内核都是胜利者,其性能显著超过了2.4内核以及FreeBSD。所有的类Unix系统都将Windows系统远远抛在后面。在各种IO模型的比较下, linux2.6内核的epoll同样成为胜利者,而IO完成端口仍然是最后一名,显然,对易用性和图形性能的要求使得Windows并不适合作为一个服务器端的操作系统。 ===================== 这是怎么回事?没有看到测试方案,没有看到测试数据,但是看到了测试结论!
 
      lbaby (2003-12-7 6:01:04)  

看的好舒服...
 
      hustzhuch (2003-12-6 17:37:40)  

好文,好强!
 
      hao_yufei (2003-12-6 16:53:03)  

插一句题外话: more small, more fast more simple, more fast 是否应该写成: smaller, faster simpler faster
 
      sunhuiNO1 (2003-12-5 20:09:11)  

zhuchuanjing: 我用IOCP测试的时候虽然才1000个客户端但是压力相当大,只要服务器处理完这个线程的请求,客户端马上发新的请求过来,CPU占用率极高 2只强P4 2。4G,2G内存的机器的CPU占用率到了80%以上,测试了3个效率仍然没有出现任何问题。看来你所谓的不稳定估计是你对IOCP的理解不够深刻,怪不得去搞Linux开发啊:)
 
      MHB (2003-12-5 19:48:30)  

more small, more fast more simple, more fast 》这是什么话?不开发速度更快, (初步的测试表明,linux2.6内核每秒可以创建5000以上的线程) 》那 windows每秒可以创建多少个呢? 不要告诉我只能创建5000以下。 性能评估: 基本的测试表明,在同时并发连接数量,每个连接的延时和吞吐量上,Linux 2.6内核都是胜利者,其性能显著超过了2.4内核以及FreeBSD。所有的类Unix系统都将Windows系统远远抛在后面。在各种IO模型的比较下, linux2.6内核的epoll同样成为胜利者,而IO完成端口仍然是最后一名,显然,对易用性和图形性能的要求使得Windows并不适合作为一个服务器端的操作系统。 》作者,你对windows知多少?对linux又知多少?你的这些数据从那里来的?你的开发经验?你开发的资质有多深(比我强是肯定的)?:) ft,就是叫linux祖师爷来都不敢说这句话。
 
      MHB (2003-12-5 19:33:49)  

作者,你是一个很好玩的人。:)
 
      ddddh (2003-12-5 17:17:46)  

这方面没什么经验不敢乱说话 不过这个贴的回复比较精彩 希望发表评论的人,用数据来说话 不要用猜的,那样没意思。
 
      hx (2003-12-5 17:03:42)  

很可能是这么来说, LINUX下数据库,更优秀! WINDOWS下数据库,影响到了系统资源。
 
      hx (2003-12-5 16:44:27)  

CACHE是很重要。。代码大小,也很重要。。。但开发进度,更重要!关于LINUX和WINDOWS,你在WINDOWS的字符界面下,进行过比较吗????我也同意进行和2003的比较。我看到的IOCP,性能,还是很不错的。。我也相信,LINUX的性能。。。但决到不是轮询!!!线程之间切换,也是很费资源的!!!你没有数据出来的话,请不要误导大家。!!!因为,很容易一些,没有做过,没有经历过的新人,会被你带入误区,那样的话,你这样,比一般的刑事犯罪还罪犯!做事情之前,要先负责!!!!!先去看看三个代表,不过,对你所提出的 more small, more fast more simple, more fast 和前面一些观点,很认同。
 
      zhuchuanjing (2003-12-5 14:20:05)  

你的测试里面,每两秒钟发送一次数据,远远没有到达带宽的上限,我测试的例子有两点不同: 1实际数据发送和接收基本上占用了所有的带宽,100M的网络超过10M字节/秒 2广域网的应用环境,存在大量的不稳定连接,TCP的滑动窗口重发的次数非常大。我想在这种情况下的性能和你局域网的测试应该有很大不同
 
      TNSW (2003-12-5 9:32:49)  

我最近刚刚完成了一个用windows完成端口实现的服务器。下面是我的测试数据:机子: p4 2.4G 512M SCSI30G 内存带宽533 数据库和服务器软件运行在同一台计算机机上,每个客户端连接每隔2秒发送一次数据,服务器软件要把数据保存到数据库并得出这个客户端已经有多少条记录。在四台计算机上同时连接了2000个客户端,服务器的CUP占用率为50%,实际上大部分CPU是被SQL SQLERVER数据库占用了,测试进行了一周,没有任何问题。我们一开始也遇到很多问题,最后才发现原来是自己对完成端口理解得不够。
 
      softeye (2003-12-4 20:14:56)  

实时应用的高性能和一般服务应用的高性能不能做比较,几乎就是不同的设计方向和设计语境。
 
      shujian (2003-12-4 14:54:26)  

SnowFalcon兄关注: 与SnowFalcon兄探讨问题 To SnowFalcon: 摘录的原话:你相信Java程序会有内存泄漏么?你相信一个程序的一个星期内高效工作,但是 下个礼拜一就因为内存碎片而慢的象乌龟爬么?你相信一个java程序会应为几千 个thread而导致每个连接需要用10秒钟来完成connect么?你相信因为使用了一个 visitor/obersver模式而把整个服务器的速度拖垮么?很多在平时看上去非常良好的设计,一旦更换到highperformance就是bullshit。而 且最为危险的是,最后的性能问题出在整个设计结构上。 shujian: 其实Java程序也会产生内存泄漏的问题,但JAVA所公开的文档上其实蒙蔽了我 们。我不知道SnowFalcon用什么办法在编写代码的时候自觉得做到把不用的对象进行释放。能给我们一点经验吗?? 我也有写过java的多thread程序,但同时在线的client<10,所以对于性能的测试只是一句空话。很难相信你说的:“你相信一个java程序会应为几千 个thread而导致每个连接需要用10秒钟来完成connect么?” 但我知道这是真的,我更想知道的是,您是怎么解决这个令人头痛的问题。能告诉我吗?? 摘录的原话:因此我认为highperformance的网络程序的一个信条就是永远不要相信操作系统和 语言的特性。最大的隐患就是我们最放心的地方。 shujian: 您说的没错,其实错在我们对操作系统和语言的特性认识的不够深刻 。也只有在屡犯错误的过程中,我们才越来越深刻得认识。才能避免同样错误的发生。你说呢?
 
      metabug (2003-12-4 13:59:56)  

对于上面线程与连接数目,还有所谓的并发问题我建议还是多看看tux对这些连接的处理,我采用类似的方法,性能提高了 50% 另外,如果必须使用tcp 连接,最好使用 tcp splicing来解决保存连接中的问题。
 
      metabug (2003-12-4 13:54:37)  

对于上面线程与连接数目,还有所谓的并发问题我建议还是多看看tux对这些连接的处理,我采用类似的方法,性能提高了 50% 另外,如果必须使用tcp 连接,最好使用 tcp splicing来解决保存连接中的问题。
 
      wuyg (2003-12-4 9:03:38)  

to SnowFalcon: 老兄也太武断了吧:“无论是OO和pattern用于编写程序除了好看以外都没有太大的作用。” 这么多优秀的人才搞出的东西就这么被否决了? 太慢了,好,加机器,搞集群,不是很困难吧?但没有复杂的功能如何满足日益复杂的用户的要求呢?诚然在很多地方为了性能还要妥协,但你也看到了硬件的高速发展,越来越多的地方不需要特别关注性能。我以前在640k的计算机上也用C写过很长时间的代码,也为了节约一些内存和提高一点性能费尽心机,但是时代在不断在变化,上次我看到网上有人说数据库最好的优化方法就是加内存和CPU,虽然搞笑但值得深思。 有句很有名的话“科技以人为本”,我理解的意思是“让机器累死,让人舒服死”,当然这里的人不仅指一般用户,也指程序开发人员和系统维护人员。
 
      SnowFalcon (2003-12-3 23:33:39)  

AXE-10和voip是两码事情,axe-10是电路交换自己有硬件来解决时分和空分的。那么你上层用什么都无所谓了,只要性能损耗的不是离谱。pattern的效率问题我是这么看得。任何不好的设计,你当然可以说那是没有用或者没有理解好pattern和OO。任何的好的设计都可以说是领悟了pattern和OO的精髓。无论是OO和pattern用于编写程序除了好看以外都没有太大的作用。扩展性复用性,只不过是一种gambling。能够复用或者能够扩展只不过是随机撞上的而已。Pattern或者OO,与一个软件复用与否没有必然的逻辑联系。既不是一个充分的条件,也不是一个必要的条件.
 
      flyinair2000 (2003-12-3 18:33:31)  

To SnowFalcon: 软件的设计和实现一律都是c,void指针的强制转型,old plain strcuture。不要说玩什么pattern之类的东西,就连OO,虚拟都是不允许的。template/stl还可能有用,但是你必须要修改所有的内存分配方式,以内存池代替malloc/free或者new/delete ------------ 据我所知,爱立信AXE-10交换机就是采用的oo设计。(当然,你也可以说它设计的很烂。不过,它的设计师可是个大牛)而中国研制的交换机的比如中兴,多是采用c.其实我认为不是效率问题,而是水平问题。结构设计是最切合人的思维习惯的,解决问题要向前看,不要以为用了pattern就会带来效率问题。(不要告诉我会用C的就是高手,B.J学了一年C就创造了C++)
 
      ychener (2003-12-3 17:46:59)  

这篇文章肯定有问题,我想问问楼主是否用过完成端口, 500个并发以上,就分页内存耗尽?? 狗屁! 如果不是作者胡说就是你的系统有问题。
 
      SteroWang (2003-12-3 16:56:31)  

非常支持作者能做这么大的一个题材的探索,并且针对这么多的操作系统。在这里给与作者高度的支持。庸勿质疑,在任何时候,无论硬件多么强悍,架构合理的高性能应用开发始终值得探索。在硬件配置相同的情况下,对于大量并发Socket的管理,我认为IO模型的影响最大,编码和实现次之,再是操作系统。编码和实现不大可能有量化的可比性,可假设一个人写的代码差别不大。非常希望作者能用清晰简洁的例子(最好是同一个人所写),保持同样的硬件配置,采取单项比较法,也即保持操作系统相同比较IO模型,保持IO模型相同比较操作系统(操作系统不同IO模型可能不会完全一一对应),在比较的过程中保持客户端不变(操作系统,硬件配置,目标程序都不变),用准确的事实数据阐述自己的观点,这对我们也是最好的参考。还有,Windows2003是否也能包含在比较范围内?刚看到一篇关于Unix类操作系统下的文章,觉的不错,推荐给大家 http://www.kegel.com/c10k.html
 
      WalkWorld (2003-12-3 14:38:50)  

SnowFalcon: nod,linux 2.6即将可以支持32个处理器了:)
 
      sunhuiNO1 (2003-12-3 14:38:46)  

高性能的网络程序我认为最重要的是带宽,如果带宽不够,你服务器写的效率再高也没用。 10M带宽下的服务器程序写的再好也比不过100M带宽下的普通服务器,就算你开了1000个线程,都用select模式,也就是64000个用户,你算算流量100m/8=12.5M/S,还要考虑网卡工作效率问题,最多到10m/s就极限了,如果是下文件就是10000个用户每个用户才1K/S根本就没有实用性。只要并发支持1000~3000个用户基本上就到了极限了。
 
      SnowFalcon (2003-12-3 14:28:27)  

同时处理是不可能的,一块cpu上的thread也不是并发的。只不过看上去并发而已。除非你加cpu.
 
      WalkWorld (2003-12-3 14:16:29)  

4000个client可以看作4000个并发的socket。 linux下一个进程能够创建1024个线程,一个线程能够等待64个事件,加入select的速度足够快,是不是可以这样说一个进程能够同时处理1024×64个client?
 
      SnowFalcon (2003-12-3 14:12:26)  

4000个没错!就是这么多。其实不能说是一个进程,只不过用时钟中断来作任务调度而已。每个线程归化成一个带有loop的任务,对于某一个运行中的任务,欲使其loop被定时调用的方法是,给这个任务定时发送一个信号,使该任务得到调度,在loop执行结束时,这个任务再将自己阻塞住,等待下一次信号。 Linux提供了一个定时发送信号的系统调用alarm(unsigned int seconds),该系统调用能够根据参数seconds,在seconds秒后发送一个SIGALRM信号给当前进程。如果应用是需要需要毫秒级的定时,修改系统调用(在kernel/timer.c中),使其定时单位变为毫秒。也就是相当于让一个进程变成一个分片式的任务调度系统,这样的好处是你可以控制某个任务需要调度多少时间。超过这个时间任务就pending,如果使用线程那么调度多少时间什么时候调度都是不可控制
 
      bolidecaster (2003-12-3 13:48:11)  

用一个进程?里面有多少线程呢?4000个? :) kidding.
 
      SnowFalcon (2003-12-3 13:26:21)  

4000个客户端,当然不应该用4000个thread.我只要更改一些linux代码,用一个进程就能全部照顾他们。
 
      SnowFalcon (2003-12-3 13:16:20)  

VOIP这个名字底下猫腻可是很多的,gateway和softswitch是两个概念,就是gateway里面也有不同,有些只是一个h.323或者sip的解析器,而真正的交换依然是电路交换。市面上很多产品都是这样。一个gateway只完成从PSTN与IP之间的协议转换。而真正意义上的voip是要用软件代替的电路交换。 关于语境的问题:我刚才说过了,能够依赖loadbalance这类的东西来解决性能问题的网络程序很难说是highperformance。 另外,关于trade-off的问题。trade-off是一个大词,它可以表述任何事情但是也等于什么都也没有说。你可以说在开发效率与性能之间的取舍。那么我认为如果说到开发效率,那么因为性能问题而推倒整个设计是不是影响开发效率。即使不是推翻整个设计,为高并发量的程序进行性能优化需要的时间也是相当可观的。高并发的程序的debug,性能调整需要非常高的技巧和耐心。一个bottleneck可能仅仅是几百万行代码中的一语句,有的甚至不是程序的问题而是操作系统本身的问题。就我个人的经验来说,如果不是一开始强调性能那么为这种程序进行性能优化需要的时间可能与开发时间相当。按照一般软件工程的经验,风险越是压后你就要付出更多的代价。在一般的程序设计中性能并不是一个风险,但是highperformance的网络程序中他就是最大的风险。如果你给程序员灌输这样的思想:"开发的时候不要太考虑效率问题,等最后优化的时候再说或者我们可以加cpu,加memorY"。那么这样你就种下了几千个,几万个地雷。可能某些人写出的程序每个函数都有性能问题。而到最后性能优化要挖除这些地雷的时候,你面对的是基本上在重写整个程序。也许kuth那句话:"early optimization is the root of all evils"对于一般的程序来说是对的,但是highperformance的网络程序就应该遵循len latanzi那句话:"belated pessimization is the leaf of no good".
 
      sunhuiNO1 (2003-12-3 12:43:29)  

我用完成端口的服务器做压力测试的时候并发1000人,测试一下午,压力相当大,只要服务器处理完了请求客户就接着发请求,服务器上只开了4个IOCP工作线程(其他的密码验证线程什么的不算)一直工作稳定,而且这个服务器程序是开了7天以后测试的,所有的测试都完成的很好,没有发帖子作者说的问题,我估计应该是贴主的IOCP测试代码有问题。
 
      bolidecaster (2003-12-3 12:35:04)  

SnowFalcon: trade-off是指对各种因素的权衡考量,不太明白你的意思。
 
      bolidecaster (2003-12-3 12:32:17)  

SnowFalcon: 我所说的语境是指与4000这个数目相关的环境、数据。你不能简单地说ACE/TAO在linux RT上同时只能支持4000个client。这没有一点意义。 CUseeMe公司使用ACE和TAO开发了基于软件的H.323 MCU。当然,这和你说的softswitch可能不是一个级别的,但这也恰恰说明了,离开系统需求、离开具体的语境谈论问题是没有意义的。 CUseeMe Networks, Inc use ACE and TAO in their CUseeMe Conference Server, a software-based H.323 MCU, enabling standards-based group conferencing with a variety of endpoints. The server uses a Reactor to handle all T.120 protocol communication, and a Proactor to handle the huge numbers of RTP/RTCP audio and video datagrams. Also, an ACE_Task is used as a thread pool for mixing audio packets. TAO is being used to configure and monitor the server.
 
      SnowFalcon (2003-12-3 12:32:03)  

另外一个问题是,tradeoff。tradeoff的一个关键假设是硬件性能提升的速度可以赶上糟糕代码所带来的消耗。但是这个假设成立么?在highperformance的语境下基本是不成立的。因为高并发的应用,就意味着代码调用的次数是指数级的增长。面对这种增长,硬件几乎无能为力。trade off很多时候会给程序员一种假象,即操作系统能管理任何事情。但是当出了问题以后,那已经是nightmare了。以前我曾经写过很长时间的java网络通信的应用。这些应用最麻烦的bug就是性能问题,而这些问题基本根源就是程序员书写的糟糕代码。你相信Java程序会有内存泄漏么?你相信一个程序的一个星期内高效工作,但是下个礼拜一就因为内存碎片而慢的象乌龟爬么?你相信一个java程序会应为几千个thread而导致每个连接需要用10秒钟来完成connect么?你相信因为使用了一个visitor/obersver模式而把整个服务器的速度拖垮么?很多在平时看上去非常良好的设计,一旦更换到highperformance就是bullshit。而且最为危险的是,最后的性能问题出在整个设计结构上。因此我认为highperformance的网络程序的一个信条就是永远不要相信操作系统和语言的特性。最大的隐患就是我们最放心的地方。
 
      SnowFalcon (2003-12-3 12:08:51)  

是的,的确是需要特定的语境。但是这个语境已经很清楚了,那就是highperformance。如果一个应用没有critical的性能需求,那么显然是不是在讨论highperformance。网络通信的种类很多,什么是high performance?一般来说高并发量,高流量的需求并不能代表是high performance。因为比如http,ftp这样的服务,应该是说中等的并发量,上下文无关,但是会有流量上的严格要求。那这些服务,用ace/tao之类的类库甚至使用java(例如tomcat/jboss)无疑是合适的。因为即使你的一台机器负荷很高,那么也能用loadbalance的方法将请求切换到其他空闲的服务器上。但是如果应用是有严格的上下文相关的,比如我正在从事的工作softswitch,每个tcp包都是一个连续的语音流的一部分,那么就无法在连接工作的时候切换服务器。那么就不得不提高每个服务器的性能和稳定性。要提高性能,那么一个关键的问题就是不要等待操作系统来调度资源,包括线程,内存,锁等等.这个观点在高性能的网络程序中都是通用的,只是程度不同而已。例如EJB的连接池,就是一种接管系统资源的方法。你可以说java自动回收内存,finalize来关闭连接。但是这种连接高消耗,也不得不让Java这种傻瓜语言低头。其实去看看jboss/tomcat的原代码,很多地方都充斥这些为了性能而舍弃设计的方法。
 
      WalkWorld (2003-12-3 11:38:01)  

linux里面的一个进程里能够创建的线程数量实际上就局限了它的连接能力,这样说你同意吗?linux你肯定要采用select ,而采用select的话有一个线程只能等待64个事件,这也限制了它的并发处理能力!未分页内存池耗尽可以通过限制未完成操作的数量来避免,你说的windows 2000 server下并发连接数量到500的时候,cpu 占用率就很高,而且时间长了会有未分页缓冲区耗尽的问题,不知道是从何而来? 你看看windows网络编程(2 edion)里面的数据吧! I/O模型 尝试连接数/成功连接数 使用内存(KB) 未分页内存池 CPU用量 线程 数据吞吐率(每秒发送/接收的字节) IO完成端口 7000/7000 36160 31128 40%~50% 2 6282473/3893507 12000/12000 59256 38862 40%~50% 2 5027914/5027905 50000/49,997 242272 148192 55%~65% 2 4326946/4326496 服务器P4 1.7G xeon, 内存768MB。客户机建立在3台计算机上:PII 233M,内存128MB;PII 350M,内存128M; Itanium 733M, 内存1G。测试网络100MB隔离集线器。操作系统win XP。它的服务器端和客户端的程序这本上都有,虽然我没有测试过,但是我相信这本书而不会相信你!你可以把你的数据贴出来。另外,我先声明,我不是对你漫骂,我只是希望你能对你的书认真负责!
 
      zhuchuanjing (2003-12-3 10:57:21)  

很高兴看到这么多人发表评论,还好谩骂的不是很多,比我预料的好多了,在这里我做几点申明:首先,我说的是linux的线程创建开销,和进程的线程上限没有关系其次,我实际做过大量IOCP和real time signal, epoll的工程,IOCP在处理极大数量并发连接的时候效率比rts和epoll差很多,接下来我会提供实际的例子和评测数据,在2000 server下IOCP的并发连接数量达到500以上的时候,CPU占用率就很高了,而且时间长了会有未分页缓冲区耗尽的问题(连续运行24小时,在网络环境不稳定的情况下)。我不知道这位朋友的IOCP效率最高的说法从何而来,如果是听来的就不要误导大家,如果是自己测试的结果我会很高兴的和你探讨。
 
      bolidecaster (2003-12-3 10:54:20)  

如果“完成端口用很少的几个线程处理几K并发连接依然保持很稳定的CPU占用……”,4000个客户大概也就并非等价于4000个并发线程,当然也就不用从第1个线程切换到第4000个线程了。这里有一篇关于ACE_Proactor(封装了NT的完成端口和POSIX的AIO API)的论文: http://www.cs.wustl.edu/~schmidt/PDF/PDCS-98.pdf SnowFalcon所说的评测数据,要看其系统设计针对的环境,没有特定的语境,这样的数据是没有意义的。另外,从理论上说,用C写的程序几乎总是可以比C++更快,但就像编写游戏软件一样:用汇编写游戏肯定最快,但现在没有人全部用汇编写大型游戏,因为你很难、或是不可能完成游戏,也没有必要这么做——用C写大部分代码、用汇编写性能关键的代码就能够满足要求了。这是一个trade-off的问题。如果用ACE这样的OO框架开发的应用能够满足要求,而且开发周期要短得多,就算性能稍差,我们仍旧可以选择ACE。更何况,一般开发者用C写成的网络应用未必能胜过基于ACE这样的应用。 SnowFalcon:辩论是很正常的事情,你不用觉得受到了挑衅。 Cowboy22:我也已经等了很久了。不知道什么时候能出版。
 
      wuyg (2003-12-3 10:23:20)  

to SnowFalcon: 如果没有上下文,老兄这么说我是完全正确,但你也要看看正文说的是商业应用上的网络应用, 不是硬件级的编程,因此从上下文来看,老兄这么说我可不对哦。 你应该也承认,每个观点都有自己的使用范围。几年前我也是非常武断地讲究运行效率,可是几年 的应用软件市场的打拼,让我明白了一些道理。
 
      WalkWorld (2003-12-3 10:20:40)  

TO:zhuchuanjing作者按照你现在的思路,如果你不做实际的测试,你的“网络IO模型的设计和评估:”这一章节将很难写! linux下面一个进程根本不能开5000个线程,最多也就1024个。而且开这么多的线程肯定会陷入Thread hell,线程间的切换开销将十分巨大,效率可想而知。更不要说采用轮询机制来检查套节字的状态了。可以采用select,但是也有局限,因为每个线程只能等待64个事件,这对于处理大规模的并发socket来说也是难以接收的。如果你真的想写书的话,希望你能对读者负责,你作出实际的测试,用数据来表明你的观点!谢谢!期待有一本好书出现!
 
      WalkWorld (2003-12-3 10:06:49)  

re windows下的IOCP(IO completion port) 是所有的I/O方案中效率最高的,只是在windows下面没有在linux下面稳定而已。我现在正在研究I/O完成端口编程,有兴趣的大家一起讨论吧,qq:43490118
 
      sunhuiNO1 (2003-12-3 8:42:16)  

真不知道你用过完成端口做过开发没有?完成端口用很少的几个线程处理几K并发连接依然保持很稳定的 CPU占用,同时由于线程很少,所以线程竞争很小,而且相互之间的切换由内核来控制,比循环SELECT效率要高多了。SnowFalcon的讲的很对。实话说,我很想看看你的测试代码和测试的数据。在我的影象中,完成端口模型在各种测试中都击败了linux下的各种I/O模型。
 
      Cowboy22 (2003-12-3 8:32:34)  

好好, 很有可能是一本好书, 出来的时候通知我一声. bolidecaster是马sir吧? 你和lostmouse的C++NP什么时候出来? 等了很久了.
 
      SnowFalcon (2003-12-3 0:01:17)  

ACE/TAO,So what!看看他的评测数据,linux RT上4000个client已经是峰值。如果4000个client就算high performance的话,那就未免儿戏了。就按照他的bench mark的机器,起码上要每分钟处理10万个call才能算是high performance。其实4000个线程,性能已经相当低了。Thread之间的context切换都是毫秒级的从Thread1切换到Thread4000就需要几秒钟的时间。如果Thread 4000被某个客户使用它要到几秒钟之后才能处理数据。当然在一般的http,ftp的应用这个几秒钟是无关紧要的.但是如果是一台softswitch,客户使用的是IP电话,那么你需要客户听到的语音就会出现严重的掉帧。
 
      bolidecaster (2003-12-2 23:20:07)  

FYI: http://www.cs.wustl.edu/~schmidt/ACE.html The ADAPTIVE Communication Environment (ACE) is a freely available, open-source object-oriented (OO) framework that implements many core patterns for concurrent communication software. ACE provides a rich set of reusable C++ wrapper facades and framework components that perform common communication software tasks across a range of OS platforms. The communication software tasks provided by ACE include event demultiplexing and event handler dispatching, signal handling, service initialization, interprocess communication, shared memory management, message routing, dynamic (re)configuration of distributed services, concurrent execution and synchronization. ACE is targeted for developers of high-performance and real-time communication services and applications. It simplifies the development of OO network applications and services that utilize interprocess communication, event demultiplexing, explicit dynamic linking, and concurrency. In addition, ACE automates system configuration and reconfiguration by dynamically linking services into applications at run-time and executing these services in one or more processes or threads. ACE continues to improve and its future is bright. ACE is supported commercially via the Riverace corporation using an open-source business model. In addition, many members of the ACE development team are currently working on building The ACE ORB (TAO). http://www.cs.wustl.edu/~schmidt/ACE-users.html ACE and TAO are being used in thousands of commercial products at many companies, as well as at various universities and research labs around the world, in many types of communication systems, particularly telecom, medical, aerospace, and financial services. Our research group has personally worked with companies using ACE and TAO on UNIX, Win32, and embedded platforms on the following projects: * TAO and ACE are used for a number of manned and unmanned real-time avionics mission computing embedded systems at Boeing/McDonnell Douglas in St. Louis, MO. Several successful test flights of Boeing aircraft were recently flown using ACE and TAO. * TAO and ACE are used for the Run-Time Infrastructure (RTI) for the Defense Modeling and Simulation Organization's (DMSO) High-Level Architecture (HLA) distributed interactive simulation environment at SAIC. * ACE and TAO are used for syngo, which is next-generation medical software framework for Siemens Medical Engineering, Erlangen, Germany. * TAO and ACE are being used for network management and a call processing gateway for a "voice over IP" system in the Siemens ICN division in Munich, Germany. * ACE and TAO are being used for a number wireless and wireline telecommunication and data networking embedded systems at Lucent Technologies and Motorola. ... ...
 
      bolidecaster (2003-12-2 23:04:21)  

zhuchuanjing:对6502了如指掌?
 
      BlueTrees (2003-12-2 20:53:17)  

没有明白文章在说什么,作者到底是什么观点?
 
      SnowFalcon (2003-12-2 17:28:01)  

以我所观察的情况而言,一般5万元人民币的服务器硬件可以承担的应用是至少50万人民币的软件价值。 所以我认为作者并没有充分考虑总体成本。 ___________________________________________ 你知道通常的softswtich每分钟会处理多少个call么?7个billion 你知道一个u的服务器只要要支撑多少并发数才能满足一个局的IP电话的接入?1.5 个million 不懂就不要乱说.高性能的网络服务器甚至是杜绝线程的使用,而采用时钟中断进行状态机轮询。如果一个服务器开1万个thread,可能已经到了极限。面对7个billion的呼叫简直就是杯水车薪。软件的设计和实现一律都是c,void指针的强制转型,old plain strcuture。不要说玩什么pattern之类的东西,就连OO,虚拟都是不允许的。template/stl还可能有用,但是你必须要修改所有的内存分配方式,以内存池代替malloc/free或者new/delete。
 
      wuyg (2003-12-2 15:55:04)  

我常听人说,现代计算机系统开发的侧重点已经从运行效率转到开发效率,是因为硬件的高速降价和软件成本的据高不下造成的。以我所观察的情况而言,一般5万元人民币的服务器硬件可以承担的应用是至少50万人民币的软件价值。 所以我认为作者并没有充分考虑总体成本。
 
      captainwh (2003-12-2 15:44:53)  

怀疑其权威性,是个Linux fans吧
 
      securitytrip (2003-12-2 15:24:17)  

感谢你提供这样一个思路,希望我们能在测试方法和数据分析等方面作进一步探讨。
 
      Alex_20 (2003-12-2 14:59:44)  

高效率的缓存机制也非常重要啊,希望作者不要忘了这一点。
 
      solarsoft (2003-11-30 13:36:41)  

epoll
 
  

来自: 张无忌, 时间:2003-12-16 10:53:00, ID:2356676
楼上的帖子里的sunhuiNO1就是我,
我认为上面最重要的是:
l         删除冗余代码,和一般理解相反,放在那里不动的代码即使没有使用也会影响系统的性能。

l         不要低估或者高估编译器的优化,除了代码本身所能提供的信息以外,编译器永远不知道你到底要做什么。

l         不要梦想复用,在代码级重复使用你代码的可能性接近于0

l         层层的封装是效率的杀手

l         动态运行时解析,类型….带来的问题远远大于所承诺的好处。

l         专用、专用再专用,把你的代码限制在一个非常具体的场景中,通用的代码往往意味着低效、潜在的误用和其他错误。

l         不要做期待之外的事情,所有的bonus都是要付出代价的!

l         优化最常见的情况,而不是最糟糕的情况。

l         内存分配和释放是非常昂贵的操作(从时间上,稳定性上都是)

l         不要想象,使用工具观察你的代码,vTune或者gProf等等,只有这样才能发现真正的瓶颈所在。
在服务器开发里string带来了很多额外的开销。总之不适合在服务器的代码里使用也是正常的。  

来自: 张无忌, 时间:2003-12-16 10:58:00, ID:2356702
l         不要梦想复用,在代码级重复使用你代码的可能性接近于0
//这句我认为有一些不妥当,适当的封装是可以的,只要封装的层次不是太深
对于频繁调用的函数不要采用虚拟函数重复使用代码是可行的,对于类的普通
函数(非虚函数),只是比静态函数多传入了一个参数而已,其他和静态函数
一样,所以不降低效率。  

来自: dirk, 时间:2003-12-16 13:03:00, ID:2356782






  

来自: mywyn, 时间:2003-12-16 11:23:00, ID:2356790
to 张无忌:
  你这家伙有事没事换马甲干吗![:D]
//适当的封装是可以的
这句话我同意,不过我拒绝用继承。
  

来自: baifeng, 时间:2003-12-16 11:29:00, ID:2356812
to mywyn, 各位:
哈哈,插个题外话,"马甲"是什么意思?  

来自: 张无忌, 时间:2003-12-16 11:33:00, ID:2356815





  

来自: 张无忌, 时间:2003-12-16 11:31:00, ID:2356818
to mywyn:
那是在csdn上的。偶 的注册名有人注册过的。  

来自: yanyandt2, 时间:2003-12-16 11:33:00, ID:2356821
吵啥吵啊
谁能静下心来辅导一下新手啊
  

来自: baifeng, 时间:2003-12-16 11:37:00, ID:2356838
同意张无忌,
总有一些马甲在搅混水,,大家都讨论技术吧


  

来自: dirk, 时间:2003-12-16 13:03:00, ID:2357003





  

来自: 张无忌, 时间:2003-12-16 13:04:00, ID:2357032
























  

来自: dirk, 时间:2003-12-16 13:03:00, ID:2357062





  

来自: 张无忌, 时间:2003-12-16 13:03:00, ID:2357076






















  

来自: dirk, 时间:2003-12-16 13:02:00, ID:2357080




  

来自: mywyn, 时间:2003-12-16 12:54:00, ID:2357091
to dirk:
 过去的已经过去了,不要再翻出来了。如果你对张无忌有任何看法,建议你发消息跟他单独聊。这里是论坛,不要影响大家的心情。

to baifeng:
  我只是奇怪张无忌在CSDN上没有用这个ID而已。不过,如果你这样说他小心他生气。
  我说说也许没事,因为他知道我没别的意思。[8D]

  

来自: 张无忌, 时间:2003-12-16 13:03:00, ID:2357093















  

来自: dirk, 时间:2003-12-16 13:02:00, ID:2357100
   


  

来自: ka52, 时间:2003-12-16 13:00:00, ID:2357110
晕啊.你们怎么老斗气啊.
看开点,都是写Code的,交流问题就可以了.

  

来自: mywyn, 时间:2003-12-16 13:03:00, ID:2357120
建议楼主限定一下讨论范围,在特定的语境下讨论才有意义比如:
Win2000 AD Server+Delphi  每秒上万并发连接 30*24连续运行(多了WIN2000也受不了)
好有什么大家往上添啊!
    

来自: 张无忌, 时间:2003-12-16 13:07:00, ID:2357134
to mywyn:
每秒上万连接估计很难,带宽都不够,上千并发连接是可行的,
我估计楼主的需求是几百个连接把,那么DXSocket是足够应付的
DXSocket是用的select模型,好象可以跨平台,代码写的很精简
不过都是框架,很多地方还是需要自己动手的。  

来自: mywyn, 时间:2003-12-16 13:12:00, ID:2357151
to 张无忌:
 对,还有带宽。就定10M吧!我公司那烂HUB也就这数了。
 楼主呢?这事得他同意啊!  

来自: mywyn, 时间:2003-12-16 13:17:00, ID:2357166
to 张无忌:
看情况ACE比DXSocket行!什么时候我有空把它翻译成DELPHI,当然,只翻WIN部分[:)]  

来自: 张无忌, 时间:2003-12-16 13:18:00, ID:2357172
mywyn:
偶搞到了一本ace设计思路和模式的好书,哈,有机会介绍给你看看,[:D]  

来自: 张无忌, 时间:2003-12-16 13:19:00, ID:2357180
对了,mywyn那可以下ace的代码,我没找到了,不过我以后改用c++了,所以不用翻译了,
mywyn你也改用C++把!!!  

来自: baifeng, 时间:2003-12-16 13:31:00, ID:2357215
来自:mywyn, 时间:2003-12-16 12:54:00, ID:2357091
to dirk:
 过去的已经过去了,不要再翻出来了。如果你对张无忌有任何看法,建议你发消息跟他单独聊。这里是论坛,不要影响大家的心情。

to baifeng:
  我只是奇怪张无忌在CSDN上没有用这个ID而已。不过,如果你这样说他小心他生气。
  我说说也许没事,因为他知道我没别的意思。

 
to mywyn:我那样说他了?,莫明奇妙,

"如果你这样说他小心他生气"你这句话的意思,就是你
认为他小气,最烦你这样挑剔离间、暗箭伤人的人,  

来自: mywyn, 时间:2003-12-16 13:30:00, ID:2357219

来自: mywyn, 时间:2003-12-16 13:45:00, ID:2357269
to baifeng:
  TMD,不懂马甲这词就不要乱叫唤。不信,去找个跟你不熟的网友聊天试试

//你这个意思,就是你
//认为他小气,最烦你这样挑剔离间的人
我认为张无忌小气?他会这样认为才怪!挑剔离间?你是哪路毛神我都不知道,我离间谁啊!以后说话注意点,别TM有事没事乱扣屎盆子。
  

来自: baifeng, 时间:2003-12-16 13:57:00, ID:2357318
我那样说张了?
我只是说同意张的见解,

然道不懂一个网络名词,就能不能问.你算那根毛,

"如果你这样说他小心他生气",那你解释一下你的意思。别在乱扣屎盆子,还说别人。
  

来自: mywyn, 时间:2003-12-16 14:11:00, ID:2357356
to 张无忌:
//不过我以后改用c++了
vc or bcb?
   

来自: 张无忌, 时间:2003-12-16 14:14:00, ID:2357365
VC把,不过更多的是纯的C++,比如ACE什么的,  

来自: 未来黑客, 时间:2003-12-16 17:09:00, ID:2357945
鸟教主

  

来自: 雪上霜, 时间:2003-12-16 19:46:00, ID:2358312
精彩,看到了这么多高手,
studing。。。。。。。。。。。  

来自: tomato, 时间:2003-12-16 21:25:00, ID:2358427
呵呵,不吵了,两天了!  

来自: yjhzzgl, 时间:2003-12-16 21:46:00, ID:2358452
学习学习!!!!  

来自: lich, 时间:2003-12-17 13:39:00, ID:2359828
ACE是什么东西啊? --不要说我灌水
  

来自: 张无忌, 时间:2003-12-17 13:43:00, ID:2359844
ACE是DON BOX就是哪个著名的COM专家的硕士同学搞的一套跨平台的
SOCKET类库,很多大的研发结构支持,同时美国军方支持。
其中主要使用了一些简洁的类设计,基于模板的一套类库。  

来自: iDevCN, 时间:2003-12-17 13:52:00, ID:2359884
http://www.flyingdonkey.com/
有ACE方面的书籍,比较厉害的喔,我看了ACE的代码,太复杂了
一个字"晕"!  

来自: lynu, 时间:2003-12-17 13:58:00, ID:2359899
ACE这么说来,是很庞大的东东了.得到美国军方支持,那应该是很N的东东了.
也有一个Delphi第三方跨平台Socket开发库,好象叫什么Synapse TCP/IP Library,不知道有没有谁用过,给个评价?这个好象也没有应用OO设计.
DON BOX是那个"com本质论"的作者?


  

来自: 张无忌, 时间:2003-12-17 14:01:00, ID:2359905
是啊,don box就是哪本书的作者,他在MS搞web service方面的开发了,
现在他拜倒在XML的石榴裙下了。[:D]  

来自: shaga, 时间:2003-12-17 15:06:00, ID:2360106
g z

提个菜鸟话题,如何进行1000个并发测试?自己写程序?还是有工具的?  

来自: 李衍智, 时间:2003-12-17 15:20:00, ID:2360177
关于服务器的压力问题,如果用isapi效率如何,谁有这方面的测试或建议?  

来自: ZZHI, 时间:2003-12-17 16:10:00, ID:2360377
同意张无忌关于String的观点,我写服务器时也出过同样问题,改成数组后才稳定下来!  

来自: iDevCN, 时间:2003-12-17 17:20:00, ID:2360662
我用Synapse 开发了一个服务器,但是不知道是我的问题还是Synapse 的问题
并发数据一达到100+就丢连接  

来自: yangying_2000, 时间:2003-12-19 9:55:00, ID:2363951
下面是两个队列类的代码,高手帮忙指点指点看看有什么不对的地方,
第一个是限制容量的QUEUE,第二个是标准链表实现的QUEUE
事件是根据条件编译来决定是否使用的

unit FifoQueue;

interface
uses
  Classes, Sysutils, Windows, Syncobjs;

type
  TCompareQueueItemMethod = function(Item1, Item2: Pointer): Boolean;

  TPointerArray = array of Pointer;

  TItemEvent = procedure(Item: Pointer) of object;

  TFifoQueue = class(TObject)
  private
    FQueueArray: TPointerArray;
    FQueueLock: TCriticalSection;
{$IFNDEF SERVERMODE}
    FOnPushItem: TItemEvent;
    FOnGetItem: TItemEvent;
{$ENDIF}
    FFirstItemIndex, FLastItemIndex: Integer;
    FCount, FCapacity: Integer;
  public
    constructor Create(QueueLength: Integer);
    destructor Destroy; override;
    function PushItem(Item: Pointer): Boolean;
    function GetItem: Pointer;
    //function GetItems(GetNum: Integer): TPointerArray;
    function FindItem(Item: Pointer; method: TCompareQueueItemMethod): Boolean;
    property Count: Integer read FCount;
{$IFNDEF SERVERMODE}
    property OnPushItem: TItemEvent read FOnPushItem write FOnPushItem;
    property OnGetItem: TItemEvent read FOnGetItem write FOnGetItem;
{$ENDIF}
  end;

  PLinkNode = ^TLinkNode;
  TLinkNode = record
    Prior: PLinkNode;
    Data: Pointer;
    Next: PLinkNode;
  end;

  TExtendQueue = class(TObject)
  private
    FFirstNode, FLastNode: PLinkNode;
{$IFNDEF SERVERMODE}
    FOnPushItem: TItemEvent;
    FOnPopItem: TItemEvent;
{$ENDIF}
    FCount: Integer;
    procedure DeleteNode(node: PLinkNode);
    procedure Clear;
  public
    constructor Create;
    destructor Destroy; override;
    procedure PushItem(Item: Pointer);
    function PopItem: Pointer;
    function GetItem: Pointer;
    function FindItem(Item: Pointer; method: TCompareQueueItemMethod): Boolean;
    function PopFindItem(Item: Pointer; method: TCompareQueueItemMethod): Pointer;
    property Count: Integer read FCount;
{$IFNDEF SERVERMODE}
    property OnPushItem: TItemEvent read FOnPushItem write FOnPushItem;
    property OnPopItem: TItemEvent read FOnPopItem write FOnPopItem;
{$ENDIF}
  end;

implementation

{ TFifoQueue }

constructor TFifoQueue.Create(QueueLength: Integer);
begin
  FCapacity := QueueLength;
  SetLength(FQueueArray, FCapacity);
  FQueueLock := TCriticalSection.Create;
  FFirstItemIndex := -1;
  FLastItemIndex := -1;
  FCount := 0;
end;

destructor TFifoQueue.Destroy;
begin
  FQueueLock.Free;
  FQueueArray := nil;
end;

function TFifoQueue.FindItem(Item: Pointer; method:
  TCompareQueueItemMethod): Boolean;
var
  i: Integer;
begin
  Result := False;
  if FCount = 0 then Exit;
  FQueueLock.Enter;
  try
    for i := FFirstItemIndex to FLastItemIndex do
      if method(Item, FQueueArray[i]) then
      begin
        Result := True;
        Break;
      end;
  finally
    FQueueLock.Leave;
  end;
end;

function TFifoQueue.GetItem: Pointer;
begin
  Result := nil;
  if FCount = 0 then
    Exit;
  FQueueLock.Enter;
  try
    Result := FQueueArray[FFirstItemIndex];
    Dec(FCount);
    if FCount = 0 then
    begin
      FFirstItemIndex := -1;
      FLastItemIndex := -1;
    end
    else
    begin
      if FFirstItemIndex = FCapacity - 1 then
        FFirstItemIndex := 0
      else
        Inc(FFirstItemIndex);
      if FCount = 1 then
        FLastItemIndex := FFirstItemIndex;
    end;
{$IFNDEF SERVERMODE}
    if Assigned(FOnGetItem) then
      FOnGetItem(Result);
{$ENDIF}
  finally
    FQueueLock.Leave;
  end;
end;

{function TFifoQueue.GetItems(GetNum: Integer): TPointerArray;
var
  ResultNum: Integer;
begin
  ResultNum := Min(GetNum, FCount);
  SetLength(Result, ResultNum);
  FQueueLock.Enter;
  try
    if FFirstItemIndex < FCapacity - 1 then
      FFirstItemIndex := ResultNum - 1
    else
      Inc(FFirstItemIndex, ResultNum);
    Dec(FCount, ResultNum);
    Move(FQueueArray[FFirstItemIndex]);
  finally
    FQueueLock.Leave;
  end;
end;}

function TFifoQueue.PushItem(Item: Pointer): Boolean;
begin
  Result := False;
  if FCount = FCapacity then
    Exit;
  FQueueLock.Enter;
  try
    if FLastItemIndex = FCapacity - 1 then
      FLastItemIndex := 0
    else
      Inc(FLastItemIndex);
    Inc(FCount);
    if FCount = 1 then
      FFirstItemIndex := FLastItemIndex;
    FQueueArray[FLastItemIndex] := Item;
    Result := True;
{$IFNDEF SERVERMODE}
    if Assigned(FOnPushItem) then
      FOnPushItem(Item);
{$ENDIF}
  finally
    FQueueLock.Leave;
  end;
end;

{ TExtendQueue }

procedure TExtendQueue.Clear;
var
  tmp, node: PLinkNode;
begin
  node := FFirstNode;
  while Assigned(node) do
  begin
    tmp := node;
    node := node^.Next;
    Dispose(tmp);
  end;
end;

constructor TExtendQueue.Create;
begin
  FCount := 0;
  FFirstNode := nil;
end;

destructor TExtendQueue.Destroy;
begin
  Clear;
  inherited;
end;

function TExtendQueue.FindItem(Item: Pointer;
  method: TCompareQueueItemMethod): Boolean;
var
  node: PLinkNode;
begin
  Result := False;
  if FCount = 0 then
    Exit
  else
  begin
    node := FFirstNode;
    while Assigned(node) do
    begin
      Result := method(Item, node.Data);
      if Result then
        Break;
      node := node^.Next;
    end;
  end;
end;

function TExtendQueue.PopFindItem(Item: Pointer;
  method: TCompareQueueItemMethod): Pointer;
var
  node: PLinkNode;
begin
  Result := nil;
  if FCount = 0 then
    Exit
  else
  begin
    node := FFirstNode;
    while Assigned(node) do
    begin
      if method(Item, node.Data) then
      begin
        Result := node.Data;
        DeleteNode(node);
        Dec(FCount);
{$IFNDEF SERVERMODE}
        if Assigned(FOnPopItem) then
          FOnPopItem(Result);
{$ENDIF}
        Break;
      end;
      node := node^.Next;
    end;
  end;
end;

function TExtendQueue.GetItem: Pointer;
begin
  if FCount = 0 then
    Result := nil
  else
    Result := FFirstNode^.Data;
end;

procedure TExtendQueue.DeleteNode(node: PLinkNode);
begin
  if Assigned(node^.Prior) then
    node^.Prior^.Next := node^.Next;
  if Assigned(node^.Next) then
    node^.Next^.Prior := node^.Prior;
  if FCount = 1 then
  begin
    FFirstNode := nil;
    FLastNode := nil;
  end
  else
  begin
    if node = FFirstNode then
      FFirstNode := node^.Next;
    if node = FLastNode then
      FLastNode := node^.Prior;
  end;
  Dispose(node);
end;

function TExtendQueue.PopItem: Pointer;
var
  node: PLinkNode;
begin
  if FCount = 0 then
    Result := nil
  else
  begin
    node := FFirstNode;
    Result := node^.Data;
    if FCount = 1 then
    begin
      FLastNode := nil;
      FFirstNode := nil;
    end
    else
    begin
      FFirstNode := node^.Next;
      FFirstNode.Prior := nil;
    end;
    Dec(FCount);
{$IFNDEF SERVERMODE}
  if Assigned(FOnPopItem) then
    FOnPopItem(Result);
{$ENDIF}
    Dispose(node);
  end;
end;

procedure TExtendQueue.PushItem(Item: Pointer);
var
  node: PLinkNode;
begin
  if FCount = 0 then
  begin
    New(FFirstNode);
    FFirstNode^.Data := Item;
    FFirstNode^.Next := nil;
    FFirstNode^.Prior := nil;
    FLastNode := FFirstNode;
  end
  else
  begin
    New(node);
    node^.Data := Item;
    node^.Next := nil;
    node^.Prior := FLastNode;
    FLastNode^.Next := node;
    FLastNode := node;
  end;
  Inc(FCount);
{$IFNDEF SERVERMODE}
  if Assigned(FOnPushItem) then
    FOnPushItem(Item);
{$ENDIF}
end;

end.

  

来自: 乡村月光, 时间:2003-12-19 21:39:00, ID:2365618
ACE哪里有下载没有?最新的版本是多少?  

来自: 李衍智, 时间:2003-12-19 22:21:00, ID:2365656
关于服务器的压力问题,如果用isapi效率如何,谁有这方面的测试或建议?  
  

来自: xiao.lit, 时间:2003-12-20 15:29:00, ID:2366621
张无忌: welcome to c++ [:D]  

来自: xuxiaohan, 时间:2004-1-5 13:49:00, ID:2393827
真的大好人,学习加收藏!
  

来自: zeroyou, 时间:2004-1-5 13:54:00, ID:2393841
到此一游,  

来自: 小雨哥, 时间:2004-3-30 2:43:17, ID:2528919
很好嘛,确实精彩,虽然没看完,也要写一句留着以后慢慢看。
很好、很好,最好是谁也别同意谁,同时说明您为什么不同意,这样才最好!
我在自己的 SharpReader 里已经把这个贴子锁上了,大家继续!  

来自: 余远源, 时间:2005-8-24 11:44:09, ID:3178294
学习  

来自: element, 时间:2005-8-30 13:35:33, ID:3185443
这么老的贴子都被翻出来啦。看来我也要好好学习学习咯。  

来自: hhmyz, 时间:2005-8-30 14:26:29, ID:3185506
經典的東西永遠不會沉下去。  

来自: element, 时间:2005-8-30 15:13:08, ID:3185589
我已经很久没有上论坛咯,主要是工作太忙咯。看见大家激烈的讨论,我很高兴。这两个我准备在开一个这个主题的续片。在将我这段时间的经验在总结一下。发表出来。  

来自: jmvodnet, 时间:2005-12-20 19:10:21, ID:3303370
 张无忌
你在哪?我有急事找你啊?
加我QQ:368709858或msn:heshan_sky@hotmail.com
  

来自: jmvodnet, 时间:2005-12-20 19:12:24, ID:3303372
急!!
我出人民币买成熟的c/s完成端口的登陆认证代码,
具体:
1.服务器端:负责处理客户端发送过来的登陆信息(帐、密码、ip),得到后就在本机的sql server查找,如果发现有登陆权限,就允许建立连接,并发送成功的信息给客户端,如果不行就断开。
2。服务器端需要随时发送信息给客户端,例如广播信息
3、客户端得到认证通过后就登陆成功,如果接受到服务器的特定信息就处理一些过程。
4、客户端超时处理
5、支持5000人以上的在线数量
qq:368409858
  

来自: wx_ham, 时间:2006-3-16 10:44:13, ID:3383517
这个帖子我找了很久,没有想到这么好的帖子沉了这么久!
现在再把它顶上去,我自己也记一下。
希望大富翁以前多有这样的帖子,这样才能帮助大家提高!  

来自: email_chen, 时间:2006-11-21 12:02:51, ID:3628374
好贴顶上去。
补充一点:
写服务器软件  能用结构类型Record替代的,不要用对象Tobject;能用ShortString替代的,不用String  

来自: sky1001, 时间:2006-11-21 12:41:40, ID:3628410
俺也补充一点:
对内存的分配不要直接交给DELPHI的堆管理器来做,那样容易产生内存抖动,很影响速度.应该学学VCL的一些写法.一次性把内存分配出来,连续的分配在一个页面里.产生一个节点管理器来管理这些一次性分配出来的内存.服务器所需要的内存都从这个管理器来分配和释放(不是真的释放).这样,会大大提高效率和速度.VCL的一些类都是这种思路.  

来自: ufo!, 时间:2006-11-21 17:11:57, ID:3628773
哦,3年前的帖子了。
  

来自: element, 时间:2006-11-22 9:44:27, ID:3629090
很多久没有回来咯,今天看见这3年前的帖子,看见大家又重新拾起。感动ING。  

来自: 伤心的石头, 时间:2006-11-23 10:27:48, ID:3630045
感动啊。。。。学习!收藏!![:D] ^_^  

来自: linuxping, 时间:2006-12-2 15:24:50, ID:3637694
好贴~顶~~

另外,问问楼主,贴子中你不是说要发经验总结么?怎么没找到...
麻烦把网址贴出来~  

问题讨论没有结束 ...
您尚未进入本论坛 或者 您的账号没有经过确认。
<script>//xls=load_xls("dispq_1.xsl");</script> element 100 6014 137 2003-12-8 20:52:00 2006-12-2 15:24:49 Internet/TCPIP 10 luyear robertcool chenshaizi 2003-12-8 20:58:00 guilinxie 2003-12-8 21:12:00 fjjb 2003-12-8 22:03:00 tongdanfeng 2003-12-9 9:16:00 唐佐平 2003-12-9 9:28:00 QSmile 2003-12-9 9:52:00 strgold 2003-12-9 10:22:00 kkg 2003-12-9 10:46:00 张无忌 2003-12-9 12:37:00 lynu 2003-12-9 12:45:00 element 2003-12-9 12:47:00 pingshx 2003-12-9 12:47:00 张无忌 2003-12-9 12:49:00 张无忌 2003-12-9 12:51:00 element 2003-12-9 12:51:00 张无忌 2003-12-9 12:56:00 element 2003-12-9 13:00:00 element 2003-12-9 13:01:00 张无忌 2003-12-9 13:05:00 yu_ting 2003-12-9 13:08:00 张无忌 2003-12-9 13:10:00 element 2003-12-9 13:13:00 张无忌 2003-12-9 13:15:00 delphifaq 2003-12-9 13:21:00 lynu 2003-12-11 9:38:00 张无忌 2003-12-11 14:09:00 lynu 2003-12-12 12:54:00 张无忌 2003-12-12 13:02:00 yanyandt2 2003-12-12 13:23:00 lynu 2003-12-12 13:44:00 EdwinYeah 2003-12-12 13:58:00 qince 2003-12-12 14:00:00 张无忌 2003-12-12 14:14:00 fly2003 2003-12-12 15:03:00 wfzha 2003-12-12 15:18:00 softdog 2003-12-12 15:47:00 smiledayly 2003-12-12 20:49:00 ego 2003-12-12 22:03:00 lich 2003-12-13 13:28:00 张无忌 2003-12-13 13:49:00 lynu 2003-12-13 17:48:00 lynu 2003-12-13 17:56:00 iDevCN 2003-12-14 1:19:00 张无忌 2003-12-14 10:51:00 yanyandt2 2003-12-14 11:20:00 tseug 2003-12-14 13:16:00 lich 2003-12-14 20:24:00 未来黑客 2004-5-5 13:27:29 未来黑客 2004-5-5 13:28:03 gongxingg 2003-12-14 23:17:00 fox816 2003-12-15 0:20:00 张无忌 2003-12-15 8:29:00 张无忌 2003-12-15 8:33:00 yanyandt2 2003-12-15 9:38:00 未来黑客 2004-5-5 13:29:37 张无忌 2003-12-15 10:21:00 未来黑客 2004-5-5 13:28:37 app2001 2003-12-15 17:41:00 张无忌 2003-12-15 18:54:00 lich 2003-12-15 19:02:00 ego 2003-12-15 19:07:00 未来黑客 2004-5-5 13:29:05 lich 2003-12-15 22:43:00 未来黑客 2003-12-15 23:37:00 张无忌 2003-12-16 8:49:00 lich 2003-12-16 9:06:00 element 2003-12-16 9:19:00 yanyandt2 2003-12-16 9:39:00 LeeChange 2003-12-16 9:47:00 张无忌 2003-12-16 9:49:00 baifeng 2003-12-16 9:56:00 HAZL 2003-12-16 9:59:00 张无忌 2003-12-16 10:13:00 mywyn 2003-12-16 10:41:00 张无忌 2003-12-16 10:53:00 张无忌 2003-12-16 10:58:00 dirk 2003-12-16 13:03:00 mywyn 2003-12-16 11:23:00 baifeng 2003-12-16 11:29:00 张无忌 2003-12-16 11:33:00 张无忌 2003-12-16 11:31:00 yanyandt2 2003-12-16 11:33:00 baifeng 2003-12-16 11:37:00 dirk 2003-12-16 13:03:00 张无忌 2003-12-16 13:04:00 dirk 2003-12-16 13:03:00 张无忌 2003-12-16 13:03:00 dirk 2003-12-16 13:02:00 mywyn 2003-12-16 12:54:00 张无忌 2003-12-16 13:03:00 dirk 2003-12-16 13:02:00 ka52 2003-12-16 13:00:00 mywyn 2003-12-16 13:03:00 张无忌 2003-12-16 13:07:00 mywyn 2003-12-16 13:12:00 mywyn 2003-12-16 13:17:00 张无忌 2003-12-16 13:18:00 张无忌 2003-12-16 13:19:00 baifeng 2003-12-16 13:31:00 mywyn 2003-12-16 13:30:00 mywyn 2003-12-16 13:45:00 baifeng 2003-12-16 13:57:00 mywyn 2003-12-16 14:11:00 张无忌 2003-12-16 14:14:00 未来黑客 2003-12-16 17:09:00 雪上霜 2003-12-16 19:46:00 tomato 2003-12-16 21:25:00 yjhzzgl 2003-12-16 21:46:00 lich 2003-12-17 13:39:00 张无忌 2003-12-17 13:43:00 iDevCN 2003-12-17 13:52:00 lynu 2003-12-17 13:58:00 张无忌 2003-12-17 14:01:00 shaga 2003-12-17 15:06:00 李衍智 2003-12-17 15:20:00 ZZHI 2003-12-17 16:10:00 iDevCN 2003-12-17 17:20:00 yangying_2000 2003-12-19 9:55:00 乡村月光 2003-12-19 21:39:00 李衍智 2003-12-19 22:21:00 xiao.lit 2003-12-20 15:29:00 xuxiaohan 2004-1-5 13:49:00 zeroyou 2004-1-5 13:54:00 小雨哥 2004-3-30 2:43:17 余远源 2005-8-24 11:44:09 element 2005-8-30 13:35:33 hhmyz 2005-8-30 14:26:29 element 2005-8-30 15:13:08 jmvodnet 2005-12-20 19:10:21 jmvodnet 2005-12-20 19:12:24 wx_ham 2006-3-16 10:44:13 email_chen 2006-11-21 12:02:51 sky1001 2006-11-21 12:41:40 ufo! 2006-11-21 17:11:57 element 2006-11-22 9:44:27 伤心的石头 2006-11-23 10:27:48 linuxping 2006-12-2 15:24:50 <script> function show() { //load_xml(menupanel, menuxml, menuxsl); load_xmln(menupanel, menuxml, "../delphibbs/login.xsl"); //load_xmlx(mainpanel, mainxml, xls); //load_xml(mainpanel, mainxml, mainxsl); load_xmln(mainpanel, mainxml, "dispq_1.xsl"); prestr(); } function changeFontSize(size) { obj = document.getElementById('mainpanel'); if (!obj) alert('not found'); for (var ii=0; ii < obj.all.tags('TD').length; ii++) { var td = obj.all.tags("TD").item(ii); td.style.fontFamily = "宋体"; td.style.fontSize = size; td.style.lineHeight = "150%"; } } function validateForm() { var s1,r,ra; s1=DelBadChar(document.getElementById("S1").value); if(s1.length<12){ ra=/gz|up|关注|支持|继续|/?+/i; r = s1.search(ra); if(r>=0){ alert("请不要灌水"); return false; } } document.getElementById("S1").value=s1; } </script> <script language=javascript id=clientEventHandlersJS> function document_onkeydown() { var i,t; if((window.event.keyCode==84||window.event.keyCode==83) && window.event.ctrlKey ) // ctrl-T { t=0; if (reply.ups.length){ for (i=0;i
<script> showMsg();show(); </script>

(C) 版权所有,大富翁编程网站 1998-2004 粤ICP备05016107号
感谢您的惠顾,如有任何建议和意见,请 联系版主2004.3.20

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页