線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:5245
推到 Plurk!
推到 Facebook!

以 Lazarus 實作收銀機前後台通訊程式 (使用 INDY 10)

 
digitraveler
初階會員


發表:89
回覆:91
積分:46
註冊:2005-06-01

發送簡訊給我
#1 引用回覆 回覆 發表時間:2010-05-26 17:11:30 IP:118.171.xxx.xxx 訂閱
看到標題, 或許有人會覺得奇怪, POS 跟後台 DB 通訊或要資料, 用資料庫聯結的方式或是 HTTP/XML 查詢不就得了 ??  原因如下
1.這是有數百台 POS 規模的客戶, 不會讓你每台 POS 都灌個 ORACLE CLIENT 之類的東西直接去連資料庫
2.專櫃收銀員素質不一, 預防利用 POS 偷上網, 或是病毒感染癱瘓整個賣場網路, 幾乎所有通訊協定都鎖起來, 除了 POS 跟 POS SERVER 間自定的 Middle ware 的通訊協定
3.包含檔案傳輸, 都絕不透過網芳或是 FTP , 完全使用自訂通訊方式 , 本案例就是實做自訂的 POS 跟 POS SERVER 間的 Middle ware 通訊


我個人把收銀機前後台網路通訊歸類為四種主要命令, 建立好後這四種命令機制後, POS 前後台之間的通訊需求都可搞定, 其它業種之程式需求當然也可應用到

1.SEND_STR 命令 : POS 向主機發出 STRING 資料
例如 : POS 送出 "I AM ALIVE" 告知主機 POS 連線狀況

2.GET_STR 命令 : POS 向主機要求取回 STRING 資料

例如 : POS 向後台要回主機時間字串以便同步 POS 時間,
或是簡單查詢字串 (送出會員卡號, 從主機取回會員姓名等)

3.SEND_FILE 命令 : POS 向主機送出 FILE
例如 : POS 把交易資料檔送至主機

4.GET_FILE 命令 : POS 向主機要求取回 FILE (可能有多筆檔案)
例如 : POS 從主機取得 "程式更新", "主檔配信",
甚至較複雜的查詢(如退貨交易資料內容)都可透過 GET_FILE 命令機制

這些機制可以綁在 POS 程式中, 變成 POS 程式的一部份; 也可獨立出成一 Middleware 程式, 大檔案的分割傳送, Server 收到 Client 取檔指令後如何透過原連線回送檔案到 Client(不用再開一新連線), 分享給大家



[code delphi]
CLIENT 端程式

//----------------------------------------------------------------------------
//函式使用例 : SND_STR('HELLO',0);
//----------------------------------------------------------------------------
procedure TForm1.SND_STR(command_text: String ; command_tag:integer);
begin
if (connection_busy) then exit;
connection_busy:=true;
if (IdTCPClient1.Connected) then IdTCPClient1.Disconnect;
IdTCPClient1.Host := Edit1.Text;
IdTCPClient1.ReadTimeout:=1000;
MemoLog('●SND_STR(' command_text ',' IntToStr(command_tag) ') 開始');

try
IdTCPClient1.Connect;
except
MemoLog('連結失敗');
connection_busy:=false;
MemoLog('○SND_STR() 結束');
exit;
end;

try
IdTCPClient1.IOHandler.Write(LongInt(1),true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag
finally
IdTCPClient1.Disconnect;
connection_busy:=false;
MemoLog('○SND_STR() 結束');
end;
end;

//----------------------------------------------------------------------------
//函式使用例 : ShowMessage(GET_STR('TIME',0)); //秀出主機時間字串
//----------------------------------------------------------------------------
function TForm1.GET_STR(command_text: String ; command_tag:integer): String;
var r:String;
begin
if (connection_busy) then exit;
connection_busy:=true;
if (IdTCPClient1.Connected) then IdTCPClient1.Disconnect;
IdTCPClient1.Host := Edit1.Text;
IdTCPClient1.ReadTimeout:=1000;

MemoLog('●GET_STR(' command_text ',' IntToStr(command_tag) ') 開始');
r:='';
try
IdTCPClient1.Connect;
except
MemoLog('連結失敗');
connection_busy:=false;
MemoLog('○GET_STR() 結束');
result:=r;
exit;
end;

try
IdTCPClient1.IOHandler.Write(LongInt(2),true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag
r:=IdTCPClient1.IOHandler.ReadLn;
MemoLog('接收到回傳字串 : "' r '"');
finally
IdTCPClient1.Disconnect;
connection_busy:=false;
MemoLog('○GET_STR() 結束');
end;
result:=r;
end;

//----------------------------------------------------------------------------
//函式使用例 : if (OpenDialog1.Execute) then SND_FILE(OpenDialog1.FileName,0);
//----------------------------------------------------------------------------
procedure TForm1.SND_FILE(command_text: String ; command_tag:integer);
var buff:array[0..1024] of byte;
buff2:array of Byte; //TBytes
buff3:TIdBytes; //uses idGlobal
fstream:TFileStream;
filesize:integer;
filename:String;
readcount:integer;
begin
if (connection_busy) then exit;
connection_busy:=true;
if (IdTCPClient1.Connected) then IdTCPClient1.Disconnect;
IdTCPClient1.Host := Edit1.Text;
IdTCPClient1.ReadTimeout:=1000;

MemoLog('●SND_FILE(' command_text ',' IntToStr(command_tag) ') 開始');

try
IdTCPClient1.Connect;
except
MemoLog('連結失敗');
connection_busy:=false;
MemoLog('○SND_FILE() 結束');
exit;
end;

try
fstream:=TFileStream.Create(command_text, fmShareDenyWrite); //fmOpenRead(會獨佔), fmShareDenyWrite, fmShareDenyNone
filename:=ExtractFileName(command_text);
filesize:=fstream.Size;
IdTCPClient1.IOHandler.Write(LongInt(3),true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(filename); //Command_Text
IdTCPClient1.IOHandler.Write(filesize,true); //Command_Tag
MemoLog('發送檔案:' command_text);
fstream.Position:=0;
While (fstream.Position < filesize) do
begin

if ((filesize - fstream.Position) >= 1024) then //PASCAL 不能用 SIZEOF(buff) ??
readcount := 1024
else
readcount := filesize - fstream.Position;
fstream.ReadBuffer(buff, readcount);
SetLength(buff3,readcount);
Move(buff[0],buff3[0],readcount);
IdTCPClient1.Socket.Write(buff3, readcount);
end;
MemoLog('發送檔案完成 (' IntToStr(filesize) ' bytes)');

finally
fstream.Free;
IdTCPClient1.Disconnect;
connection_busy:=false;
MemoLog('○SND_FILE() 結束');
end;

end;

//----------------------------------------------------------------------------
//函式使用例 : GET_FILE('PG_DOWNLOAD',0); //取得程式更新檔案 ('PG_DOWNLOAD'為自訂的程式更新識別字)
//----------------------------------------------------------------------------
procedure TForm1.GET_FILE(command_text: String ; command_tag:integer);
var buff:array[0..1024] of byte;
buff3:TIdBytes; //uses idGlobal
fstream:TFileStream;
filesize:integer;
filename:String;
readcount:integer;
get_file_path:String;
TmpList: TStringList;
TmpList2: TStringList;
i: integer;
begin
if (connection_busy) then exit;
connection_busy:=true;
if (IdTCPClient1.Connected) then IdTCPClient1.Disconnect;
IdTCPClient1.Host := Edit1.Text;
IdTCPClient1.ReadTimeout:=5000;

MemoLog('●GET_FILE(' command_text ',' IntToStr(command_tag) ') 開始');

try
IdTCPClient1.Connect;
except
MemoLog('連結失敗');
connection_busy:=false;
MemoLog('○GET_FILE 結束');
exit;
end;

try
IdTCPClient1.IOHandler.Write(LongInt(4),true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag
get_file_path:='';
if (Trim(command_text)='PG_DOWNLOAD') then get_file_path:='C:\PG';
TmpList:=TStringList.Create;
TmpList2:=TStringList.Create;
//讀回檔案可能有多個
IdTCPClient1.Socket.ReadStrings(TmpList); //讀回檔名列
IdTCPClient1.Socket.ReadStrings(TmpList2); //讀回檔案 size 列

for i:=0 to TmpList.Count-1 do
begin
filename:=TmpList.Strings[i];
filesize:=StrToInt(TmpList2.Strings[i]);

//假如 Client 端已存在該檔案要先刪除
filename:=get_file_path '\' filename;
ForceDirectories(get_file_path);
if (FileExists(filename)) then DeleteFile(filename);

try
fstream:=TFileStream.Create(filename, fmCreate); //接收端檔名路徑
MemoLog('接收檔案:' filename);
while (filesize > fstream.Size) do
begin
if ((filesize - fstream.Size) > 1024) then
readcount := 1024
else
readcount := filesize - fstream.Size;
//MemoLog("接收 bytes:" IntToStr(readcount));
IdTCPClient1.Socket.ReadBytes(buff3, readcount, false);
Move(buff3[0],buff[0],readcount);
fstream.WriteBuffer(buff, readcount);
Application.ProcessMessages();
end;
finally
MemoLog('接收檔案完成 (' IntToStr(filesize) ' bytes)');
fstream.Free;
end;
end;

finally
TmpList.Free;
TmpList2.Free;
IdTCPClient1.Disconnect;
connection_busy:=false;
MemoLog('○GET_FILE 結束');
end;
end;

--------------------------------------------------------------------------------------------------------------------------------------
SERVER 端程式

只要放一個 IdTCPServer 控件, 在其 OnExecute() 事件中處理所有命令需求

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var command_id:integer;
command_text: String ;
command_tag:integer;
client_ip:String;
buff:array[0..1023] of byte;
buff2:array of Byte;
buff3:TIdBytes; //uses idGlobal

fstream:TFileStream;
filesize:integer;
filename:String;
readcount:integer;
i:integer;
SID:Integer;
TmpList:TStringList;
TmpList2:TStringList;
TmpList3:TStringList;
MySync: TMySync;
begin
MySync:=TMySync.Create();
Randomize();
SID:=Random(100) Random(100); //INDY 10 沒有 THREAD ID, 需要的話可用這模擬

IncrConnectioncount;

client_ip:=AContext.Connection.Socket.Binding.PeerIP; //同 AContext.Binding.PeerIP;


try

command_id:=AContext.Connection.IOHandler.ReadLongInt(true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
//MemoLog('command_id=' IntToStr(command_id));
case command_id of
1 : begin //收到 SND_STR 命令
//command_text:=AContext.Connection.IOHandler.ReadLn('\n',1000,-1);
command_text:=AContext.Connection.IOHandler.ReadLn;
command_tag :=AContext.Connection.IOHandler.ReadLongInt(true);
if (command_text='POS_CONNECT') then
begin
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_STR): ' command_text '( POS_ID = ' IntToStr(command_tag) ')';
MySync.Synchronize;
end
else
begin
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_STR): ' command_text '(' IntToStr(command_tag) ')';
MySync.Synchronize;
end;
end;
2 : begin //收到 GET_STR 命令
//command_text:=AContext.Connection.IOHandler.ReadLn('\n',1000,-1);
command_text:=AContext.Connection.IOHandler.ReadLn;
command_tag :=AContext.Connection.IOHandler.ReadLongInt(true);
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (GET_STR): ' command_text '(' IntToStr(command_tag) ')';
MySync.Synchronize;
if (command_text='TIME') then
begin
AContext.Connection.IOHandler.WriteLn('SERVER TIME IS ' FormatDateTime('hh:nn:ss', Now));
end
end;
3 : begin //收到 SND_FILE 命令
command_text:=AContext.Connection.IOHandler.ReadLn;
command_tag :=AContext.Connection.IOHandler.ReadLongInt(true);
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_FILE): ' command_text '(' IntToStr(command_tag) ')';
MySync.Synchronize;
filename:='C:\' command_text;
filesize:=command_tag;
if (FileExists(filename)) then DeleteFile(filename);
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_FILE): SERVER 接收檔案 = ' filename;
MySync.Synchronize;
fstream:=TFileStream.Create(filename, fmCreate);
while (filesize>fstream.Size) do
begin
if ((filesize-fstream.Size) >= 1024) then
readcount := 1024
else
readcount := filesize - fstream.Size;
//log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_FILE): read_bytes = ' IntToStr(readcount);
//MySync.Synchronize;
//SetLength(buff3,readcount); //這裡可以不用加
AContext.Connection.Socket.ReadBytes(buff3, readcount, false);
Move(buff3[0],buff[0],readcount);
fstream.WriteBuffer(buff, readcount);

end;
fstream.Free;
log_text:=client_ip '/' {IntToStr(AContext.Data) }' (SND_FILE): SERVER 接收檔案完成 (' IntToStr(filesize) ' bytes)';
MySync.Synchronize;
end;

4 : begin //收到 GET_FILE 命令
command_text:=AContext.Connection.IOHandler.ReadLn;
command_tag :=AContext.Connection.IOHandler.ReadLongInt(true);

log_text:=client_ip '/' {IntToStr(AContext.Data) }' (GET_FILE): ' command_text '(' IntToStr(command_tag) ')';
MySync.Synchronize;

if (command_text='PG_DOWNLOAD') then
begin
TmpList:=TStringList.Create;
TmpList2:=TStringList.Create;
TmpList3:=TStringList.Create;
_GetFileList(TmpList,'C:\windows\system32\*.*');
//檔名串列
TmpList2.Clear;
for i:=0 to TmpList.Count-1 do
begin
TmpList2.Add(ExtractFileName(TmpList.Strings[i]));
end;
//檔案 Size 串列
TmpList3.Clear;
for i:=0 to TmpList.Count-1 do
begin
TmpList3.Add(IntToStr(_FileSize(TmpList.Strings[i])));
end;
AContext.Connection.IOHandler.Write(TmpList2, true);
AContext.Connection.IOHandler.Write(TmpList3, true);

//傳送檔案
for i:=0 to TmpList.Count-1 do
begin
filename:=TmpList.Strings[i];
fstream:=TFileStream.Create(filename, fmShareDenyWrite); //fmOpenRead(會獨佔), fmShareDenyWrite, fmShareDenyNone
filesize:=fstream.Size;

log_text:=client_ip '/' {IntToStr(AContext.Data) }' (GET_FILE): SERVER 回傳檔案 = ' filename;
MySync.Synchronize;

while (fstream.Position < filesize) do
begin
if ((filesize - fstream.Position) >= 1024) then
readcount := 1024
else
readcount := filesize - fstream.Position;
//debug 用, 打開的話傳大檔會慢很多
//log_text:=client_ip '/' {IntToStr(AContext.Data) }' (GET_FILE): send_bytes = ' IntToStr(readcount);
//MySync.Synchronize;

fstream.ReadBuffer(buff, readcount);
SetLength(buff3,readcount);
Move(buff[0],buff3[0],readcount);
AContext.Connection.IOHandler.Write(buff3, readcount);

end;
fstream.Free;

log_text:=client_ip '/' {IntToStr(AContext.Data) }' (GET_FILE): SERVER 回傳檔案完成 (' IntToStr(filesize) ' bytes)';
MySync.Synchronize;
end;

TmpList.Free;
TmpList2.Free;
end;

end;

else
//MemoLog(client_ip);
end;
finally
AContext.Connection.Disconnect();
DecrConnectioncount;
end;

MySync.Free;
end;

[/code]


■ 相關參考及源碼下載 : http://tw.myblog.yahoo.com/bruce0829/article?mid=37





編輯記錄
digitraveler 重新編輯於 2010-05-26 17:13:21, 註解 無‧
digitraveler 重新編輯於 2010-05-26 17:14:24, 註解 無‧
digitraveler 重新編輯於 2010-05-26 17:18:26, 註解 無‧
digitraveler 重新編輯於 2010-05-31 20:30:54, 註解 無‧
digitraveler 重新編輯於 2010-06-12 11:28:26, 註解 無‧
digitraveler 重新編輯於 2010-06-12 11:29:03, 註解 無‧
digitraveler 重新編輯於 2010-06-12 11:30:21, 註解 無‧
digitraveler 重新編輯於 2010-06-12 11:39:07, 註解 無‧
digitraveler
初階會員


發表:89
回覆:91
積分:46
註冊:2005-06-01

發送簡訊給我
#2 引用回覆 回覆 發表時間:2010-05-30 15:39:04 IP:111.254.xxx.xxx 訂閱
下雨天閒閒在家沒事幹, 休閒研究 ....

VS2003 C# INDY.NET 版


[code c#]
動態產生 TCPClient 控件
TCPClient IdTCPClient1 = new TCPClient();

//----------------------------------------------------------------------------
private void Form1_Load(object sender, System.EventArgs e)
{
Random randObj = new Random();
pos_id=randObj.Next(100,999); //取 100-999 隨機值
textBox1.Text=My.IntToStr(pos_id); //pos id


IdTCPClient1.Port = 6501;

Can_Connect();

}

//----------------------------------------------------------------------------
//Example : SND_STR("HELLO",0);
//----------------------------------------------------------------------------
private void SND_STR(String command_text, int command_tag)
{
if (connection_busy) return;
connection_busy=true;

MemoLog("●SND_STR(" command_text "," My.IntToStr(command_tag) ") 開始");

if (IdTCPClient1.Connected()) IdTCPClient1.Disconnect();
IdTCPClient1.Host = textBox2.Text;

try
{
try
{
IdTCPClient1.Connect();
IdTCPClient1.IOHandler.Write(1,true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag
}
catch
{
MemoLog("連結失敗");
}
}
finally
{
IdTCPClient1.Disconnect();
connection_busy=false;
MemoLog("○SND_STR() 結束");
}
}

//----------------------------------------------------------------------------
//Example : My.ShowMessage("SERVER TIME IS " GET_STR("TIME",0));
//----------------------------------------------------------------------------
private String GET_STR(String command_text, int command_tag)
{
string r="";

if (connection_busy) return r;
connection_busy=true;

MemoLog("●GET_STR(" command_text "," My.IntToStr(command_tag) ") 開始");

if (IdTCPClient1.Connected()) IdTCPClient1.Disconnect();
IdTCPClient1.Host = textBox2.Text;

try
{
try
{
IdTCPClient1.Connect();
IdTCPClient1.IOHandler.Write(2,true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag

r=IdTCPClient1.IOHandler.ReadLn();
}
catch
{
MemoLog("連結失敗");
}
}
finally
{
IdTCPClient1.Disconnect();
connection_busy=false;
MemoLog("○GET_STR() 結束");
}



return r;
}

//----------------------------------------------------------------------------
//Example : if (openFileDialog1.ShowDialog() == DialogResult.OK) SND_FILE(openFileDialog1.FileName,0);
//----------------------------------------------------------------------------
private void SND_FILE(String command_text, int command_tag)
{
if (connection_busy) return;
connection_busy=true;

MemoLog("●SND_FILE(" command_text "," My.IntToStr(command_tag) ") 開始");

if (IdTCPClient1.Connected()) IdTCPClient1.Disconnect();
IdTCPClient1.Host = textBox2.Text;

FileStream fstream = null; //using System.IO
String filename;
int filesize;
int readcount;
int fileoffset;
byte[] buff=new byte[1024];

try
{
try
{
fstream = new FileStream(command_text, FileMode.Open, FileAccess.Read);
filename=My.ExtractFileName(command_text);
filesize=(int)fstream.Length;
fileoffset=0;

IdTCPClient1.Connect();
IdTCPClient1.IOHandler.Write(3,true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(filename); //Command_Text
IdTCPClient1.IOHandler.Write(filesize,true); //Command_Tag

MemoLog("發送檔案:" command_text);

fstream.Position=0;
while (fstream.Position < filesize)
{
if ((filesize - fstream.Position) >= buff.Length)
readcount = buff.Length;
else
readcount = filesize - (int)fstream.Position;

fstream.Read(buff, 0, readcount);
IdTCPClient1.Socket.Write(buff);
}

MemoLog("發送檔案完成 (" My.IntToStr(filesize) " bytes)");

fstream.Close();

}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
MemoLog("連結失敗");
}
}
finally
{
IdTCPClient1.Disconnect();
connection_busy=false;
MemoLog("○SND_FILE() 結束");
}
}

//----------------------------------------------------------------------------
//Example : GET_FILE("PG_DOWNLOAD",0);
//----------------------------------------------------------------------------
private void GET_FILE(String command_text, int command_tag)
{
if (connection_busy) return;
connection_busy=true;

MemoLog("●GET_FILE(" command_text "," My.IntToStr(command_tag) ") 開始");

if (IdTCPClient1.Connected()) IdTCPClient1.Disconnect();
IdTCPClient1.Host = textBox2.Text;

FileStream fstream = null; //using System.IO
string filename;
int filesize;
int readcount;
byte[] buff=new byte[1024];
string get_file_path;

try
{
try
{
IdTCPClient1.Connect();
IdTCPClient1.IOHandler.Write(4,true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
IdTCPClient1.IOHandler.WriteLn(command_text); //Command_Text
IdTCPClient1.IOHandler.Write(command_tag,true); //Command_Tag

get_file_path="";
if (My.Trim(command_text)=="PG_DOWNLOAD") get_file_path="C:\\PG";
//讀回檔案可能有多個
String fn_list = IdTCPClient1.Socket.ReadLn(); //讀回檔名列
String fs_list = IdTCPClient1.Socket.ReadLn(); //讀回 File Size 列


if (My.Trim(fn_list)!="")
{
for (int i=1; i<=My.StrSegCount(fn_list,','); i )
{
filename=My.StrSeg(fn_list,',',i);
filesize=My.StrToInt(My.StrSeg(fs_list,',',i));
//假如 Client 端已存在該檔案要先刪除
filename = get_file_path "\\" filename;
My.ForceDirectories(get_file_path);
if (My.FileExists(filename)) My.DeleteFile(filename);
try
{
MemoLog("接收檔案:" filename);
fstream = new FileStream(filename, FileMode.Create, FileAccess.Write); //接收端檔名路徑

while (filesize > fstream.Length)
{
if ((filesize - fstream.Length) > buff.Length)
readcount = buff.Length;
else
readcount = filesize - (int)fstream.Length;
//MemoLog("接收 bytes:" IntToStr(readcount));
IdTCPClient1.Socket.ReadBytes(ref buff, readcount, false);
fstream.Write(buff, 0, readcount);
}
}
finally
{
fstream.Close();
//fstream.Dispose();
MemoLog("接收檔案完成 (" My.IntToStr(filesize) " bytes)");
}

}
}

}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
MemoLog("連結失敗");
}
}
finally
{
IdTCPClient1.Disconnect();
connection_busy=false;
MemoLog("○GET_FILE() 結束");
}


}



動態產生 TCPServer 控件
private TCPServer IdTCPServer1 = new TCPServer();
並在其 OnExecute() 事件中處理可以所有命令需求
private void Form1_Load(object sender, System.EventArgs e)
{
IdTCPServer1.OnConnect = new TIdServerThreadEvent(TCPServerConnect);
IdTCPServer1.OnExecute = new TIdServerThreadEvent(TCPServerExecute);
IdTCPServer1.DefaultPort = 6501;
IdTCPServer1.Active = true;

}

private void TCPServerExecute(Context AContext)
{
int command_id;
int command_tag;
string command_text;
string client_ip=AContext.Connection.Socket.Binding.PeerIP; //同 AContext.Binding.PeerIP;

FileStream fstream = null; //using System.IO
String filename;
int filesize;
int readcount;
byte[] buff=new byte[1024];
string fn_list, fs_list;


try
{
command_id=AContext.Connection.IOHandler.ReadInteger(true); //Command_Id => 1:SND_STR 2:GET_STR 3:SND_FILE 4:GET_FILE
try
{
switch(command_id)
{
case 1 : //收到 SND_STR
command_text=AContext.Connection.IOHandler.ReadLn();
command_tag=AContext.Connection.IOHandler.ReadInteger(true);
MemoLog(client_ip "(SND_STR): " command_text "(" My.IntToStr(command_tag) ")");
if (command_text=="POS_CONNECT")
{
;;
}
break;


case 2 : //收到 GET_STR
command_text=AContext.Connection.IOHandler.ReadLn();
command_tag=AContext.Connection.IOHandler.ReadInteger(true);

MemoLog(client_ip "(GET_STR): " command_text "(" My.IntToStr(command_tag) ")");

if (command_text=="TIME")
{
AContext.Connection.IOHandler.WriteLn(My.FormatDateTime("yyyy/mm/dd hh:nn:ss", My.Now()));
}
break;


case 3 : //收到 SND_FILE
command_text=AContext.Connection.IOHandler.ReadLn();
command_tag=AContext.Connection.IOHandler.ReadInteger(true);

MemoLog(client_ip "(SND_FILE): " command_text "(" My.IntToStr(command_tag) ")");

filename="C:\\" command_text;
filesize=command_tag;
if (My.FileExists(filename)) My.DeleteFile(filename);


MemoLog(client_ip " (SND_FILE): SERVER 接收檔案 = " filename);
fstream = new FileStream(filename, FileMode.Create, FileAccess.Write);
while (filesize>fstream.Length)
{
if ((filesize-fstream.Length) > buff.Length)
readcount = buff.Length;
else
readcount = filesize - (int)fstream.Length;

//MemoLog(client_ip " (SND_FILE): read_bytes = " IntToStr(readcount));
AContext.Connection.Socket.ReadBytes(ref buff, readcount, false);
fstream.Write(buff, 0, readcount);
}
fstream.Close();
MemoLog(client_ip " (SND_FILE): SERVER 接收檔案完成 (" filesize.ToString() " bytes)");

break;


case 4 : //收到 GET_FILE

command_text=AContext.Connection.IOHandler.ReadLn();
command_tag=AContext.Connection.IOHandler.ReadInteger(true);

MemoLog(client_ip "(GET_FILE): " command_text "(" My.IntToStr(command_tag) ")");


TStringList TmpList = new TStringList();
if (command_text=="PG_DOWNLOAD")
{
My.GetFileList(ref TmpList, "C:\\ABCPSV\\UPDATE\\*.*");


fn_list="";
fs_list="";
for (int i=0; i<=TmpList.Count-1; i )
{
fn_list=fn_list My.ExtractFileName(TmpList.Strings(i)) ",";
fs_list=fs_list My.FileSize(TmpList.Strings(i)) ",";
}
fn_list=My.CutRight(fn_list,1); //減掉最後一個 ","
fs_list=My.CutRight(fs_list,1); //減掉最後一個 ","
AContext.Connection.IOHandler.WriteLn(fn_list);
AContext.Connection.IOHandler.WriteLn(fs_list);



//傳送檔案
for (int i=0; i<=TmpList.Count-1; i )
{
filename=TmpList.Strings(i);
fstream = new FileStream(filename, FileMode.Open, FileAccess.Read);
filesize = (int)fstream.Length;
//filesize = My.StrToInt(My.StrSeg(fs_list,',',i 1));
MemoLog(client_ip " (GET_FILE): SERVER 回傳檔案 = " filename);

fstream.Position=0;
while (fstream.Position < filesize)
{
if ((filesize - fstream.Position) >= buff.Length)
readcount = buff.Length;
else
readcount = filesize - (int)fstream.Position;
//debug 用, 打開的話傳大檔會慢很多
//MemoLog(client_ip " (GET_FILE): send_bytes = " IntToStr(readcount));

//回傳遞二個以後的檔都會毀損??
//fstream.Read(buff, 0, readcount);
//AContext.Connection.Socket.Write(buff);

//要用這種方法
byte[] buff2=new byte[readcount];
fstream.Read(buff2, 0, readcount);
AContext.Connection.Socket.Write(buff2);


}

fstream.Close();
MemoLog(client_ip " (GET_FILE): SERVER 回傳檔案完成 (" filesize.ToString() " bytes)");
}



}


break;
}
}
catch (Exception E)
{
MemoLog(E.Message);
throw;
}
}
finally
{
AContext.Connection.Disconnect();
}



}
[/code]


■ 相關參考及源碼下載 : http://tw.myblog.yahoo.com/bruce0211/article?mid=282&prev=-1&next=269


編輯記錄
digitraveler 重新編輯於 2010-05-30 15:40:51, 註解 無‧
digitraveler 重新編輯於 2010-05-30 15:42:08, 註解 無‧
digitraveler 重新編輯於 2010-05-30 17:54:09, 註解 無‧
digitraveler 重新編輯於 2010-05-30 20:04:23, 註解 無‧
digitraveler
初階會員


發表:89
回覆:91
積分:46
註冊:2005-06-01

發送簡訊給我
#3 引用回覆 回覆 發表時間:2010-06-27 08:58:32 IP:111.254.xxx.xxx 訂閱
INDY 10 在 Linux Lazarus 下無法運作的解決方案

在 Unbutu 10.04 之 Lazarus 0.9.28 下灌好 INDY 10 後, 看起來都很正常, 高興之餘拉了一個 TIdTcpServer 元件到一個空白專案中 , 在 Form Active 時寫了兩行
procedure TForm1.FormActivate(Sender: TObject);
begin
IdTCPServer1.DefaultPort:= 6501;
IdTCPServer1.Active:=true;

end;


結果程式編譯完一 RUN , 程式就馬上自動結束掉 (也就是 DO NOTHING), 連錯誤訊息都沒有, 換了其它 Server 類的元件, 全部都是如此, 完了.... INDY 10 的 Server 類元件在 Linux 上無法運作 ?? 去網路查, 好像真的有 BUG ... 還說是什麼 pthread_kill problem 造成....怪不得 google 都沒找到有人在 Linux 上寫過的範例 , 搞了兩個多禮拜, 無意中發現這個問題是 Muti Thread 的問題造成


INDY 10 的 README 中有說, 在 UNIX 系統中編譯的時後要加入 "-dUseCThreads" 參數

在你的 INDY 專案主選單 -> Project -> Compiler Options 之 Other 頁, 加入 "-dUseCThreads" 編譯參數







在 Linux 跟 WIN32 環境中, 驅動 TIdTcpServer 的寫法也有不同
WIN32 :
procedure TForm1.FormActivate(Sender: TObject);
begin
IdTCPServer1.DefaultPort:= 6501;
IdTCPServer1.Active:=true;

end;

Linux
procedure TForm1.FormActivate(Sender: TObject);
begin
//uses idGlobal , the Id_IPv4 force the IdTCPServer to work in Id_IPV4 mode.
IdTCPServer1.Bindings.Add.IPVersion := Id_IPv4; //否則將出現 with socket error # 98 , address already in use
IdTCPServer1.Bindings.Add.IP:='127.0.0.1';
IdTCPServer1.Bindings.Add.Port:= 6501;
IdTCPServer1.Active:=true;
end;
在 IdTCPServer1 的 OnExecute() 事件若要處理 VCL , 請使用 TIdSync 同步物件; 否則, 直接在 OnExecute() 事件中處理 VCL 顯示類的 CODE , 將導致被連線幾次後整個 Server 程式就 Crash 掉

TIdSync 同步物件的使用, 請參照以下連結下載源碼






詳細內容請參考



Lazarus 源碼任務

INDY 10 在 Linux Lazarus 下無法運作的解決方案
http://tw.myblog.yahoo.com/bruce0829/article?mid=58&prev=-1&next=55
系統時間:2024-12-04 1:00:10
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!