版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://samver.blogbus.com/logs/57192157.html //客户端: //定义了一个常量: const BLOCK_SIZE = 1024;//把文件分开来传,每一次传送1024个字节 //然后在Form上放一个TTcpClient和一个按钮,为按钮的点击写处理过程: procedure TForm1.Button1Click(Sender: TObject); var sFileFullName, sFileName : string; //带路径的文件名, 文件名 iFileLen : LongInt; //文件大小 sMsg : string; //接收到的消息 i, cyc : Integer; //循环传送次数 FileStem : TMemoryStream; //文件数据 PBuf : Pointer; acBlockSize, tmpSize, LastBlockSize : Integer; //实际读取的数据大小 ,从Stream中际读取的大小,最后一个数据块的大小 begin if dlgOpen1.Execute then begin sFileFullName := dlgOpen1.FileName; end; if sFileFullName = '' then Exit; sFileName := ExtractFileName(sFileFullName); //从带路径的文件名中提取文件名 FileStem := TMemoryStream.Create; try FileStem.LoadFromFile(sFileFullName); iFileLen := FileStem.Size; sMsg := sFileName + '^' + IntToStr(iFileLen); //拼装消息:文件名+^+文件大小 TcpClient1.RemoteHost := edt1.Text; //从Edit中取服务器地址 TcpClient1.RemotePort := '32765'; //要连接的端口 TcpClient1.Open; //连接服务器 if TcpClient1.Connected then begin with TcpClient1 do begin Sendln(sMsg); //发送拼装好的消息 sMsg := Receiveln(CRLF); if iFileLen mod BLOCK_SIZE = 0 then cyc := iFileLen div BLOCK_SIZE //把文件分块进行传输,如果文件大小能被定义好的BLOCK_SIZE整除,就循环iFileLen 除以 BLOCK_SIZE那么多次就行了 else cyc := iFileLen div BLOCK_SIZE + 1; //把文件分块进行传输,如果文件大小不能被定义好的BLOCK_SIZE整除,要多传一次,把剩下的数据块传完 if sMsg = 'OK' then //服务器已经准备好,下面开始发送数据 begin GetMem(PBuf,BLOCK_SIZE); //分配内存 try for i := 1 to cyc do begin if i = cyc then begin LastBlockSize := iFileLen - FileStem.Position; //如果已经传到最后一块了,计算好最后一块的大小 tmpSize := FileStem.Read(PBuf^,LastBlockSize); acBlockSize := SendBuf(PBuf^,LastBlockSize); end else begin tmpSize := FileStem.Read(PBuf^,BLOCK_SIZE); //发送正常的数据块 acBlockSize := SendBuf(PBuf^,BLOCK_SIZE); end; //} end; finally FreeMem(PBuf); end; end; end; end; TcpClient1.Close; finally FileStem.Free; end; end; //服务器端: //同样也要定义一个常量: const BLOCK_SIZE = 1024;//把文件分开来传,每一次传送1024个字节 //在Form上放一个按钮,Caption为“开始监听” procedure TForm1.Button1Click(Sender: TObject); begin TcpServer1.LocalPort := '32765'; TcpServer1.Open; Button1.Enabled := False; end; 在Form上放一个TTcpServer,为它的OnAccept编写事件: procedure TForm1.TcpServer1Accept(Sender: TObject; ClientSocket: TCustomIpClient); var MsgList : TStringList; //消息列表 FileStem: TMemoryStream; //文件流 sFileName : string; //文件名 iFileLen : LongInt; //文件大小,最大为2G sMsg : string; //接收的消息,用于商量传输过程 PBuf : Pointer; //内存块指针 i, cyc : Integer; //cyc是计算出来的,一个文件要循环传送多少次 acBlockSize,tmpSize,LastBlockSize : Integer; //实际读取的数据大小 begin MsgList := TStringList.Create; FileStem := TMemoryStream.Create; try with ClientSocket do begin sMsg := Receiveln(CRLF);//接收消息 MsgList.CommaText := StringReplace(sMsg,'^',',',[rfReplaceAll]); //折分消息,如果文件名上有空格,Delphi会把空格也作为分隔符,这好像是Delphi中的一个BUG。 if MsgList.Count < 2 then begin Sendln('Error'); Exit; end else begin sFileName := MsgList.Strings[0]; //文件名 iFileLen := StrToIntDef(MsgList.Strings[1],-1);//文件大小 if (sFileName = '') or (iFileLen = -1) then begin ShowMessage('文件大小为负或文件名不全,不接收'); Exit; end else begin if iFileLen mod BLOCK_SIZE = 0 then cyc := iFileLen div BLOCK_SIZE //文件大小能被块大小整除,循环次数就是文件大小除以块大小 else cyc := iFileLen div BLOCK_SIZE + 1; // 文件大小不能被块大小整除,循环次数就是文件大小除以块大小 + 1 Sendln('OK'); //文件名和文件大小获取成功,发送回执 GetMem(PBuf,BLOCK_SIZE); try for i := 1 to cyc do begin if i = cyc then begin LastBlockSize := iFileLen - FileStem.Size; //最后一块,计算最后一个数据块的大小 acBlockSize := ReceiveBuf(PBuf^,LastBlockSize); //从Socket接收内容 tmpSize := FileStem.Write(PBuf^,acBlockSize); //写到memoryStream 中 end else begin acBlockSize := ReceiveBuf(pbuf^,BLOCK_SIZE); //接收正常的数据块 tmpSize := FileStem.Write(PBuf^,BLOCK_SIZE); end; end; finally FreeMem(PBuf); end; FileStem.Position := 0; FileStem.SaveToFile('d:/Tcptmp/' + sFileName); //保存到硬盘中 end; end; end; finally MsgList.Free; FileStem.Free; end; end;