RT  ... 现在要做一个上传的模块,打算用这两个组件来实现..可是从来没用过这两个东东,也没搞过网络编程这方面的东西....
现在有些问题问下.各位达人帮帮忙...
1.客户端要求是每个文件支持多线程上传,要求线程数可配置.(即如果有多个用户同时上传,则为每个用户单独开一线程来接收)
2.客户端要求能支持断点续传功能,客户端再连接到网络的时候自动把还没传完的文件传完.
3.客户端连接到服务器端的时候会进行身份认证而且客户端的登录帐号和密码是加密.
4.客户端发到服务端的文件需要进行文件数据完整较验.如果失败通知客户端重新发送文件,服务器端会把不完整文件删除.
5.服务器接收的并发数可配置,最高要求1000个.
6.服务端要做好统计数量的接口,统计项目如下:上传/下载速度(KB/S),已上传的文件大小(KB),登录上传的用户名,上传的文件路径.文件传输完共用的时间,上传文件的日期和时间
7.端口监听和释放,即其中一个用户传输完毕,则释放相应的端口.
-------------------------------------------------------------
真诚的请教各位....如果有实现过,麻烦帮忙说一下其中的原理,比如多线程的创建和调用,当有用户发出请求时就要创建一个线程.都要注意些什么,具体的代码我想自己实现
分不够再加.....

解决方案 »

  1.   

    1.把TClientSocket控件的创建和连接都写到线程里去
    2.可以这样,如果只有一个线程的话,从文件的开始下载,下载长度为文件的长度,如果已经存在一个线程的话,从文件的中间下载,然后把前一个线程要读取的长度告诉给第一个线程,即第一个文件要下载的长度为文件长度除2,依次类推,这是动态的。不过一般不这样进行。
    3.预先定义好要创建的线程数,然后把要下载的文件分成几部分,分别下载,下载完成后把分别下载的部分合并
    4.如果断点续传的话,首先判断有几个线程,方法是查找有几个文件即可,然后把要下载的文件分成几个部分,判断各个线程文件的长度,即得要要下载的起始位置,然后每个线程关联一个文件,从起始位置开始下载指定的长度即可,最后合并
      

  2.   

    你这个一个端口的就可以了,如果在服务端创建线程,
    第一步:把ServerSocket1.ServerType设成stThreadBlocking
    第二步,在主线程中循环接收命令数据
    第三步,如果有客户发过来请求下载文件命令,则根据Socket: TCustomWinSocket创建该用户的线程,然后处理文件即可,这个Socket负责这个线程的接收和发送
      

  3.   

    给楼主一个建议,使用indy里面的idTCPServer直接就可以了,不用这么麻烦
      

  4.   

    谢谢你一直这么热心.....
    那idTCPServer 这两个控件能否实现我要的那些功能?
    具体的实现步骤能否讲一下?谢谢!
      

  5.   

    给你个例子吧
    这个是客户端的
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
      IdTCPClient;type
      TForm1 = class(TForm)
        btnSend: TButton;
        Edit1: TEdit;
        Memo1: TMemo;
        btnConnect: TButton;
        IdTCPClient1: TIdTCPClient;
        btn001: TButton;
        btn002: TButton;
        procedure btnConnectClick(Sender: TObject);
        procedure btnSendClick(Sender: TObject);
        procedure btn001Click(Sender: TObject);
        procedure btn002Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
        TClientHandleThread = class(TThread)
      private
        cmdstr:string;
        procedure HandleInput;
      protected
        procedure Execute; override;
      end;
    var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btnConnectClick(Sender: TObject);
    begin
    try
    IdTCPClient1.Host:='127.0.0.1';
    IdTCPClient1.Port:=5000;
    IdTCPClient1.Connect;
    showmessage('连接成功');
    except
    showmessage('连接失败');
    end;
    end;procedure TForm1.btnSendClick(Sender: TObject);
    begin
    IdTCPClient1.WriteLn('003');
    IdTCPClient1.WriteLn(edit1.Text);
    end;{ TClientHandleThread }procedure TClientHandleThread.Execute;
    begin
      inherited;
      while not Terminated do
      begin
        if not form1.IdTCPClient1.Connected then
          Terminate
        else
        try
          cmdstr:=form1.IdTCPClient1.Readln;
          Synchronize(HandleInput);
        except
        end;
      end;
    end;procedure TClientHandleThread.HandleInput;
    begin
    if cmdstr='001' then
       begin
       form1.Memo1.Lines.Add('I am 001');
       end;
    if cmdstr='002' then
       begin
       form1.Memo1.Lines.Add('I am 002');
       end;
    end;procedure TForm1.btn001Click(Sender: TObject);
    begin
    IdTCPClient1.WriteLn('001');
    end;procedure TForm1.btn002Click(Sender: TObject);
    begin
    IdTCPClient1.WriteLn('002');
    end;
      

  6.   

    这个是服务器端的
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPServer;type
      TForm1 = class(TForm)
        btnlogin: TButton;
        Memo1: TMemo;
        Edit1: TEdit;
        btnSend: TButton;
        IdTCPServer1: TIdTCPServer;
        procedure btnloginClick(Sender: TObject);
        procedure IdTCPServer1Execute(AThread: TIdPeerThread);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btnloginClick(Sender: TObject);
    begin
    IdTCPServer1.DefaultPort:=5000;
    IdTCPServer1.Active:=true;
    end;procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    var
    str:string;
    begin
    str:=AThread.Connection.ReadLn;
    if str='001' then
       begin
       showmessage('001 cmd');
       AThread.Connection.WriteLn('001');
       exit;
       end;
    if str='002' then
       begin
       showmessage('002 cmd');
       AThread.Connection.WriteLn('002');
       exit;
       end;
    if str='003' then
       begin
       memo1.Lines.Add(athread.Connection.ReadLn);
       exit;
       end;
    end;end.
      

  7.   

    当客户端连到服务器时,服务器程序可以把不同连接到的IP存起来。IP的个数就是连接的个数。如果要限制连接就,看IP数据是不超过了
    最大允许数;如果超出就给客户机返回一个状态告诉客户机已经不能连了,同时结束其它操作.我想用户名和密码的验证应该很容易就搞定了吧?! 至于连接还是断开,只是给客户机返回两种不同的状态而已。
    断点续传,其实可以简单的实现: 客户机每次发文件数据包给服务器的时候,带上数据体的大小和在文件中位置的信息,服务器在接收时,
    以这个数据体的大小及位置信息写到相应的接收文件中就可以了。注:再实现的时候用Indy的idTcpServer和idTcpClient控件来作,这样在服务器返回要好实现一点(直接在idTcpServer.OnExecute
    事件里就要实现)。
      

  8.   

    对于一个连接(一个用户),TidTcpServer会开辟一个线程(执行一次OnExcute事件)来处理的;所以有多个用户,不用关心,你一
    次处理好一个用户的验证就好了,但是要注意处理可能存在的资源冲突
      

  9.   

    客户端在上传文件的时候,最好一个文件传,放到一个线程里传。如果把文件分块让多个线程来传,控制起来要麻烦得多。
    在传文件时,客户端大概过程如下,服务端就接收返回相应状态就可以了:procedure TXXXThread.OnExecute(Sender: TObject);
    type
      TFileBlock = record
        FileName: string[225];
        Start: Integer;
        Size: Integer;
        BlockData: array[1..1024] of byte;
      end;
    var
      ABlock: TFileBlock;
      Remain: Integer;
      AFile: TFileStream;
      Flag: Integer;
    begin
      //.....
      ABlock.Start := XXX//从传输日志取已经传完的位置信息;
      ABlock.FileName := YourFileName;
      repeat;
        AFile.Position := ABlock.Start;
        ABlock.Size := AFile.Read(ABlock.BlockData, SizeOf(ABlock.BlockData));
        ABlock.Start := AFile.Position;    IdTCPClient.WriteBuffer(ABlock, SizeOf(ABlock));
        IdTcpClient.ReadBuffer(Flag, SizeOf(Flag));//由服务器接收返回
        if Flag = 成功 then
        begin
          记录当前传输的日志
          Remain := AFile.Size - ABlock.Start;
        end
        else
        begin
          Self.Terminate;
          Break;
        end;
      until Remain <=0;
    end;注:楼主还先在网上下载一些示例代码,再学一下控件。然后再看看考虑考虑说不定问题就解决了
      

  10.   

    200分的问题果然有料...在这个问题上,建议楼主一步步实现。不要想着一次性完全做好。
    第一步实现简单上传,一个用户对应一个连接,同时一个文件,并做好相应的其它功能,包括日志等等。对于客户端来讲,多线程,其实就是多个连接,也就是需要多个TClientSocket来参与。这个只是把原来单一的一个,复制成了多份,然后放在不同的线程里。第二步,断点,也就是分段。第三步,对多个连接增加一个登录标识,就好象是原来多个连接的一个客户,进行了一个统一地计划,而划分到一个连接组当中,当然最方便的方法就是为同时在传输同一个文件的多个连接划分在一起。
      

  11.   

    以前构思过一个类似的系统,不过没有去实现大致的想法是:
    服务器有一个公开端口用来监听请求,比如5000.客户端连接都往5000这个端口发送连接请求.服务器端验证并且通过后,创建线程,线程里bind一个新的空闲端口,比如40000,并给客户端回消息让客户端更改服务器通讯端口.线程启动后,客户端实际是与服务器的40000端口在通讯,而主线程继续监听5000端口.
    linux下socket通讯很多都是这样做的,只不过linux下是fork出一个进程来处理,而delphi里一般创建线程来处理.
    端口冲突你不用担心,可以让系统自动分配可用端口.实际上你应该不用担心要在服务器维护一个庞大的客户端列表.idtcpserver本身就可以支持与多个客户端通讯,所以你可以给每个客户端创建一个idtcpserver,这样每个客户端的多线程也可以解决.
      

  12.   

    以前有个项目和你的需求大致相似,当时我和一起做的朋友专门讨论过。觉得这种方式会比较好一些。将监听和业务用不同端口主要是方便管理,也安全一些。而且主线程阻塞的话不会影响已经建立的通讯。
    当然,这些仅限于当初的方案设计。当时考虑的主要是负载问题,我们的方案设想在负载较大时可能要创建进程而不是线程,因为创建进程可以更好的抢占系统资源。后来因为一些原因项目还没正式启动就搁置了,只有个java写的服务器端的最初测试程序,但后来弄丢了。
      

  13.   

    如果你用idtcpserver的话,可以只用一个端口与多个客户端通讯,idtcpserver本身有这个机制.
    服务器开多线程主要是方便管理,因为你有多用户且每个用户还有多线程,所以干脆给每个用户创建个idtcpserver并分配独立端口通讯比较好一些.
      

  14.   

    1.Accept到的客户连接是不占用端口资源的,通讯仍然往同一个端口,这样可以大大节省端口资源,之所以说某些服务器可以达到上十万的用户量,就是因为它不受端口(只有六万多)的约束。至于多线程,那只是业务处理上面的一个性能需求。一个线程可以管一个也可以管多个连接的通讯。重要的是在多核心多CPU的环境当中可以将CPU资源利用起来,这才是多线程的目的。
    2.分块传输,这不应该是服务器干的活,而是客户端干的,客户端只要指明了文件是哪一个,当前传上来了多少数据,是从文件哪一个位置(Position)开始的就可以了。
    3.就是CreateFileMapping就可以,这样子一个线程控制一个文件的句柄,可以随意地移动文件的位置指针,而不影响其它线程。
      

  15.   

    服务端是否使用多线程并没有太大的关系,实际上我们通常写文件都是以禁写的方式打开的,但是对于这种应用来讲,我们就要使用允写方式打开,在写入之前只需要使用LockFile/LockFileEx锁定我们需要写入的那一分片对应的小段就可以了,如此,其它线程如果操作的分片只要跟这个分片不相重叠的话,就不受影响,各自就可以安好地并行。
      

  16.   

    to : skylkj
    我觉得这种方法也行,只是操作起来太复杂和繁琐了...
    to :unsigned
    如你所说客户端用多线程发送文件块,那在服务端它怎么将这些写入文件?在 IdTCPServer1Execute 这里进行写文件就可以了吗?
    还有就是,你是采用什么方式来断点续传的?也是用记录文件写入的信息吗?
      

  17.   

    IdTCPServer没用过。没有做过断点传输。
      

  18.   

    c/s模式的TCPserver and TCPclient文件傳輸很簡單的吧,一個發送,另一個接收。