全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:7767
推到 Plurk!
推到 Facebook!

關于使用Indy開發TCP/IP程式的流程

尚未結案
flowermarsh
一般會員


發表:8
回覆:9
積分:8
註冊:2007-05-25

發送簡訊給我
#1 引用回覆 回覆 發表時間:2007-05-26 11:41:28 IP:202.108.xxx.xxx 訂閱
使用indy開發TCP/IP程式是否整個流程必須這樣,能不能由服務端發起一個請求或說信號,之後再進行一系列傳輸的動作呢?
似乎TCP Server与TCP Client之間的連接可以實現上述的動作。
Stallion
版主


發表:52
回覆:1600
積分:1995
註冊:2004-09-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2007-05-26 12:41:18 IP:211.22.xxx.xxx 未訂閱
「服務」本身的基本意義就是Client「提出需求(Request)」與Server「回應需求(Response)」的機制,如此,一來一往完成通信功能,至於誰是Server或是誰是Client往往依照需求不同,在通信的過程之中可能身份互換。
至於要作什麼樣的功能,則必須在底層的通信協定之上定義自己的上層協定(Protocol),好比Client先送一個[File]的信號,當Server端收到[File]這個信號時,則可能知道Client端要傳送檔案上來了,此時Server端就要為接收檔案準備一系列的動作,當動作準備好了,可能以一個[Send]告訴Client你可以開始傳送檔案了。其他類似的功能需求依此類推~
Client<---->Server功能亦即如此!
===================引 用 flowermarsh 文 章===================
使用indy開發TCP/IP程式是否整個流程必須這樣,能不能由服務端發起一個請求或說信號,之後再進行一系列傳輸的動作呢?
似乎TCP Server与TCP Client之間的連接可以實現上述的動作。
flowermarsh
一般會員


發表:8
回覆:9
積分:8
註冊:2007-05-25

發送簡訊給我
#3 引用回覆 回覆 發表時間:2007-05-26 13:46:31 IP:202.108.xxx.xxx 訂閱
感謝Stallion的解答
解答中的含義我能够理解,不過我的想法是是否一次的通信動作,必須由客戶端發起。解答中的例子亦是由client發起的。
可是事實上,我們有些時候却需要由server發起一個通信的信號。比方説server中有新的數據需要傳達給client,可是client還沒有請求(事實上client也不知道什麽時候server有新的數據),所以需要server通知client過來取數據。這時候的發起人應該是server了。
我是想如果這樣的動作indy不能實現,我也就不用在這個方法上浪費時間了。
Stallion
版主


發表:52
回覆:1600
積分:1995
註冊:2004-09-15

發送簡訊給我
#4 引用回覆 回覆 發表時間:2007-05-26 13:52:22 IP:211.22.xxx.xxx 未訂閱
你的問題我在你的另一篇問題中有提及,Client與Server的身份本身就很難確定!問題在於是誰提出需求,誰回應需求!雙方都要對於雙方可能提出的需求有相對應的回應機制如此而以~所以你的問題不是INDY能不能達成實現,INDY本身也是包裝WINSOCK API的,如何不能達成?問題在於你如何去設計那個相映的機制。
===================引 用 flowermarsh 文 章===================
感謝Stallion的解答
解答中的含義我能够理解,不過我的想法是是否一次的通信動作,必須由客戶端發起。解答中的例子亦是由client發起的。
可是事實上,我們有些時候却需要由server發起一個通信的信號。比方説server中有新的數據需要傳達給client,可是client還沒有請求(事實上client也不知道什麽時候server有新的數據),所以需要server通知client過來取數據。這時候的發起人應該是server了。
我是想如果這樣的動作indy不能實現,我也就不用在這個方法上浪費時間了。
pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#5 引用回覆 回覆 發表時間:2007-05-26 22:06:31 IP:59.40.xxx.xxx 訂閱
通常,TCP 通信的通常的概念,就是有一方作为 server,在固定的IP/ Port等待另一方作为 Client 连接上去。

比如常见的就是用浏览器去看某个网站比如 delphi.ktop.com.tw ,这个网站的 WEB Server 就是一个 TCP Server,它在监听 80 端口,等待你的浏览器连接上去。

===================引 用 flowermarsh 文 章===================
使用indy開發TCP/IP程式是否整個流程必須這樣,能不能由服務端發起一個請求或說信號,之後再進行一系列傳輸的動作呢?
似乎TCP Server与TCP Client之間的連接可以實現上述的動作。
暗黑破壞神
版主


發表:9
回覆:2301
積分:1627
註冊:2004-10-04

發送簡訊給我
#6 引用回覆 回覆 發表時間:2007-05-26 22:26:17 IP:218.170.xxx.xxx 未訂閱
插個花。
你同意你的 ie 來看 ktop 的情況為 ie 是 client , 而 ktop 是 server 嗎?
如果同意,那我們再來說下去。
別人來發表一篇文章。這個動作會造成 server 上有新的”數據”。
那 ktop 有這個義務通知你的 ie 嗎?
沒有吧。
如果有,也是經由 email....etc 來告知。
所以你所想的這個情況。就應該分成一對 client/server 來解決了。
而不是單一連線了。

===================引 用 flowermarsh 文 章===================
感謝Stallion的解答
解答中的含義我能够理解,不過我的想法是是否一次的通信動作,必須由客戶端發起。解答中的例子亦是由client發起的。
可是事實上,我們有些時候却需要由server發起一個通信的信號。比方説server中有新的數據需要傳達給client,可是client還沒有請求(事實上client也不知道什麽時候server有新的數據),所以需要server通知client過來取數據。這時候的發起人應該是server了。
我是想如果這樣的動作indy不能實現,我也就不用在這個方法上浪費時間了。
liangyenchen
一般會員


發表:6
回覆:5
積分:2
註冊:2007-01-20

發送簡訊給我
#7 引用回覆 回覆 發表時間:2007-05-31 10:06:32 IP:59.120.xxx.xxx 訂閱
你的問題,我之前就有遇過.
簡單的說就是Clinet沒有OnExecute事件
也在板上有問過,不過我沒有得到解答.
時間久了我也得到解決,不過我並非高手,使用上可能還有不好的地方
由下面這個例子可以解決:
聊天室,由一個client傳訊息給server,server再傳這個訊息給所有連到此server的client

Client:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, ExtCtrls;

type
TClientExecute = class(TThread)//自己創一個多執行緒,內容是不斷的讀取ReadLn,模擬Onexecute
private
FReadStr: String;
protected
procedure ShowOnForm;
public
procedure execute;override;
end;

TForm1 = class(TForm)
IdTCPClient1: TIdTCPClient;
Edit2: TEdit;
Button1: TButton;
Memo1: TMemo;
Timer1: TTimer;
Edit1: TEdit;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Edit2KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
textClient: TClientExecute;

implementation
{$R *.dfm}
{ TClientExecute }
procedure TClientExecute.execute;//內容如下
var str:string;
begin
while (not textClient.Terminated) and (Form1.IdTCPClient1.Connected) do begin
str := Form1.IdTCPClient1.IOHandler.ReadLn;
FReadStr := str;
Synchronize(ShowOnForm);//同步,其實我不懂他的用意,
Sleep(1);//這個是我看其他文章提到的,Game的主迴圈方式,會導致CPU100%,所以加一個sleep就可以不會100%,這邊也是無窮迴圈,所以我也加,不確定是否有必要
end;
end;

procedure TClientEXecute.ShowOnForm;
begin
form1.memo1.lines.add(FReadStr);
end;

{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
randomize;
Memo1.Clear;
idtcpclient1.Host := '127.0.0.1';
idtcpclient1.Port := 10247;
edit2.Text := inttostr(random(10000));
idtcpclient1.Connect;
idtcpclient1.IOHandler.WriteLn('Login ' edit1.Text);
//初始
textClient:=TClientExecute.Create(true);
textClient.FreeOnTerminate:=True;
textClient.Resume;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
idtcpclient1.IOHandler.WriteLn(Edit1.Text '說:' Edit2.Text);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
idtcpclient1.IOHandler.WriteLn(Edit1.Text '說:' Edit2.Text);
end;

procedure TForm1.Edit2KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key=VK_RETURN then
idtcpclient1.IOHandler.WriteLn(Edit1.Text '說:' Edit2.Text);
end;

end.


Server:
//重點在於,Server再client連進來時,將他的AContext紀錄下來,到時就可以傳給他,不用再利用OnExecute函式裡面他的專用AContext,
//因為這樣只能當下回傳給造成OnExecute事件的client端,函式下這樣解可以回傳給該Client ::: AContext.Connection.IOHandler.WriteLn(str);
//但是一離開函式他就會不見了(Acontext),所以就不能回傳,那就把他紀錄下來,因此:


unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdCustomTCPServer,
IdTCPServer, IdContext, IdGlobal;

type //創一個紀錄,裡面有兩個我想要存的clinet資訊,他的連線,及暱稱
TUser = Record
Context: TIdContext;
NickName: String;
end;

TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure IdTCPServer1Execute(AContext: TIdContext);
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
num: integer;
User:array[0..100] of ^TUser;//創101個指標,用來紀錄玩家,有就是上限,但是要怎麼看情形動態上限呢,我不會,所以就固定八>_<

implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
Memo1.Clear;
idtcpserver1.DefaultPort := 10247;
idtcpserver1.Active := true;
//for i:=0 to 10 do
//New(con[i]);
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);//先跳過,玩家連進來不會先跑這個,
var str: string;
i:integer;
get1,get2,get3,get4:string;
begin
str := AContext.Connection.IOHandler.ReadLn;//這邊是將玩家傳過來的資訊作判斷,所以我在client一連線就會傳一個訊息過來,
if str <> '' then begin
get1 := Fetch(str);
if get1='Login' then begin//判斷為登陸.將暱稱存入與AContext相同的陣列中
for i:=0 to 100 do
if (User[i]<>nil) and (User[i]^.Context=AContext) then
User[i]^.NickName := str;
end else begin//不是login,傳文字訊息給所有人
for i:=0 to 100 do
if User[i]<>nil then
User[i]^.Context.Connection.IOHandler.WriteLn(str);
end;
end;
end;

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);//這邊玩家連進來了
var i: integer;
begin
for i:=0 to 100 do begin //這邊大概是這樣的情形,跑101次如果,該位置沒東西 New一個,並將玩家的連線(Acontext)紀錄進去,然後離開迴圈,之後玩家傳東西就會進onExecute,回頭看吧
if User[i]=nil then begin
New(User[i]);
User[i]^.Context := Acontext;
Exit;
end;
end;
end;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);//下面兩個都一樣,離線,出錯就釋放掉
var i: integer;
begin
for i:=0 to 100 do begin
if (User[i]<>nil) and (User[i]^.Context=AContext) then begin
AContext.Connection.Disconnect;
User[i]:=nil;
Exit;
end;
end;
end;

procedure TForm1.IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
var i:integer;
begin
for i:=0 to 100 do begin
if User[i]^.Context=AContext then begin
AContext.Connection.Disconnect;
User[i]:=nil;
Exit;
end;
end;
end;

end.




===================引 用 flowermarsh 文 章===================
感謝Stallion的解答
解答中的含義我能够理解,不過我的想法是是否一次的通信動作,必須由客戶端發起。解答中的例子亦是由client發起的。
可是事實上,我們有些時候却需要由server發起一個通信的信號。比方説server中有新的數據需要傳達給client,可是client還沒有請求(事實上client也不知道什麽時候server有新的數據),所以需要server通知client過來取數據。這時候的發起人應該是server了。
我是想如果這樣的動作indy不能實現,我也就不用在這個方法上浪費時間了。
編輯記錄
liangyenchen 重新編輯於 2007-05-31 10:09:57, 註解 無‧
flowermarsh
一般會員


發表:8
回覆:9
積分:8
註冊:2007-05-25

發送簡訊給我
#8 引用回覆 回覆 發表時間:2007-06-04 09:54:02 IP:202.108.xxx.xxx 訂閱

===================引 用 liangyenchen 文 章===================
感謝解答。
這種方式,我也進行過這種嘗試,但沒有這麽深入。不知這種方式的效率如何。
而我用了一種最笨的方法也實現了相似的功能,就是使用兩組indy服務和客戶端。不知你對我這種方法的想法是怎樣
John Wong
初階會員


發表:1
回覆:35
積分:32
註冊:2004-09-18

發送簡訊給我
#9 引用回覆 回覆 發表時間:2007-06-06 21:46:14 IP:218.102.xxx.xxx 未訂閱
當雙方建立TCP連線後, 雙方都可以隨時向對方送出訊息, 並不一定要先等client向server發送, 再等server回應的.
相信樓主現時現時在客戶端的方法, 是先向server發出, 然後再用ReadLn等待server的回應. 這種方法不能反應server發出的主動訊息. 解決的方法可以有下面兩種.
1) 如果server所發出的訊息不多, 可以設一個timer定時每秒或每半秒readln一次, 然後根據訊息內容處理. 這個你可以參考indy的chat sample.
2) 直接開一條thread, 內是一個loop去readln, 然後根據訊息內容處理, 處理後再readln讀取下個訊息. 而視乎情況, 處理訊息的procedure應該要thread safe的, 以免和main thread相撞.

現時我使用方法2, 並不需要建立多條連線, 伺服器端與客戶端只需建立一條連便足夠.

===================引 用 flowermarsh 文 章===================
使用indy開發TCP/IP程式是否整個流程必須這樣,能不能由服務端發起一個請求或說信號,之後再進行一系列傳輸的動作呢?
似乎TCP Server与TCP Client之間的連接可以實現上述的動作。
zhweizw
一般會員


發表:7
回覆:16
積分:9
註冊:2008-01-12

發送簡訊給我
#10 引用回覆 回覆 發表時間:2008-01-13 01:33:01 IP:58.218.xxx.xxx 訂閱
妳的問題可以這樣解決:

定義一個Record類;
Type TClientRecord = Record;
Cip,Dns:String;
CThread:Pointer;
end;
Type pClientRecord = ^TClientRecord;
var NewClientRecord :pClientRecord ;
ClientList : TThreadList;//定義綫程列錶,用于保存Server都每條綫程(也就是每個Client),記得要初始化,
如:在FormCreate中加入,ClientList :=TThreadList.Create;在程序退齣時釋放之。
在client連接到server之后,即server端的OnConnect()事件中記下每個Client的資料(Dns,Ip等):
GetMem(NewClientRecord,SizeOf(TClientRecord));//每當有Client連接到Server,都為之開坯一塊內存,用于記下資料。
NewClientRecord.Cip :=AThread.Connect.Socket.Bindings.PeerIp;//記下Client都IP。
NewClientRecod.Dns :=AThread.Connect.LocalName;
NewClientRecord.CThread :=AThread;//用于記下子綫程。

AThread.Data :=Tobject(NewClientRecord);//將此客戶耑對象記憶在相應都綫程數據中,妳用不到的話可以不寫。
Try
Clientlist.LockList.Add(NewClientRecord);//鎖定綫程列錶,將此綫程添加進去,此方法可以解決樓上兄弟無法確定Client數量都問題,他採
用Array[0..100]數組來記憶客戶耑當然也可以,但是受限製都。:--)

Finally
Clientlist.UnLockList();//無論添加綫程成功與否,都必須解鎖。否則髮生綫程死鎖,繫統將崩潰。
end;
//以下省略其他代碼

//以下開始處理關鍵問題:Client沒有請求都時候,Server主動髮送數據。
在妳端Server端,一個適當的位置(比如,Server端新數據嗅探過程Procedure中)加入以下代碼(理解原理即可,代碼不盡相同)
With ClientList.LockList do
try
For i :=0 to Count-1 do //遍歷綫程列錶中所有客戶耑,如果妳僅僅想髮送至某個客戶耑,可以在循環中加判斷代碼查找。
begin
Item[i].CThread.Connect.WriteBuffer(妳要髮送的數據,數據大小,True);
end;
Finally
ClientList.UnLockList;//同理,要解鎖
end;


時間倉促,代碼中可能存在缺陷或省略,見諒!希望對妳有說幫助。妳可通過Emai:zhweizw@163.com或者OICQ:17561129与我聯繫,共同學習,謝謝。
breathsoft
一般會員


發表:1
回覆:3
積分:0
註冊:2008-02-24

發送簡訊給我
#11 引用回覆 回覆 發表時間:2008-02-26 18:44:20 IP:221.226.xxx.xxx 訂閱
我也像楼上说的。写了一个。但发现客户端断开后,没有激活服务器端disconnect   
我用的indy 9.00.14 .
------
因为专业,所以安全;
因为安全,所以成功。
系統時間:2024-04-25 0:40:25
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!