안녕하새요... 초보입니다..
소켓전송 자료를 참고를 작성하였습니다..
서버에서 클라이언트로 파일을 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.