Q&A

  • 파일전송 소켓프로그램 확인부탁합니다..
안녕하새요... 초보입니다..
소켓전송 자료를 참고를 작성하였습니다..
서버에서 클라이언트로 파일을 1:N으로 전송하는 프로그램입니다..

단독으로는 수행이 잘됩니다.. 하지만. 멀티로 구성하여 시간차를 주고 수행을 하면
각 다른 클라이언트에서 같은 파일에 대한 다운을 수행할 때는 항상 서버측에서 FileStream에서'Can't Open'
에러가 발생합니다...

고수님 소스 확인 부탁합니다..
수고하세요...

[서버]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ScktComp, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ServerSocket1: TServerSocket;
    StatusBar1: TStatusBar;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ServerSocket1ClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientDisconnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ServerSocket1ClientError(Sender: TObject;
      Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
      var ErrorCode: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Telegram(Kind,m: integer);
  end;

Type TClientInfo = record
        Handle  : Integer;
        Ip      : String;
        FStream : TFileStream;
        Used    : Boolean;
        Point   : Integer;
end;

Type TFileInfo = record
      Name : String;
      Size : Integer;
      Date : String;
      Time : String;
end;

const MAXCLIENT = 99;
const TRANSSIZE = 4096;
var
  Form1: TForm1;
  ConnectCnt : Integer;
  ClientInfo : array [0..maxclient] of TClientInfo;
  SendBuffer, RecvBuffer: array[0..TRANSSIZE] of Char;
  FStream:  TFileStream;
  FileName, TaFileName: String;
  FileSize, FileMok, FileNam, TransCount: integer;
  FileInfo : TFileInfo;

implementation

{$R *.DFM}
function ReturnFileCheck(Fin:TFileInfo):Integer;
var
    SearchRec: TSearchRec;
    ChkDate,ChkTime : String;
begin
if FindFirst(Fin.Name, FaAnyfile, SearchRec) = 0 then
  begin
   ChkDate := FormatDateTime('yyyymmdd', FileDateToDateTime(SearchRec.Time));
   ChkTime := FormatDateTime('hhmm', FileDateToDateTime(SearchRec.Time));
   if (Fin.Time = ChkTime) and (Fin.Date = ChkDate) then
         Result := 1
   else  Result := 2;
  end
else    Result := 0;
end;

function ReturnFileSize(FileName: String): integer;
var SearchRec: TSearchRec;
    Re: integer;
begin
  Result:= -1;
  Re := Sysutils.FindFirst(FileName, faAnyFile, SearchRec);
  if Re = 0 then Result:= SearchRec.Size;
  FindClose(SearchRec);
end;

procedure GetFileInfo(rcvStr: String);
var info : string;
begin
   //COPYSTART[#11]FILENAME[#12]FILEDATE[#13]FILETIME[#14]FILESIZE[#15]
   info := Copy(RecvBuffer, 1, pos(#15, RecvBuffer)); // 받은 버퍼값을 저장
   delete(info, 1, pos(#11, info)); // 전송시작부분 잘라냄 COPYSTART부분
   with FileInfo do
   begin
        Name := copy(info, 1, pos(#12, info) - 1);
        delete(info, 1, pos(#12, info));
        Date := copy(info, 1, pos(#13, info) - 1);
        delete(info, 1, pos(#13, info));
        Time := copy(info, 1, pos(#14, info) - 1);
        delete(info, 1, pos(#14, info));
        Size := strtoint(copy(info, 1, pos(#15, info) - 1));
        delete(info, 1, pos(#15, info));

   end;
end;



function   HandlCheck(hid:Integer):Integer;
var I,Rs : Integer;
begin
  I := 0; Rs := 0;
  while I <> maxclient do
  begin
    if (ClientInfo[I].Used = True) and
       (ClientInfo[I].Handle= hid) then
      begin
       Rs := ClientInfo[I].Point;
       Break;
      end;
    Inc(I);
    Rs := -1;
  end;
  Result := Rs;
end;

procedure ShitMove(Pnt:Integer);
var m : Integer;
begin
for m := 0 to Maxclient do
     if (ClientInfo[m].Used = True) and (ClientInfo[m].Point > Pnt) then  dec(ClientInfo[m].Point);
end;


// Kind - 1: CopyStart , 2: CopyData, 3: CopyEnd.
procedure TForm1.Telegram(Kind,m: integer);
var FileContent: array[0..TRANSSIZE] of Char;
//    Temp: String;
begin
  FileContent:= '';
  case Kind of
    1: begin  //Copy Start
         TransCount:= 0; //전송횟수
         FileSize:= ReturnFileSize(FileInfo.Name);
         if FileSize = -1 then
         begin
           //File Search Fail
         end;
         FileMok := FileSize div (TRANSSIZE-10);
         FileNam := FileSize mod (TRANSSIZE-10);
         try
           ClientInfo[m].FStream:= TFileStream.Create(FileInfo.Name, fmOpenRead); //전송파일 Stream 생성
           ClientInfo[m].Used := True;
         except
           FillChar(SendBuffer, TRANSSIZE, #0);
           StrCopy(SendBuffer, 'FILEERROR ');
           ServerSocket1.Socket.Connections[m].SendBuf(SendBuffer, TRANSSIZE); //Error 발생하고 다시파일 복사 시작.
         end;
         FillChar(SendBuffer, TRANSSIZE, #0);
         StrCopy(SendBuffer, PChar('COPYSTART ' +#11+FileInfo.Name + #12 + IntToStr(FileSize) + #13 +InttoStr(FileNam) + #14));
         StatusBar1.Panels[0].Text:= FileName + ' 복사중...';
         ServerSocket1.Socket.Connections[m].SendBuf(SendBuffer, TRANSSIZE); //파일 복사 시작.
       end;
    2: begin  //Data Transmission
         if ClientInfo[m].Used = True then
         begin
                 FillChar(SendBuffer, TRANSSIZE, #0);
                 StrCopy(SendBuffer, 'COPYDATA  ');
                 ClientInfo[m].FStream.ReadBuffer(FileContent, TRANSSIZE-10);
                 Move(FileContent, SendBuffer[10], TRANSSIZE-10);
                 ServerSocket1.Socket.Connections[m].SendBuf(SendBuffer, TRANSSIZE); //파일 복사 시작.
                 inc(TransCount);
         end;
       end;
    3: begin  //Copy End;
         if ClientInfo[m].Used = True then
         begin
                 FillChar(SendBuffer, TRANSSIZE, #0);
                 StrCopy(SendBuffer, 'COPYEND   ');
                 ClientInfo[m].FStream.ReadBuffer(FileContent, FileNam); //File Remainder
                 Move(FileContent, SendBuffer[10], TRANSSIZE-10);
                 ServerSocket1.Socket.Connections[m].SendBuf(SendBuffer, TRANSSIZE); //파일 복사 시작.
         end;
       end;
    4: begin  //FileNext Check;
                 FillChar(SendBuffer, TRANSSIZE, #0);
                 StrCopy(SendBuffer, 'FILENEXT  ');
                 ServerSocket1.Socket.Connections[m].SendBuf(SendBuffer, TRANSSIZE); //파일 복사 시작.
       end;


  end;

end;
//------------------------------------------------------------------------------

procedure TForm1.Button1Click(Sender: TObject);
begin

ServerSocket1.Open;
Statusbar1.Panels[0].Text := 'Starting.....';
end;

procedure TForm1.Button2Click(Sender: TObject);
var I,J : integer;
begin
if ServerSocket1.socket.ActiveConnections > 0 then
for I:=0 to MAXCLIENT do
  begin
       J := ClientInfo[I].Point;
       ClientInfo[I].Handle := 0;
       ClientInfo[I].Ip    := '';
       ClientInfo[I].FStream.Free;
       ClientInfo[I].Used  := False;
       ClientInfo[I].Point := 0;
       ServerSocket1.Socket.Connections[J].Disconnect(J);
  end;
ServerSocket1.Close;
Statusbar1.Panels[0].Text := 'Stopping.....';
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
  Socket: TCustomWinSocket);
var I : Integer;
begin
  // 접속
  I := 0;
  while I <> maxclient do
  begin
    if  (ClientInfo[I].Used  = False)     then Break;
    Inc(I);
  end;

  if I = maxclient then
   begin
            // Socket.SendText('접속실패:'+InttoStr(Socket.handle));
             Socket.Close;
             Exit;
   end
  else
   begin
    ClientInfo[I].Handle := Socket.Handle;
    ClientInfo[I].Ip     := Socket.RemoteAddress;
    ClientInfo[I].Used   := True;
    ClientInfo[I].Point  := ServerSocket1.Socket.ActiveConnections - 1;
   end;
//   Memo1.Lines.Add('신규접속자:'+IntToStr(Socket.handle)+'['+Socket.RemoteAddress+']'+IntToStr(ClientInfo[I].Point));
end;

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
var I,J : Integer;
begin

  // 단절
  I := 0;
  while I <> maxclient do
  begin
    if (ClientInfo[I].Used = True) and (ClientInfo[I].Handle = Socket.Handle) then
      begin
       J:=ClientInfo[I].Point;
       ClientInfo[I].Handle := -1;
       ClientInfo[I].Ip    := '';
       ClientInfo[I].Used  := False;
       ClientInfo[I].Point := -1;
       ClientInfo[I].FStream := nil;
       ShitMove(J);
       ServerSocket1.Socket.Connections[J].Disconnect(J); //연결끊음
       Break;
      end;
    Inc(I);
  end;

//  Memo1.Lines.Add(IntToStr(Socket.handle)+'['+Socket.RemoteAddress+'] 접속단절');
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var I : Integer;
    Buff,Command: String;
begin
  Socket.ReceiveBuf(RecvBuffer, TRANSSIZE);
  Command:= Trim(Copy(RecvBuffer, 1, 10));
  I    := HandlCheck(Socket.Handle);
  if I <> -1 then
   begin
          if Command = 'FILECHECK' then
           begin
               GetFileInfo(RecvBuffer);
               case ReturnFileCheck(FileInfo) of
                  1: Telegram(4,I);
                  2: Telegram(1,I);
               end;
           end;
          if Command='COPYSTART' then
          begin
            if FileMok=0 then Telegram(3,I)  else Telegram(2,I);
          end;

          if Command='COPYDATA' then
          begin
            if TransCount=FileMok then Telegram(3,I)  else Telegram(2,I);
          end;

          if Command='COPYEND' then
          begin
            StatusBar1.Panels[0].Text:= ' 복사완료.';
            ClientInfo[I].FStream.Free;
            Telegram(4,I);
          end;
          if Command='FILEEND' then  ServerSocket1.Socket.Connections[I].Disconnect(I);



   end
  else
     Socket.Close;
end;

procedure TForm1.ServerSocket1ClientError(Sender: TObject;
  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
  var ErrorCode: Integer);
var I,J : Integer;
begin

  // 단절

  I := 0;
  while I <> maxclient do
  begin
    if (ClientInfo[I].Used = True) and
       (ClientInfo[I].Handle = Socket.Handle) then
      begin
       J:=ClientInfo[I].Point;
       ClientInfo[I].Handle := 0;
       ClientInfo[I].Ip    := '';
       ClientInfo[I].Used  := False;
       ClientInfo[I].Point := 0;
       ShitMove(J);
       ServerSocket1.Socket.Connections[J].Close;  //연결끊음
       Break;
      end;
    Inc(I);
  end;

//  Memo1.Lines.Add(IntToStr(Socket.handle)+'['+Socket.RemoteAddress+'] 접속단절');
end;

end.


[클라이언트]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, ScktComp, StdCtrls, ComCtrls, Inifiles, ImgList;

type
  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
    StatusBar1: TStatusBar;
    ClientSocket1: TClientSocket;
    Timer1: TTimer;
    Animate1: TAnimate;
    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
    procedure FormActivate(Sender: TObject);
    procedure ClientSocket1Disconnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure AppError(Sender:Tobject; EE:Exception);
    procedure Telegram(Kind: integer);

  end;

const TRANSSIZE=4096;

//전송 파일 Information
Type TFileInfo = record
      Name  : String;
      Size  : Integer;
      Date  : String;
      Time  : String;
      Spare : Integer;
end;

var
  Form1: TForm1;
  FileName,CurrDir,ExeFile,BuffTmp,Path,FileType: String;
  FileSize, FileNam, MaxFile,FileIndex : integer;
  SendBuffer, RecvBuffer: array[0..TRANSSIZE] of char;
  FStream: TFileStream;
  SearchRec : TSearchRec;
  FileInfo : TFileInfo;
  FileList : array[0..100] of TFileInfo;
implementation

{$R *.DFM}

Procedure GetFileInfo(rcvStr: String);
var info : string;
begin
  //COPYSTART[#11]FILENAME[#12]FILESIZE[#13]FILESPARE[#14]
  info := Copy(rcvStr, 1, pos(#14, rcvStr)); // 받은 버퍼값을 저장
  delete(info, 1, pos(#11, info)); // 전송시작부분 잘라냄 COPYSTART부분
  with FileInfo do
  begin
        Name := copy(info, 1, pos(#12, info) - 1);
        delete(info, 1, pos(#12, info));
        Size := strtoint(copy(info, 1, pos(#13, info) - 1));
        delete(info, 1, pos(#13, info));
        Spare := strtoint(copy(info, 1, pos(#14, info) - 1));
  end;
end;

procedure TForm1.AppError(Sender:TObject; EE:Exception);
begin
showMessage(EE.Message);
end;


// Kind - 1: CopyStart , 2: CopyData, 3: CopyEnd.
procedure TForm1.Telegram(Kind: integer);
var Temp: array[0..TRANSSIZE] of Char;
    TmpBuff : String;
begin
  FillChar(Temp, TRANSSIZE, #0);
  case Kind of
    1: begin
         TmpBuff := copy(RecvBuffer, 1, length(RecvBuffer));
         GetFileInfo(TmpBuff);
         StatusBar1.Panels[1].Text := 'Download : '+ FileInfo.Name;
         ProgressBar1.Max    := FileInfo.Size;
         ProgressBar1.Step   := TRANSSIZE - 10;
         try
           FStream:= TFileStream.Create(FileInfo.Name, fmCreate or fmOpenWrite); //Stream 생성
         except
           Application.MessageBox('Client화일을 오픈할수 없습니다', '오류', MB_OK);
           Exit;
         end;
         FillChar(SendBuffer, TRANSSIZE, #0); // SendBuffer #0로 초기화
         StrCopy(SendBuffer, PChar('COPYSTART '));  // 파일전송 요구
         ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE); //파일복사를 시작.
         Animate1.Active := True;
         ProgressBar1.Position:= 0; // 진행표시
         ProgressBar1.Position:= Progressbar1.Position + ProgressBar1.Step;
         Application.ProcessMessages;
       end;
    2: begin  // File Data File 전송
         Move(RecvBuffer[10], Temp, SizeOf(Temp));
         FStream.WriteBuffer(Temp, TRANSSIZE-10);
         FillChar(SendBuffer, TRANSSIZE, #0);
         StrCopy(SendBuffer, PChar('COPYDATA  '));
         ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE); //파일복사중.

         ProgressBar1.Position:= Progressbar1.Position + ProgressBar1.Step;
         Application.ProcessMessages;
       end;
    3: begin  // file 종료
         Move(RecvBuffer[10], Temp, SizeOf(Temp));
         FStream.WriteBuffer(Temp, FileInfo.Spare);
         FillChar(SendBuffer, TRANSSIZE, #0);
         StrCopy(SendBuffer, PChar('COPYEND   '));
         ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE); //파일복사 종료.
         Animate1.Active := False;
         ProgressBar1.Position:= FileSize;
         Application.ProcessMessages;
       end;
  end;
end;
//------------------------------------------------------------------------------

procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
var Command,Buff : String;
begin
  Socket.ReceiveBuf(RecvBuffer, TRANSSIZE);
  Command:= Trim(Copy(RecvBuffer, 1, 10));  //서버로부턱 패킷에서 명령어 출추

  if Command='COPYSTART' then   Telegram(1);
  if Command='COPYDATA'  then   Telegram(2);
  if Command='COPYEND'   then
   begin
    Telegram(3);     FStream.Free;
   end;
  if Command='FILEERROR'   then
   begin
       BuffTmp := #11+FileList[FileIndex].Name+#12+IntToStr(FileList[FileIndex].Size)+#13
                  +FileList[FileIndex].Date+#14+FileList[FileIndex].Time+#15;
       FillChar(SendBuffer, TRANSSIZE, #0);
       StrCopy(SendBuffer, PChar('FILECHECK '+BuffTmp));
       ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE);
   end;

  if Command = 'FILENEXT'  Then
  begin
   Inc(FileIndex);
   if FileIndex < MaxFile then
    begin
       BuffTmp := #11+FileList[FileIndex].Name+#12+IntToStr(FileList[FileIndex].Size)+#13
                  +FileList[FileIndex].Date+#14+FileList[FileIndex].Time+#15;
       FillChar(SendBuffer, TRANSSIZE, #0);
       StrCopy(SendBuffer, PChar('FILECHECK '+BuffTmp));
       ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE);
     end
    else
     begin
       FillChar(SendBuffer, TRANSSIZE, #0);
       StrCopy(SendBuffer, PChar('FILEEND   '));
       ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE);
       ClientSocket1.Close;
     end;
  end;
end;

procedure TForm1.FormActivate(Sender: TObject);
var
  AppIni: TIniFile;
   FAll : String;
      I : Integer;
begin
  Path := ExtractFilePath(Application.EXEName)+'DownFile.INI';
  if FileExists(Path) then
   begin
     AppIni := TIniFile.Create(Path);
     ClientSocket1.Port := AppIni.ReadInteger('Server', 'Port', -1);
     if ClientSocket1.Port = -1 then
      begin
       Application.MessageBox(PChar(Path+' 파일이 없습니다'),'확 인', MB_OK);
       halt(1);
      end;

     ClientSocket1.Host := AppIni.ReadString('Server', 'HostIp', 'Error');
     if ClientSocket1.Host = 'Error' then
      begin
       Application.MessageBox(PChar(Path+' 파일이 없습니다'),'확 인', MB_OK);
       halt(1);
      end;

     FileType           := AppIni.ReadString('Server', 'FileType', 'Error');
     if FileType = 'Error' then
      begin
       Application.MessageBox(PChar(Path+' 파일이 없습니다'),'확 인', MB_OK);
       halt(1);
      end;

     AppIni.Free;

     try
       ClientSocket1.Open;
       StatusBar1.Panels[0].Text := 'Connected';
     except
       Application.MessageBox('Server 연결 실패.', '확인', MB_OK);
       halt(1);
     end;
   end
  else
   begin
       Application.MessageBox(PChar(Path+' 파일 없습니다.'), '확인', MB_OK);
       Application.Terminate;
   end;
   Sleep(500);
  CurrDir := ExtractFilePath(Application.ExeName); // 디렉토리 경로 출추
  Exefile := Uppercase(ExtractFileName(Application.ExeName)); // 실행 파일 출추
  FAll:= CurrDir + FileType;                      // 클라언트 실행 파일조사
  if FindFirst(FAll, faAnyFile, SearchRec) = 0 then
  begin
   //찾은게 파일일 경우..
    I := 0;
    Repeat
     if (SearchRec.Name <> '.') and
        (SearchRec.Name <> '..') and
        (Uppercase(SearchRec.Name) <> ExeFile)
        then
      begin
       FileList[I].Name := SearchRec.Name;
       FileList[I].Size := SearchRec.Size;
       FileList[I].Date := FormatDateTime('yyyymmdd', FileDateToDateTime(SearchRec.Time));
       FileList[I].Time := FormatDateTime('hhmm', FileDateToDateTime(SearchRec.Time));
       Inc(I);
     end;
    Until FindNext(SearchRec) <> 0;
     MaxFile := I-1;  FileIndex:= 0;
     Sleep(500);
     Timer1.Enabled := True;
  end;
end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
ClientSocket1.Close;
StatusBar1.Panels[0].Text := 'Disconnected';
Close;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
     FillChar(SendBuffer, TRANSSIZE, #0);
     BuffTmp := #11+FileList[0].Name+#12+FileList[0].Date+#13+FileList[0].Time+#14+IntToStr(FileList[0].Size)+#15;
     StrCopy(SendBuffer, PChar('FILECHECK '+BuffTmp));
     ClientSocket1.Socket.SendBuf(SendBuffer, TRANSSIZE);
     Timer1.Enabled := False;
end;

end.
0  COMMENTS