请用过Indy的朋友帮我看一下以下问题如何解决?

tonylk 2003-09-09 09:08:36
我在做一个比较简单的网络通信程序,它要求server端和client端可以互相

向对方发送一定的命令和数据,并且让对方返回其它命令或数据。使用tcp连接或者udp

连接都可以,目前还没有定。
但是在我使用indy的过程中发现,TIdTcpServer和TIdTCPClient组件中只

有TCPClient可以主动向对方发送数据,并且也只有TCPServer可以通过OnExecute方

法来主动捕获对方发送的数据,反之都不行。
TIdUDPServer和TIdUDPClient也有类似情况,而我记得以前delphi中所带的TNMUDP

控件是双方都可以主动捕获对方发送的数据的。
我想知道到底是Indy的控件本身就不支持这样的功能,还是我需要使用一些

其它的方法来解决这个问题呢?
...全文
153 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
wxk119 2003-09-12
  • 打赏
  • 举报
回复
其实不用那么麻烦的,两端都用IdUDPServer就可以了,窗口创建时就激活,想发送消息时,用IdUDPServer.send(ip,port,strtosend)就可以了,接收消息在ONREAD里处理。
starcbh 2003-09-12
  • 打赏
  • 举报
回复
当然可以了……

客户端要有一个读线程
tonylk 2003-09-12
  • 打赏
  • 举报
回复
to sevencat(七猫) :

很不幸的告诉你,D6下的这个dclisp70我也试过了,完全一样,提示类没有注册。
并且当时我的那台机器同时安装了D5,D6,D7,BCB6,D5下的控件是可以用的,如果它们是使用相同dll的话,我再d7中用应该不会有问题。。。。可是现在就是不行。。
微雪缤纷 2003-09-12
  • 打赏
  • 举报
回复
去网上下indy的demo,\IndyDemos\IdTCPDemo
不过要Indy9以上才可以。这个例子能满足你的要求。
sevencat 2003-09-12
  • 打赏
  • 举报
回复
是的,class not registered不是因为你的问题,而是少DLL或者OCX,你去找一下看看,我估计D6里面应该有的。

又:
server.send后client除了主动去接收,不然得不到。

这就是阻塞套接字的最大优点(或缺点)因 为你必须要对两边的业务逻辑比较清楚。
假如一个在发送,另外一个就要在接收才行。

假如你觉得这个限制不好的话,你直接用C++,或者在DELPHI下面用套接字编最好了。现在我还基本上没看过用DELPHI写完成端口的呢。连SELECT都很少见。
tonylk 2003-09-11
  • 打赏
  • 举报
回复
又有一个问题想要请教各位大虾:

我安装了dclisp70包,全称是“Borland Internet Solutions Pack Components”,想使用其中的TUDP控件(即delphi5中的TNMUDP控件),可是安装完后,控件确实出现在了面板上,但是当将控件拖动到窗体上时,却提示“Class not registered”,后来我在Delphi目录下的ocx\isp目录下发现了这个控件的源代码,以及另两个文件,"GUIDS.txt"和"licenses.reg",前者的格式看不懂,不知道怎么用,我将后者导入到注册表了,可是没有任何作用。程序代码也没有怎么看懂。。我估计是需要安装什么ocx,因为它的类都是从TOleControl继承的。

我想知道,我要使用这套控件的话到底应该怎么安装呢?
tonylk 2003-09-10
  • 打赏
  • 举报
回复
现在又遇到一个奇怪的问题,以上我写的程序,在单位p4的机器上运行正常,而到了家里c3的机器上,在我自定义的OnReceive事件中接收到的数据是正确的,而之后我再用GetReceivedStream()方法得到的数据则大小始终为零,可我并没有清掉过它呀,跟到indy里去看也看不出个所以然。。。。。看然这个方法不是很稳定啊。。

另,感觉indy的作者,除了将indy设计为阻塞模式外,还极力推广:使用时建立连接,用完即断的理论,豪富哦例子都是这样的。。。
tonylk 2003-09-10
  • 打赏
  • 举报
回复
我希望的功能是Server.Send后,Client能够得到,并且回复一些数据,而Client.Send后,server也能够得到,并回复数据,应该说两端是同等地位的,
ftpServer我去研究一下。
sevencat 2003-09-10
  • 打赏
  • 举报
回复
你想做成什么样的功能???
tcpserver.send???这样是肯定行不通的。

你想做的是什么样的东西,
是不是像FTP一样的。
客户端发送一条命令?服务器再发送一个返回??
这种情况INDY里面有个COMMANDHEANDLER类可以用的。在文件TCPSERVER里面有的。
在TCPSERVER属性里面应该可以定义的。


INDY不是还有个FTPSERVER类吗?就是一个典型的互动型的例子。
tonylk 2003-09-10
  • 打赏
  • 举报
回复
to sevencat(七猫)
你所说的内容有没有例子呢?indy自带的sample都太简单了,而且我感觉写得很差。。
tonylk 2003-09-10
  • 打赏
  • 举报
回复
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPServer, IdTCPConnection,
IdTCPClient, TxServer, TxClient, StdCtrls;

type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
IdTCPClient1: TIdTCPClient;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
private
{ Private declarations }
FTxServer:TTxServer;
FTxClient:TTxClient;
procedure OnServerReceive(ASender:TObject; AIndex:Integer; AStream:TMemoryStream);
procedure OnClientReceive(ASender:TObject; AStream:TMemoryStream);
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.OnServerReceive(ASender:TObject; AIndex:Integer; AStream:TMemoryStream);
begin
//showmessage('Server ' + ASender.ClassName + ' ' + IntToStr(AIndex) + ' received stream');
//AStream.SaveToFile('f:\received.dat');
end;

procedure TForm1.OnClientReceive(ASender:TObject; AStream:TMemoryStream);
begin
//showmessage('Client ' + ASender.ClassName + ' received stream');
//AStream.SaveToFile('f:\receive.dat');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
FTxServer:=TTxServer.Create(self);
FTxServer.ServerPort:=3111;
FTxServer.ClientPort:=3112;
FTxServer.OnReceive:=OnServerReceive;
FTxServer.Active:=true;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
FTxClient:=TTxClient.Create(self);
FTxClient.ServerPort:=3111;
FTxClient.ClientPort:=3112;
FTxClient.OnReceive:=OnClientReceive;
FTxClient.Active:=true;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
FTxServer.CreateClient('tonix');
end;

procedure TForm1.Button4Click(Sender: TObject);
var
Stream:TMemoryStream;
begin
Stream:=TMemoryStream.Create();
try
Stream.LoadFromFile('f:\23-1.rm');
FTxServer.SendStream(0,Stream);
showmessage(inttostr(FTxClient.GetReceivedStream().Size));
finally
Stream.Free;
end;
end;

procedure TForm1.Button5Click(Sender: TObject);
var
Stream:TMemoryStream;
begin
Stream:=FTxClient.GetReceivedStream();
Stream.SaveToFile('f:\xxxxx.rm');
end;

procedure TForm1.Button6Click(Sender: TObject);
var
Stream:TMemoryStream;
begin
Stream:=TMemoryStream.Create();
try
Stream.LoadFromFile('f:\ScanImage001.bmp');
FTxClient.SendStream(Stream);
finally
Stream.Free;
end;
end;

procedure TForm1.Button7Click(Sender: TObject);
var
Stream:TMemoryStream;
begin
Stream:=FTxServer.GetReceivedStream(0);
Stream.SaveToFile('f:\xxxxx.bmp');
end;

procedure TForm1.Button8Click(Sender: TObject);
begin
FTxServer.ClearClientList();
end;

end.
sevencat 2003-09-10
  • 打赏
  • 举报
回复
是这样的,
因为TCPSERVER是一RUN就先开端口监听线程。
然后有连接的时候就开一个客户线程。
这时候一般先发送一个欢迎的信息。
然后就处于onexecute里面了。你只能重载那个tidpeerthread来实现你所说的功能了。

或者通过TCPSERVER中的IDPEERTHREAD或者是连接池来实现你所要的发送功能了。
蓝色光芒 2003-09-10
  • 打赏
  • 举报
回复
用TNMUDP就行了
tonylk 2003-09-10
  • 打赏
  • 举报
回复
unit TxClient;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, IdBaseComponent,
IdComponent, IdTCPServer, IdTCPClient;

type
TOnClientReceiveEvent = procedure (ASender:TObject;AStream:TMemoryStream) of object;

TTxClient = class(TComponent)
private
FActive:Boolean;
FTCPServer:TIdTCPServer;
FTCPClient:TIdTCPClient;
FServerPort:Integer;
FClientPort:Integer;
FReceivedStream:TMemoryStream;
FOnReceive:TOnClientReceiveEvent;
procedure OnServerConnect(AThread: TIdPeerThread);
procedure OnServerDisconnect(AThread: TIdPeerThread);
procedure OnServerExecute(AThread: TIdPeerThread);
procedure SyncOnReceive();
public
constructor Create(AOwner:TComponent);override;
destructor Destroy();override;
procedure SetActive(AValue:Boolean);
procedure SetServerPort(AValue:Integer);
procedure SetClientPort(AValue:Integer);
procedure SendStream(AStream:TStream);
function GetReceivedStream():TMemoryStream;
published
property Active:Boolean read FActive write SetActive;
property ServerPort:Integer read FServerPort write SetServerPort;
property ClientPort:Integer read FClientPort write SetClientPort;
property OnReceive:TOnClientReceiveEvent read FOnReceive write FOnReceive;
end;

implementation

type
TxClientException = class(Exception);

{ TTxClient }

procedure TTxClient.OnServerConnect(AThread: TIdPeerThread);
begin
FTCPClient.Host:=AThread.Connection.LocalName;
FTCPClient.Port:=FServerPort;
FTCPClient.Connect();
end;

procedure TTxClient.OnServerDisconnect(AThread: TIdPeerThread);
begin
FTCPClient.Disconnect;
end;

procedure TTxClient.OnServerExecute(AThread: TIdPeerThread);
begin
FReceivedStream.Clear;
AThread.Connection.ReadStream(FReceivedStream);
AThread.Synchronize(SyncOnReceive);
end;

procedure TTxClient.SyncOnReceive();
begin
if Assigned(FOnReceive) then begin
FOnReceive(self,FReceivedStream);
end;
end;

constructor TTxClient.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
FTCPServer:=TIdTCPServer.Create(nil);
FTCPServer.CommandHandlersEnabled:=false;
FTCPServer.ListenQueue:=1;
FTCPServer.MaxConnections:=1;
FTCPServer.OnConnect:=OnServerConnect;
FTCPServer.OnDisconnect:=OnServerDisconnect;
FTCPServer.OnExecute:=OnServerExecute;
FTCPClient:=TIdTCPClient.Create(nil);
FReceivedStream:=TMemoryStream.Create();
end;

destructor TTxClient.Destroy();
begin
if Assigned(FReceivedStream) then
FreeAndNil(FReceivedStream);
if Assigned(FTCPServer) then
FreeAndNil(FTCPServer);
if Assigned(FTCPClient) then
FreeAndNil(FTCPClient);
inherited Destroy();
end;

procedure TTxClient.SetActive(AValue:Boolean);
begin
if AValue then begin
FTCPServer.DefaultPort:=FClientPort;
FTCPServer.Active:=true;
FActive:=AValue;
end
else begin
FTCPServer.Active:=false;
FActive:=AValue;
end;
end;

procedure TTxClient.SetServerPort(AValue:Integer);
begin
if FActive then
raise TxClientException.Create('Can not modify the server port when client is activated!');
FServerPort:=AValue;
end;

procedure TTxClient.SetClientPort(AValue:Integer);
begin
if FActive then
raise TxClientException.Create('Can not modify the client port when client is activated!');
FClientPort:=AValue;
end;

procedure TTxClient.SendStream(AStream:TStream);
begin
if not FActive then
raise TxClientException.Create('Can not send stream when client not activated!');
FTCPClient.WriteStream(AStream,true,true);
end;

function TTxClient.GetReceivedStream():TMemoryStream;
begin
if not FActive then
raise TxClientException.Create('Can not get received stream when client not activated!');
Result:=FReceivedStream;
end;

end.

tonylk 2003-09-10
  • 打赏
  • 举报
回复
unit TxServer;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, IdBaseComponent,
IdComponent, IdTCPServer, IdTCPClient;

type
TOnServerReceiveEvent = procedure (ASender:TObject;AIndex:Integer;AStream:TMemoryStream) of object;

TTxServer = class(TComponent)
private
FActive:Boolean;
FTCPServer:TIdTCPServer;
FTCPClientList:TList;
FServerPort:Integer;
FClientPort:Integer;
FOnReceive:TOnServerReceiveEvent;
FLastReceiveIndex:Integer;
procedure OnServerExecute(AThread: TIdPeerThread);
public
constructor Create(AOwner:TComponent);override;
destructor Destroy();override;
procedure SetActive(AValue:Boolean);
procedure SetServerPort(AValue:Integer);
procedure SetClientPort(AValue:Integer);
procedure CreateClient(AClientAddress:String);
procedure CloseClient(AIndex:Integer);
procedure SendStream(AIndex:Integer;AStream:TStream);
function GetReceivedStream(AIndex:Integer):TMemoryStream;
function GetClientIndex(AClientAddress:String):Integer;
procedure ClearClientList();
procedure SyncOnReceive();
published
property Active:Boolean read FActive write SetActive;
property ServerPort:Integer read FServerPort write SetServerPort;
property ClientPort:Integer read FClientPort write SetClientPort;
property OnReceive:TOnServerReceiveEvent read FOnReceive write FOnReceive;
end;

implementation

type
PClientItem = ^TClientItem;
TClientItem = record
Address:String;
TCPClient:TIdTCPClient;
ReceivedStream:TMemoryStream;
end;

TxServerException = class(Exception);

{ TTxServer }

procedure TTxServer.OnServerExecute(AThread: TIdPeerThread);
var
Index:Integer;
Item:PClientItem;
begin
Index:=GetClientIndex(AThread.Connection.LocalName);
if Index<0 then begin
AThread.Connection.InputBuffer.Clear;
end
else begin
Item:=FTCPClientList.Items[Index];
Item^.ReceivedStream.Clear();
AThread.Connection.ReadStream(Item^.ReceivedStream);
FLastReceiveIndex:=Index;
AThread.Synchronize(SyncOnReceive);
end;
end;

constructor TTxServer.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
FTCPServer:=TIdTCPServer.Create(nil);
FTCPServer.CommandHandlersEnabled:=false;
FTcpServer.OnExecute:=OnServerExecute;
FTCPClientList:=TList.Create();
end;

destructor TTxServer.Destroy();
begin
if Assigned(FTCPServer) then
FreeAndNil(FTCPServer);
if Assigned(FTCPClientList) then begin
ClearClientList();
FreeAndNil(FTCPClientList);
end;
inherited Destroy();
end;

procedure TTxServer.SetActive(AValue:Boolean);
begin
if AValue then begin
FTCPServer.DefaultPort:=FServerPort;
FTCPServer.Active:=true;
FActive:=AValue;
end
else begin
FTCPServer.Active:=false;
FActive:=AValue;
end;
end;

procedure TTxServer.SetServerPort(AValue:Integer);
begin
if FActive then
raise TxServerException.Create('Can not modify the server port when server is activated!');
FServerPort:=AValue;
end;

procedure TTxServer.SetClientPort(AValue:Integer);
begin
if FActive then
raise TxServerException.Create('Can not modify the client port when server is activated!');
FClientPort:=AValue;
end;

procedure TTxServer.CreateClient(AClientAddress:String);
var
Item:PClientItem;
begin
if not FActive then
raise TxServerException.Create('Can not create client when server not activated!');
new(Item);
try
Item^.Address:=AClientAddress;
Item^.TCPClient:=TIdTCPClient.Create(nil);
Item^.ReceivedStream:=TMemoryStream.Create();
Item^.TCPClient.Host:=AClientAddress;
Item^.TCPClient.Port:=FClientPort;
Item^.TCPClient.Connect();
FTCPClientList.Add(Item);
except
if Assigned(Item^.TCPClient) then
FreeAndNil(Item^.TCPClient);
if Assigned(Item^.ReceivedStream) then
FreeAndNil(Item^.ReceivedStream);
dispose(Item);
raise;
end;
end;

procedure TTxServer.CloseClient(AIndex:Integer);
var
Item:PClientItem;
begin
if not FActive then
raise TxServerException.Create('Can not close client when server not activated!');
if (AIndex<0) or (AIndex>=FTCPClientList.Count) then
raise TxServerException.Create('Client index out of range!');
Item:=FTCPClientList.Items[AIndex];
Item^.TCPClient.Disconnect;
FreeAndNil(Item^.TCPClient);
FreeAndNil(Item^.ReceivedStream);
dispose(Item);
FTCPClientList.Delete(AIndex);
end;

procedure TTxServer.SendStream(AIndex:Integer;AStream:TStream);
var
Item:PClientItem;
begin
if not FActive then
raise TxServerException.Create('Can not send stream when server not activated!');
if (AIndex<0) or (AIndex>=FTCPClientList.Count) then
raise TxServerException.Create('Client index out of range!');
Item:=FTCPClientList.Items[AIndex];
Item^.TCPClient.WriteStream(AStream,true,true);
end;

function TTxServer.GetReceivedStream(AIndex:Integer):TMemoryStream;
var
Item:PClientItem;
begin
if not FActive then
raise TxServerException.Create('Can not get received stream when server not activated!');
if (AIndex<0) or (AIndex>=FTCPClientList.Count) then
raise TxServerException.Create('Client index out of bound!');
Item:=FTCPClientList.Items[AIndex];
Result:=Item^.ReceivedStream;
end;

function TTxServer.GetClientIndex(AClientAddress:String):Integer;
var
I:Integer;
Item:PClientItem;
begin
Result:=-1;
for I:=0 to FTCPClientList.Count-1 do begin
Item:=FTCPClientList.Items[I];
if CompareText(AClientAddress, Item^.Address)=0 then begin
Result:=I;
exit;
end;
end;
end;

procedure TTxServer.ClearClientList();
var
I:Integer;
begin
if not FActive then
raise TxServerException.Create('Can not clear client list when server not activated!');
for I:=FTCPClientList.Count-1 downto 0 do begin
CloseClient(I);
end;
end;

procedure TTxServer.SyncOnReceive();
var
Item:PClientItem;
begin
if Assigned(FOnReceive) then begin
Item:=FTCPClientList.Items[FLastReceiveIndex];
FOnReceive(self,FLastReceiveIndex,Item^.ReceivedStream);
end;
end;

end.
tonylk 2003-09-10
  • 打赏
  • 举报
回复
另,我又在D7下发现了TTCPClient,TTCPServer,TUDPSocket,这么几个控件,其中tcp的那套竟然和indy的差不多,只有server有接收事件,还有就是TUDPSocket没有例子,我试着写了一个程序,发送数据竟然不成功。

今天按照在每个网络端放置一对TIdTCPServer和TIdTCPClient的理论,自己封装了一层类,可以实现server和client端都独自发送和接受数据了,还没写完,现贴出来大家看看吧,有什么要改进的联系我:

tonyki@citiz.net
www.tonixsoft.com
tonylk 2003-09-10
  • 打赏
  • 举报
回复
但是Delphi似乎现在主推Indy系列,我想它不会连这么个简单的小问题都不能解决吧。。昨天想出一个办法,就是在我的Server端和Client端各放一对TIdTCPServer和TIdTCPClient,让TCPClient负责发送数据,TCPServer负责接收数据,可是我的Server需要能够同时和多个Client同时联系,我打算在服务端只使用一个TCPServer,再让它分析接收到的数据属于哪个Client,再分别放到不同的缓存内。

TNMUUDP以前有打开后,windows无法退出的问题,不知道Delphi7中带的这个修正了没有。

另,感觉Delphi7有些莫名其妙,QReport,NMSocket系列都变成非主流的了,而首推不那么好用的Rave和Indy系列,真的是因为这两套控件很有前途吗?
Drate 2003-09-09
  • 打赏
  • 举报
回复
呵呵,我还是喜欢用TNMUDP这个控件的
它在DELPHI6中还是有的,听说在D7中就没有被放到面板上了,不过楼主可以自己添加这个控件的呀。。

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧