源代码最先是barton大侠作品,太长就不贴了,csdn,delphibbs都有,也可查“封装完成端口的类及测试例子”,其中有完全测试例子,这是网上能找到的最全可用性最好的delphi iocp代码,其他的如iocpclass有网友测试有问题,FISHIOCP我测试也有问题,剩下的要不代码不全(我实在不会补全),要不只有dcu,所以只看这份IOCPComp了。
我修改了几处如下:
1.unit ServerMainUnit;
修改:以前没显示
function TServerMainForm.OnClientRead(ASocket: TCustomSocket;
  AData: Pointer; ACount: Integer): Integer;
begin
  FLock.Enter;   // Enter
  try
    Inc(FRequestCount);
    msgmemo.Lines.add(StrPas(AData));  ///add
    ASocket.write(AData^, ACount);
  finally
    FLock.Leave;   //Leave
  end;
  { 接收到后,再发送回去 }
   Result := 0;
end;修改:以前不能给client发送
procedure TServerMainForm.Button1Click(Sender: TObject);
var
  a:pchar;
  I, Count: Integer;
begin
  a:=pchar(ClientMsgEdit.Text);
  Count := length(a);
  for I := 0 to FServerSocket.ClientCount - 1 do   
    FServerSocket.Clients[I].Write(a^, Count);
end;
2.unit IOCPComp;
增加:以前只有AllocBlock
function TServerClientSocket.RemovBlock(Block:PBlock):Boolean;   
begin
    if Block.Data.IsUse then
    begin
      Block.Data.IsUse := false;
      Block.IsUse := False;
      Exit;
    end;
   FBlock.remove(Block);
   FBuffer.RemoveBlock(Block);
   Block.Data.IsUse := false;
  Result:=true;
end;修改:以前用带的客户端测试一旦断开连接则不能接受数据
procedure TServerSocket.WMClientClose(var Message: TCMSocketMessage);  
var
   ClientSocket: TCustomSocket;
begin
  ClientSocket := FindClientSocket(Message.Socket);
  if (Assigned(ClientSocket)) then
    begin
      FClients.Remove(ClientSocket);  //增加,断开出错,但能继续接受数据
   // ClientSocket.Free;//以前
    end;
end;修改:之前每接受一条数据,内存就增加几k
function TServerClientSocket.PrepareRecv(Block: PBlock = nil): Boolean;   
前面没变省略
  RemovBlock(Block);//最后加了一句
end;
3、下面是问题:
(用带的客户端测试)
并发连接几百个,内存就增加几M,断开后并不减少,这很成问题,希望找到原因,继续改进此代码4.ps
为了比较,我试了dxsock,用以上的客户端测试,并发连接几百个就死掉,不知为什么,还是我不会用

解决方案 »

  1.   

    网上的IOCP组件代码,内存只分配不释放,用完的设置标志回收,达到一定峰值以后基本上不会再增长,你看看TMemoryBuffer的代码就知道了,这样子比反复分配释放要好一点
      

  2.   

    TMemoryBuffer初始化200个block,不够再new,block以isuse为标志,其他地方都有设置Block.isuse,我连接200,断开200,再连在断,内存不断直线增加,不知哪里没有释放
      

  3.   

    服务端的Client没有及时关闭和释放
      

  4.   

    客户端断开的时候,一般服务端OnDisconnect或者OnClientDisconnect事件会被触发,或者OnClientError,在这里执行ClientSocket.Close应该就可以了
      

  5.   

    不是Socket没关闭释放的问题,如果你的通讯量大,似乎在Block的管理上有点问题。这里的代码用空间换取了时间,你调用发送的时候,就把数据写入了缓冲区,函数立刻返回,当通讯量大的时候,很多数据会占用Block,造成Block数量增加。超过定义的数量,会继续申请。所以导致内存增加,但不会无限制的增加。这应该也并没违背开发者的初衷。如果你感觉这个内存的增加不能承受,那就修改下代码吧,自己写个发送的队列,把发送的数据放到自己的队列中,判断Socket的Write的状态情况来自己控制发送数据的频率。
      

  6.   

    不清楚具体情况。个人的写法是采用同长内存分配池化处理。我测试过最高的情况(处理能力不足导致)占用600MB+(个人的程序),但是按照池化管理只要不是因为你投递的未决IO过多,卖到系统因核心内存不足而导致缓慢的话,并且又没有明显的数据缓存不处理,基本上不会存在释放的话。
      

  7.   

    下面是我个人使用的内存管理器(代码有点乱)
    class function TMemoryPool.Attach(  lpMemoryPool: TMemoryPool
                                      ): TMemoryPool;
    var
      ManagementCount : Integer;
    begin
      Result  :=  nil;  if Not Assigned(lpMemoryPool) then
        Exit;  ManagementCount :=  lpMemoryPool.Attach ;
      if ManagementCount = 1 then
        begin
          lpMemoryPool.Detach;
          Exit;
        end;  Result  :=  lpMemoryPool;
    end;
    procedure TMemoryPool.InternalFreeBuffer( lpBuffer  : TDataBuffer
                                            );begin
      if Not (lpBuffer <> nil) then
        Exit;
      HeapFreeEx(lpBuffer);
    end;function TMemoryPool.CreateNewBuffer( dwBytes : DWORD
                                        )  : TDataBuffer;
    var
      DFBufferSize    : DWORD;
      LocalBytes      : DWORD;
      lpDataBuffer    : TDataBuffer;
    begin
      Result        :=  nil;
      LocalBytes    :=  dwBytes;
      try
        if FDefaultBufferSize < 512 then
          DFBufferSize  :=  SystemInfo.dwPageSize
        else
          DFBufferSize  :=  FDefaultBufferSize;    if LocalBytes = 0 then
          LocalBytes  :=  DFBufferSize;
        if LocalBytes < sizeof(_DataBuffer) then
          LocalBytes  :=  DFBufferSize;
        lpDataBuffer  :=  HeapAllocEx(LocalBytes);    if lpDataBuffer = nil then
          Exit;
        Result            :=  lpDataBuffer;
        Result^.Owner     :=  self;
        Result^.BufferLen :=  LocalBytes - sizeof(_BaseDataBuffer);  except
        on E: Exception do
          begin
            WriteLog(   'Exception: TMemoryPool.CreateNewBuffer'
                      + '('+IntToStr(LocalBytes)+')'
                      {$ifdef DEBUG}
                      + '(Thread ID='+IntToStr(GetCurrentThreadID)+',Pool=$'+IntToHex(Integer(self),8)+')'
                      {$endif}
                      +  E.Message);
          end;
      end;
    end;procedure TMemoryPool.Init(const newPoolSize: Integer
                              );
    var
      I         : Integer;
      lpBuffer  : TDataBuffer;
    begin
      if Attach = 1 then
        begin
          Detach;
          Exit;
        end;
      try
        with FMemoryList.LockList do
          try
            for I := Count to newPoolSize  do
              begin
                lpBuffer  :=  CreateNewBuffer;
                if lpBuffer = nil then
                  break;
                lpBuffer^.Using   :=  false;
                lpBuffer^.WhereXY := Add(Pointer(lpBuffer));
                Inc(FLastMember);
              end;
          finally
            FMemoryList.UnlockList;
          end;
      finally
        Free;
      end;
    end;function TMemoryPool.CreateBuffer:TDataBuffer;
    var
      dwSize  : DWORD;
    begin
      dwSize  :=  0;
      Result  :=  CreateBuffer(dwSize);
    end;function TMemoryPool.CreateBuffer(var dwBytes: DWORD
                                      ):TDataBuffer;
    var
      lpBuffer      : TDataBuffer;
      LocalBytes    : DWORD;
    begin
      Result      :=  nil;
      try
        if Not Assigned(FMemoryList) then
          Exit;
        LocalBytes  :=  dwBytes;
        if LocalBytes <> 0 then
          LocalBytes  :=  LocalBytes + sizeof(_BaseDataBuffer);
        if Attach = 1 then
          begin
            Detach;
            Exit;
          end;
        try
          with FMemoryList.LockList do
            try
              if (FLastMember<>FFirstMember) and (FLastUsing<>FLastMember) then
                begin
                  Inc(FLastUsing);
                  lpBuffer  :=  Items[FLastUsing];
                  Result    :=  lpBuffer;
                end;
              if Result = nil then
                begin
                  lpBuffer  :=  CreateNewBuffer(LocalBytes);
                  if lpBuffer = nil then
                    Exit;
                  lpBuffer^.WhereXY := Add(Pointer(lpBuffer));
                  Result            :=  lpBuffer;
                  Inc(FLastMember);
                  FLastUsing        :=  FLastMember;            end;          Result^.Using           :=  true;
              Result^.DataLength      :=  0;
              Result^.CompletedLength :=  0;
              Result^.NextFuffer      :=  nil;
              dwBytes                 :=  Result^.BufferLen;
              Attach;        finally
              FMemoryList.UnlockList;
            end;
        finally
          Free;
        end;
      except
        on E: Exception do
          begin
            WriteLog('Exception: TMemoryPool.CreateBuffer; '+ E.Message);
          end;
      end;end;procedure TMemoryPool.FreeBuffer(     lpBuffer  : TDataBuffer
                                    );
    var
      lpTempBuffer  : TDataBuffer;begin
      if Not (lpBuffer <> nil) then
        Exit;
      if Not Assigned(lpBuffer^.Owner) then
        Exit;
      if lpBuffer^.Owner <> self then
        begin
          lpBuffer^.Owner.FreeBuffer(lpBuffer);
          Exit;
        end;
      if Attach = 1 then
        begin
          Detach;
          Exit;
        end;
      try
        with FMemoryList.LockList do
          try
            if Not lpBuffer^.Using then Exit;
            lpBuffer^.Using :=  false; 
            if FLastUsing >= 0 then
            begin
              lpTempBuffer  :=  Items[FLastUsing];
              if lpTempBuffer <> lpBuffer then
                begin
                  lpTempBuffer^.WhereXY         :=  lpBuffer^.WhereXY;
                  lpBuffer^.WhereXY             :=  FLastUsing;
                  Items[FLastUsing]             :=  lpBuffer;
                  Items[lpTempBuffer^.WhereXY]  :=  lpTempBuffer;
                end;
              Dec(FLastUsing);        end;
          finally
            FMemoryList.UnlockList;
            self.Free;
          end;
      finally
        Free;
      end;
    end;procedure TMemoryPool.FreeMultiBuffers(     lpBuffer  : TDataBuffer 
                                          );
    var
      lpTmpBuffer,  lpTmpBuffer_Free  : TDataBuffer;
    begin
      lpTmpBuffer :=  lpBuffer;
      if Not (lpTmpBuffer <> nil) then
        Exit;
      while (lpTmpBuffer <> nil) do
        begin
          lpTmpBuffer_Free  :=  lpTmpBuffer;
          lpTmpBuffer       :=  lpTmpBuffer^.NextFuffer;
          FreeBuffer(lpTmpBuffer_Free);
        end;end;procedure TMemoryPool.RemoveBufferNode(lpBuffer: TDataBuffer);
    var
      I   : Integer;
    begin
      if Not (lpBuffer <> nil) then
        Exit;
      if Not Assigned(lpBuffer^.Owner) then
        Exit;
      if lpBuffer^.Owner <> self then
        begin
          lpBuffer^.Owner.RemoveBufferNode(lpBuffer);
          Exit;
        end;
      if Attach = 1 then
        begin
          Detach;
          Exit;
        end;
      try
        with FMemoryList.LockList do
          try
            for I := Count - 1 downto 0 do
              begin
                if lpBuffer = Items[I] then
                  begin
                    Delete(I);
                    InternalFreeBuffer(lpBuffer);
                    Dec(FLastMember);
                    break;
                  end;
              end;
          finally
            FMemoryList.UnlockList;
          end;
      finally
        Free;
      end;
    end;function TMemoryPool.Attach:Integer;
    begin
      Result  :=  InterlockedIncrement(FManagementCount);
    end;function TMemoryPool.Detach:Integer;
    begin
      Result  :=  InterlockedDecrement(FManagementCount);
    end;constructor TMemoryPool.Create;
    begin
      Inherited Create;
      FManagementCount  :=  1;
      FLastMember       :=  FFirstMember;
      FLastUsing        :=  FFirstMember;
      FDefaultBufferSize:=  DEFAULT_BUFFER_SIZE;
      FMemoryList       :=  TThreadList.Create;
    end;destructor TMemoryPool.Destroy;
    var
      I     : Integer;
    begin
      with FMemoryList.LockList do
        try
          for I := 0 to Count - 1 do
            begin
              InternalFreeBuffer(Items[I]);
            end;
        finally
          FMemoryList.UnlockList;
          FMemoryList.Free;      
        end;end;procedure TMemoryPool.Free;
    begin
      if Detach>0 then
        Exit;
      Inherited Free;
    end;
      

  8.   

    根本没必要自己写分配。Delphi的内存管理器,本身就有缓冲机制。
      

  9.   

    内存管理有 FastMM4socket 用 IOCP 实用性不大, 现实编程中反而更麻烦这里有一个我们在开发中的 socket1.1 兼容组件, 支持多线程模型, 符合 TServerSocket 使用习惯, 大容量连接这个 sourceforge.net 又被封了, 郁闷ing
    最新代码用 cvs checkout
    -----------------------------------------------------------------------------------------------------------------------------
    www.sourceforge.net/projects/uvc
    uvc is a socket1.1 compatible delphi/kylix component
    support multiple threading module, request/fdset/socket per thread, 10k tcp connection on request per thread model, and socks
      

  10.   

    socket 用 IOCP 实用性不大, 现实编程中反而更麻烦 
    是这样么?
      

  11.   

    FastMM只能是比原来Delphi的那个BorlandMM稍好一点,但是并不利于高性能系统的开发。这个问题在我们现有的系统当中普遍存在。具体如何可以自己行测试,在单CPU的系统下问题并不明显,但是在多CPU的系统下(我们当时使用的是Intel提供的两台服务器,一台2CPU*4核心,一台是8CPU*4核心(这也是08年他们的主推型号,具体型号不记得了),当分配给Delphi的应用程序一个CPU核心的时候处理能力是3000笔/秒,当核心数越增加,反倒处理能力在下降,CPU和内存也不耗用。而换用内存池,则可以从原来的3000笔/秒,直接提升到8~9000笔以上。但是还是有提升空间,那就是对部分频繁使用的内存采用TLS(线程局部存储)管理,增另内存管理器数量,以分散线程间的碰撞。修改之后,可以达到12000笔/秒.IOCP对于普通的应用程序作用是不太大,但是对于作为承载1000以上用户量的情况,或者有大量数据流转的情况,就会好很多,虽然Overlapped模式已经有所提升,但是Overlapped的Routeine模式需要在起发请求的线程当中处理,使得越忙的线程忙死,而越闲的线程则闲死的不合理情况。Overlapped的WSAWaitForMultiObjects大家都知道存在一个Maxinum限制,并不利于管理。另外建议了解一下IOCP本身的一些特性。而不是空口说它没有实用性。
      

  12.   

    IOCP其实是想当简单的,就两个核心的函数:CreateIoCompletion,GetQueuedCompletion.虽然有很多文章都讨论过大用户量的情况。但是那个只是针对原来winSocket 1.0/1.1提升的解决大用户的问题,而并没有最终考量性能。
      

  13.   

    谢谢unsigned给出内存管理代码,comanche的uvc项目,各位可能都有自己的iocp实现,但我还想看看baton的iocp代码哪里可以改进利用,原因还是“最全最可用”。
    又发现一点问题
    unit IOCPComp;
    function TMemoryBuffer.AllocBlock: PBlock;
    var
      I: Integer;
    begin
      FSocket.Lock;
      try
        Result := nil;
        for I := 0 to FList.Count - 1 do
        begin
         Result := FList[I];
         if  Result.IsUse = false then
           break;  //应该为exit,当改过后又有问题
        end;
         New(Result);
         FList.Add(Result);
         FillChar(Result.Data, SizeOf(Result^.Data), 0);
         Result.IsUse := True;
      finally
        FSocket.UnLock;
      end;
    end;如哪位有更多改进,可发布在这
      

  14.   


    function TMemoryBuffer.AllocBlock: PBlock;
    var
      I: Integer;
    begin
      FSocket.Lock;
      try
        Result := nil;
        for I := 0 to FList.Count - 1 do
        begin
          Result := FList[I];
          if not Result.IsUse then
            break;
        end;
        if not Assigned(Result) or Result.IsUse then
        begin
          New(Result);
          FList.Add(Result);
        end;
        FillChar(Result^.Data, SizeOf(Result^.Data), 0);
        Result^.IsUse := True;
      finally
        FSocket.UnLock;
      end;
    end;
      

  15.   

    这个~~, 说 IOCP 不实用不是为了推我们在搞的组件嘛IOCP 在 windows 用的人很多不过
    socket 原生库是 BSD 3.4 提供的 1.1 版, 之后产生大量应用, windows 同样也是用的这个改过来的
    Overlapped & IOCP 是在 windows 对普通 IO 操作上做的一次改进,也同样适用于socket实不实用的不说, 这个会被人骂死, 但既然 windows 能实现, 我们同样也能实现一个高效的, 巨大连接数的组件UVC 的 request per thread 能支持非常大数量的连接, 现在已经有几个应用达到了 1w+, 效率也不低于完成端口(当然跟程序质量有一定关系,没什么可比性,作为测试是有过的)另外说 fastMM4 在多线程上的确有非常大的提升(有时有几十倍的差别, 线程多非常明显, 人为感觉得到), 服务器程序嘛, 单线处理能力并不重要, 关键还是多线的, 从结论上看 fastmm 的确对碎片问题有极大的改进(OS运行时间更长), 多线内存分配时间上基本不会因为运行时间变化而有改变(App性能最大化), 这个也不用吹, fastmm4 在 sourceforget.net 上的历史也可以证明了, 另外开发也不是不对, 作为学习嘛
    但作为服务稳定考虑, 我是绝对不会随便找一个代码就用的, 也不会自已介入这种无法确定结果的项目
      

  16.   

    而且 UVC 跨平台, kylix 下也能用
    并且可能在 Lazarus(基于 freepascal) 运行, 目前语法上都兼容, 但 Lazarus 组件机制还不了解
      

  17.   

    你可以用来强调跨平台,但是并不是所有人都需要进行跨平台。我也从来不相信跨平台良好的会比平台化的效率更高。仁者见仁,智者见智,多的就不说了。但是说IOCP不用实,实现太复杂,那只能给四个字的平价:天方夜谭
      

  18.   


    function TMemoryBuffer.AllocBlock: PBlock; 
    var 
      I: Integer; 
    begin 
      FSocket.Lock; 
      try 
        Result := nil; 
        for I := 0 to FList.Count - 1 do 
        begin 
          Result := FList[I]; 
          if  Result.IsUse = false then  begin
            Result.IsUse := True;//加上这一句
            Exit;  //应该为exit,当改过后又有问题
          end 
        end; 
        New(Result); 
        FList.Add(Result); 
        FillChar(Result.Data, SizeOf(Result^.Data), 0); 
        Result.IsUse := True; 
      finally 
        FSocket.UnLock; 
      end; 
    end; 不要每次都是for i := 0 to FList.Count-1,否则当存在几百上千甚至上万之后,每次都从头做一次扫描,那效率还有吗?其实当初我也是这样写的。后来想想,这样子写的目的,不过是释放的时候,比较方便,但是对于申请,这样子一次扫描,远比直接的内存管理要慢得多,那这个内存池就完全失去了意义。虽然他在实现上成分离到Socket上(存在的问题暂时不分析),但是这还是几个几十个的扫描还是存在的。换而言之,象我上面的实现方法,每次都把空闲的放在最后面,只是在释放内存的时候,比直接修改IsUse标志位要消耗大一点,但是申请的效率高得多了,只是一个指针的转移,比扫描要好得多。另外,这个每一个Socket上面都排一个内存池,这个对于都是频繁使用并且长时间在线(长连接)的Socket当然并没有什么太大问题,但是实际应用当中对于不同的应用环境这个就有一个相当大的差异。一般的应用,活动用户一般占全部在线用户当中的2%~30%不等,对于那些间歇性忙录的Socket,很有忙的时候分配了几十个单位,而实际应用当中则只有几个。这是很明显的内存浪费。
      

  19.   

    comanche的UVC借这次机会知道了,一定去试下。unsigned回答问题总那么细心热情,不愧是三颗星。本人也不是搞计算机的,这次要做个gps gprs服务器程序,才弄出这些动静,也长了些见识。再次感谢各位。如有软件外包的可能也可接受。QQ:744157426
      

  20.   

    真巧,我也是做GPS GPRS终端通讯机的时候才用到IOCP,barton的代码里面漏洞太多,自己改完以后,发现效率也不怎么样,不过还算稳定,继续修改中...    if  Result.IsUse = false then 
          break; //这里不Exit,主要是为了执行后面的FillChar和IsUse := True,有没有用就要自己决定了
        end; 
        New(Result); 
        FList.Add(Result); 
        FillChar(Result.Data, SizeOf(Result^.Data), 0); 
        Result.IsUse := True; 
      

  21.   

    function TMemoryBuffer.AllocBlock: PBlock; 
    var 
      I: Integer; 
    begin 
      FSocket.Lock; 
      try 
        Result := nil; 
        for I := 0 to FList.Count - 1 do 
          if not PBlock(FList[I])^.IsUse then
          begin
            Result := FList[I];
            Break;
          end;//if
        if Result = nil then
        begin
          New(Result); 
          FList.Add(Result); 
        end;//if
        FillChar(Result.Data, SizeOf(Result^.Data), 0); 
        Result.IsUse := True; 
      finally 
        FSocket.UnLock; 
      end; 
    end; 
      

  22.   

    其实从客户端连接从Accept到CloseSocket,Recv的时候用到的Block一直都是那一个,这个其实可以分开管理,频繁回收与分配主要是因为Write,我碰到突然一下暴涨的情况,都是因为IOCP"完成"不及时,又要不断的写,所以不断分配.其实IOCP的完成通知还是非常快的,只是我在Send完成时还做了一些处理,其中需要用临界区来保持线程同步,结果等待得差点崩溃.我做过模拟测试,接近6000个连接,每个连接4-10秒发送40-260字节数据,服务端收到不做处理直接写回,服务端CPU占用50%上下(PM1.4G,2GDDR),数据收发基本正常.实际运用中,效率主要低在多线程数据处理的同步上,大量时间浪费在临界区的等待中,不知道unsigned有没有什么好建议...
      

  23.   

    在可能的情况下,尽可能避开线程的碰撞,比如前面说的,一些频繁使用的结构,比如很多人比较习惯使用TStringList做临时的数据分解,通常情况下都是用的时候创建,这样子明显是一种不必要的消耗,那么在这个时候,可以分派一个公用的TStringList设置为ThreadVar,这样子就是一个线程局部存储当中的对象,能够访问到的就只是你这个线程,自然也就不需要考虑冲突。在很多的时候,在环境允许的情况下,我们更多的会选择使用空间来换取时间的方式来处理问题。就目前来讲,只要不是非常非常特殊的应用环境基本上一个专用的程序使用个几百兆的内存很正常。关键是这些内存是被正常使用的,也许会存在一些闲置,只要不是因为内存碎片等造成的资源浪费就算是合理的。当然自人都有自己的管理方式。
      

  24.   

    是发包请人帮忙做,zzlingaaa同行多交流
      

  25.   

    unsigned 有兴趣,是否可以考虑一下
      

  26.   

    呵呵, 我也可以接我手上刚好也有一个 gps 项目, 用的是鹰之眼车载设备 
    目的是代替原来的一个版本, 刚调稳300~500左右终端, 目前cpu占用(4核xeon) < 0.1%, 实际终端, 所以数据量也不是很大, 300终端左右执行拍照, 平均i/o 10~15k/s非要用完成端口做的就不要考虑我了, 完成端口虽然我也做过, 但对调稳信心就不是很足..可能是能力上的问题以下是条款,接受的可以联系内存管理用原始的或 fastmm4, 目前其它也不打算试, 性能你不用当心开发用就是用 uvc 组件, 因为在形成组件前有很长时间的积累, 所以用着习惯, 这个因为放在 sf, 所以也是全开放, 目前大容量程序终止得很慢(有线网没这方面问题)gps/gis server 部分不参与, 这个工程量巨大, 要一个小组上百工作日才能完成, 人在深圳合作另说, 但不排除不接受可能性程序的模式由我界定(就是输出界面),理由可以解释, 但不能商量, 做过太多这类, 好处你以后自然知道收费在 10-15k 视情况, 一般为10工作日以内完成代码, 30工作日左右试运行因为目前一个同类项目正在进行中, 要知道你的公司名等, 不存在竞争的, 要得到合作方同意才能参与, 答复是3天联系方式是 [email protected]  msn or mail;
      

  27.   

    上面忘了说接受的是 ipserver 部分的代码, 终端 <--> ipserver <--> gps/gis server
    代码用 cvs 共同所有, 版权共有, 但本人不用于商业用途, 本人之后在不直接使用你版本的代码的前提下, 不受竞业约束
      

  28.   

    ipserver <--> gps/gis server,分得挺细的...
    我现在做的这个程序简直就是一个大杂烩,终端通讯,GIS客户端通讯,数据存储,全包了,刚刚看了其中一个服务器,800+GPRS终端,20左右GIS客户端,跑了424个小时,平均每秒接收终端数据才2K(主要这些终端大多不带摄像头),每小时执行10W条SQL,酷睿2双核2.0G,占CPU20%左右,除了偶尔发生内存飙升的问题以外,还算正常...
    至于说gis server开发需要很多时间,站在我的角度是没法理解,只是把终端数据转发一下,没那么困难吧,呵呵
      

  29.   

    还在线哪, 加我 msn 吧, or qq 好了 349287437, 做不做的最终参考参考
      

  30.   

    gis/gps 这部分单一个 server 倒是没什么, 全结合起来还是很费事的, 怎么说也是地理信息系统嘛, 下面还原地图什么的, 定位, 报警, 监控~~
      

  31.   

    哦,你说的是GIS啊,弄地图那是相当麻烦...