关于idtcpserver的使用
原文:http://blog.csdn.net/hnxxcxg/article/details/2798019
用idTCPServer,客户端接上来时,如何取得客户端的IP?
IP:=AThread.Connection.Binding.PeerIP;
Port:=AThread.Connection.Binding.PeerPort;
尝试解答你的疑问:
问题一:
在Form1中放入IDTCPServer控件,一旦有socket连接,IDTCPServer自动建立一个线程与之
建立一个TCP/IP连接,我们在IDTCPServer.OnExecute中写入自己的代码就可以在这个独立
的线程中完成我们所希望的动作吗?
解答:
一旦有socket连接,IDTCPServer 不仅建立一个线程,更需要把这个建立的线程保存到一个
线程列表中去。然后在 IDTCPServer.OnExecute 中传入“每线程”这个参数,程序从传入
的“每线程”这个参数,检索出对应的 socket 连接。
问题二:
如果我们在OnExecute中调用TForm1.aaa这个函数,那么这个函数是不是会造成同步问题,
例如登录人数的统计。
解答:
统计登录人数不应该在这个事件中处理。其实只要读一下线程列表就可以知道结果。OnExecute
中的同步,是 indy 的一个工作要求,同时发生的客户必须排队处理。所以,原则上不会
造成同步问题,但是,如果你引用的 Form 过程中,有异步变量,就要注意可能的同步问题。
不知道这样的回答,是否能让你满意。
您是不是不要这样理解:
“FForm.IdTCPClient1.Connected then // IdTCPClient1 在 FForm 这个主线程中”
您可以理解为:IdTCPClient1 是类实例 FForm 的一个成员。至于您的 TReceiveThread
中引用了 FForm 这个类,并把这个 FForm 类做为了 TThread 类的成员就值得考虑了。
按你描述的意思,大概这个 TfmClient 是一个 TForm 类,这时候除非你动态在线程里创建
这个 TfmClient 类,不然的话就可能有苦头吃了,很容易造成死锁。
已经说了,Indy 是一个多线程控件,在 Server 连接的时候,针对每客户会创建一个线程,
只要有客户发送数据,就会激活 Srever 的 OnExecute 事件。需要做的,就是在 OnExecute
中识别是哪个客户(也即线程)发来的请求,针对这个客户的 socket 连接返回服务就可以
了。
Server 端首先是响应客户的 Connect 事件,一旦连接了,就自动在服务端建立了一个连接
线程。而这个连接线程是需要 Server 维护的,indy 的最大连接线程数不会大于 600 个,
有 600 个线程你还不够用的话,基本上就不能使用 indy 控件了。
Event handler for peer thread execution.
property OnExecute: TIdServerThreadEvent;
Description
OnExecute is an event handler for TIdServerThreadEvents. OnExecute occurs when a TIdPeerThread attempts to perform the
TIdPeerThread.Run method. OnExecute receives AThread as a parameter, representing the TIdPeerThread thread that will be
started.
Assign a TIdServerThreadEvent event handler procedure to OnExecute to respond to the event notification.
Use CommandHandlers and CommandHandlersEnabled to provide finer control over commands executed for a peer thread connection.
-----
procedure TIdListenerThread.Run;
var
LIOHandler: TIdIOHandler;
LPeer: TIdTCPServerConnection;
LThread: TIdPeerThread;
begin
try
if Assigned(Server) then begin // This is temporary code just to test one exception
while True do begin
LThread := nil;
LPeer := TIdTCPServerConnection.Create(Server);
LIOHandler := Server.IOHandler.Accept(Binding.Handle, SELF);
if LIOHandler = nil then begin
FreeAndNil(LPeer);
Stop;
Exit;
end
else begin
LThread := TIdPeerThread(Server.ThreadMgr.GetThread);
LThread.FConnection := LPeer;
LThread.FConnection.IOHandler := LIOHandler;
LThread.FConnection.FFreeIOHandlerOnDisconnect := true;
end;
// LastRcvTimeStamp := Now; // Added for session timeout support
// ProcessingTimeout := False;
if (Server.MaxConnections > 0) and // Check MaxConnections
NOT TIdThreadSafeList(Server.Threads).IsCountLessThan(Server.MaxConnections)
then begin
Server.ThreadMgr.ActiveThreads.Remove(LThread);
LPeer.WriteRFCReply(Server.MaxConnectionReply);
LPeer.Disconnect;
FreeAndNil(LThread); // This will free both Thread and Peer.
end else begin
Server.Threads.Add(LThread); //APR
// Start Peer Thread
LThread.Start;
Break;
end;
end;
end;
except
on E: Exception do begin
if Assigned(LThread) then
FreeAndNil(LThread);
Server.DoListenException(Self, E);
end;
end;
End;
由上述源码可以看到,TCPServer每次侦听到一个连接,就会新建一个idPeerThread,
而当这个idPeerThread触发OnExecute事件的时候,就会调用IdTCPServer1Execute,
所以indy支持多进程是无疑的,而在IdTCPServer1Execute中一定要考虑同步问题
indy的idTcpServer, 大量的client不正常断开造成的问题,求大家帮忙查原因?
首先定义了如下一个记录和指针
type
TSimpleClient = Record
id: longint; //系统编号
utype: string; //gprs, emp, unknow
Name: string; //手机号,登录操作员名称
IP: string; //IP
Port: integer; //端口
Status: string; //NULL 登录中 操作中
LastTime: integer; //登录时间
UpdateTime: Integer; //更新时间
HardWare: String; //硬件类型
DataBackTime: Integer; //监控时间, 超时则断开
end;
PClient = ^TSimpleClient;
//客户新建链接时记录客户端信息到记录中
procedure TfrmNet.TCPServerConnect(AThread: TIdPeerThread);
var Client: PClient;
begin
Client := new( PClient );
Client.id := GetTickCount + Random(1000);
Client.uType := 'GUEST';
Client.IP := AThread.Connection.Socket.Binding.PeerIP;
Client.Port := AThread.Connection.Socket.Binding.PeerPort;
Client.LastTime := GetTickCount;
Client.UpdateTime := Client.LastTime;
Client.Status := '登录中';
Client.Name := 'GUEST';
Client.HardWare := GPS_NAME_UNKNOW;
Client.DataBackTime := 3600; //监控周期
AThread.Data := Pointer( client ); <<指到 athread的指针中
end;
//客户端断开事件中释放
procedure TfrmNet.TCPServerDisconnect(AThread: TIdPeerThread);
var Client: PClient;
begin
Client := Pointer(AThread.Data);
AThread.Data := nil;
end;
//与客户通讯的处理过程
procedure TfrmNet.TCPServerExecute(AThread: TIdPeerThread);
var c: PClient;
begin
if (AThread.Connection.Connected) and (not Athread.Terminated) then
begin
sStr := AThread.Connection.CurrentReadBuffer;
end;
//其他不会造成任何死循环或者异常的处理代码
end;
//关掉当前用户以前打开的tcp/ip链接
//当用户突然断线时,所打开的tcp/ip链接有可能继续保持不断线
procedure TfrmNet.Clients_CloseGprsBeforeConnect( curId: Integer; sName: String );
var i: integer;
list: TList;
Client: PClient;
begin
List := tcpServer.Threads.LockList;
try
for i := 0 to List.Count -1 do begin
try
Client := Pointer( TIdPeerThread(List.Items[i]).Data );
if Client = nil then continue;
if (Client.Name <> sName) or (Client.id = curId ) or (Client.utype <> 'GPRS') then Continue;
if TIdPeerThread(List.Items[i]).Connection.Connected then
TIdPeerThread(List.Items[i]).Connection.Disconnect;
except
TIdPeerThread(List.Items[i]).Stop;
end;
end;
finally
tcpServer.Threads.UnlockList;
end;
end;
问题是:
大量的终端设备通过TCP/IP连接到服务器,由于设备硬件以及使用的是GPRS网络的原因.
设备经常会像断电那样,重新连接服务器,然而之前的连接没有断开(不知道是不是GPRS的原因)
虽然程序已经做了判断,断开这种异常的连接,
但是服务器程序还是经常会死掉,原因不明.请大家帮助分析分析.
求:使用IdTcpClient和IdTcpServer相互发送数据
======================server============================
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPServer, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Edit1: TEdit;
Button1: TButton;
IdTCPServer1: TIdTCPServer;
procedure IdTCPServer1Connect(AThread: TIdPeerThread);
procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
procedure Button1Click(Sender: TObject);
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
private
{ Private declarations }
public
{ Public declarations }
end;
type
PSocketThread=^TSocketThread;
TSocketThread=Record
SocketThread:TIdPeerThread;
Next:PSocketThread;
end;
var
Form1: TForm1;
ST_Head,ST_End:PSocketThread;
ST_Count:integer;
implementation
{$R *.dfm}
procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
var
PST_:PSocketThread;
begin
New(PST_);
PST_^.SocketThread:=AThread;
PST_^.Next:=nil;
if ST_Count=0 then
begin
ST_Head:=PST_;
ST_End:=ST_Head;
end
else
begin
ST_End^.Next:=PST_;
ST_End:=PST_;
end;
ST_Count:=ST_Count+1;
Edit1.Text:=IntToStr(ST_Count);
end;
procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
var
PST_,PST_0:PSocketThread;
begin
PST_:=ST_Head;
PST_0:=ST_Head;
while PST_<>nil do
begin
if PST_^.SocketThread.ThreadID=AThread.ThreadID then
begin
PST_0^.Next:=PST_^.Next;
Dispose(PST_);
ST_Count:=ST_Count-1;
Edit1.Text:=IntToStr(ST_Count);
end else
begin
PST_0:=PST_;
PST_:=PST_^.Next;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
PST_:PSocketThread;
begin
PST_:=ST_Head;
while PST_<>nil do
begin
PST_^.SocketThread.Connection.WriteLn('To U '+IntToStr(PST_^.SocketThread.ThreadID)+#$A);
PST_:=PST_^.Next;
end;
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
Memo1.Lines.Add(AThread.Connection.ReadLn);
end;
end.
================================client====================================
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient;
type
TForm1 = class(TForm)
IdTCPClient1: TIdTCPClient;
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure IdTCPClient1Connected(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure IdTCPClient1Disconnected(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
td:Dword;
doRead:boolean;
implementation
{$R *.dfm}
procedure ReadThread;
var
s:String;
begin
form1.Memo1.Lines.Add('Begin reading...');
s:=Form1.IdTCPClient1.ReadLn;
while doRead do
begin
s:=Form1.IdTCPClient1.ReadLn;
form1.Memo1.Lines.Add(s);
sleep(100);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
IdTCPClient1.Connect(3000);
doRead:=true;
CreateThread(nil,0,@ReadThread,nil,0,td);
end;
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('connected to server');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
IdTCPClient1.WriteLn(Edit1.Text);
end;
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
ExitThread(td);
Memo1.Lines.Add('disConnected from server');
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
doRead:=false;
IdTCPClient1.Disconnect;
ExitThread(td);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
// IdTCPClient1.Disconnect;
doRead:=false;
end;
end.
如何解决使用IdTcpServer时CPU利用率很高的问题?
程序主要功能就是使用IdTcpServer将数据发送给每个连接的IdTcpClient我试过两种方法.
但是服务端程序的CPU利用率很高,占用了所有资源.我试过用Application.ProcessMessages
但是无效,我又禁止了所有界面的操作也没用.没发数据的时候也一直居高不下,我用了IdAntiFreeze也没什么效果,还有用了IdThreadMgrPool也
没用,
程序虽然能正常运行,但CPU利用率却这么高,没道理呀,难道是我的程序处理逻辑有问题?还是有什么其他地方没考虑或设置周到?
那位有类似的代码参考一下,或者有没有什么其他更好的方法,功能要求简单:只要将数据从服务端单方向发送给每给客户端就可以了(不考虑用U
DP,不考虑用广播包,因为需要他能在INTERNET上运行)
方法一:SERVER端使用EXECUTE发送,客户端建立一个线程接收
procedure TCastProxy.TCPServerExecute(AThread: TIdPeerThread);
begin
// application.ProcessMessages;
try
athread.Connection.WriteStream(tempclient.ClientData,true,true,0);
tempclient.ClientData.Clear;
except
on e:exception do begin
Athread.Connection.Disconnect;
end;
end;
end;
procedure TCastProxy.TcpClientThreadRun(Sender: TIdCustomThreadComponent);
var
Adata:TmemoryStream;
begin
AData:=TmemoryStream.Create;
// application.ProcessMessages;
if assigned(Adata) then begin
if Tcpclient.Connected then begin
// Adata.Clear;
try
tcpclient.ReadStream(Adata,-1,false);
// .........
except
on e:exception do begin
tcpclient.Disconnect;
end;
end;
end else begin
try
Tcpclient.Connect(1000);
except
on e:exception do
statusbar.SimpleText:=e.Message;
end;
end;
end;
Adata.Free;
end;
方法二:SERVER端使用循环发送给每个客户,客户端使用线程接收
if TcpServer.Active then begin
try
Threads:=Tcpserver.Threads.LockList;
for temp:=0 to Threads.Count-1 do begin
try
adata.Position:=0;
TIdPeerThread(Threads[temp]).Connection.WriteStream
(Adata,true,true,adata.Size);
except
On E:Exception do
TIdPeerThread(Threads[temp]).Connection.Disconnect;
end;
end;
finally
TcpServer.Threads.UnlockList;
end;
end;
哈哈,自己解决了.
怪自己没有好好看例子.
很久了,不太记得了
好像是在server端的execute里面加了一句sleep(100);这个100可以自己改,大于18就可以。就是说发送数据了要休息一下,不要不停的发,你
试试先,不行就把你的代码发给我帮你看看
关于idtcpserver的使用的更多相关文章
- Delphi实例-IdTCPServer和IdTCPClient的使用(支持文件发送)
相关资料: http://blog.csdn.net/earbao/article/details/46514313 结果注意: 1.Use IdContext.IdGlobal 这两个单元2.不能使 ...
- IdTCPServer, idTCPClient
IdTcpServer uses IdContext //需要引用 属性,方法: IdTCPServer.Active :=True; //开启服务器 IdTCPServer1.Bindings.Ad ...
- Delphi组件indy 10中IdTCPServer修正及SSL使用心得
indy 10终于随着Delphi2005发布了,不过indy套件在我的印象中总是复杂并且BUG不断,说实话,不是看在他一整套组件的面子上,我还是喜欢VCL原生的Socket组件,简洁,清晰.Indy ...
- Delphi IdTCPClient IdTCPServer 点对点传送文件
https://blog.csdn.net/luojianfeng/article/details/53959175 2016年12月31日 23:40:15 阅读数:2295 Delphi ...
- IdTCPServer
IdTCPServer1 Server本身就支持多线程,一个服务端连接多个客户端. IdTCPServer1.Bindings.Add.IP := '127.0.0.1';IdTCPServer1.B ...
- 短信猫 TIdTCPServer TIdTCPClient
短信猫 服务端: IdTCPServer1: TIdTCPServer; IdAntiFreeze1: TIdAntiFreeze; unit UnitSever; interface uses Wi ...
- 高人ozhy111提供的下载资源
特别是有很多手机方面的独创源代码,先记下来,有空挨个看一遍: http://download.csdn.net/user/ozhy111 比如:idtcpserver文件传输xe7PC端及手机端 ht ...
- Indy10 即时通讯Demo
最近闲来无事,重新学习了Indy10,顺手写了一段即时通讯代码.与上次写的笔记有不同之处,但差别不大. 未研究过TCP打洞技术,所以下面的代码采用的是 客户端--服务器--客户端 模式,也就是服务 ...
- Indy的TCPServer到底能支持多少个连接
最近一个项目,最开始使用IdTcpServer,在大压力测试的时候,只连接了800个多一点的客户端(每个客户端连接上之后每秒钟发送一个几十字节的报文,服务器应答).但是持续的时间不会超过10分钟,服务 ...
随机推荐
- Degree Set CodeForces - 976D(双指针)
题意: 构造一个无向图,使得无向图里的所有点的度数 所组成的集合 即为给出的几个数 解析: 题中的数是以上升的顺序给出的, 我们对于dn+1个数进行处理,对于当前数i,有两个操作 1.向后边的所有点连 ...
- poj 2528 (线段树+特殊离散化)
Mayor's posters Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 51098 Accepted: 14788 ...
- 【BZOJ1297】[SCOI2009]迷路(矩阵快速幂)
[BZOJ1297][SCOI2009]迷路(矩阵快速幂) 题面 BZOJ 洛谷 题解 因为边权最大为\(9\),所以记录往前记录\(9\)个单位时间前的.到达每个点的方案数就好了,那么矩阵大小就是\ ...
- SpringBoot中使用SpringDataJPA
SpringDataJPA的使用 JPA是什么? JPA(Java Persistence API)是Sun官方提出的Java持久化规范. 为Java开发人员提供了一种对象/关联映射工具来管理Java ...
- Spring中 <context:property-placeholder 的使用与解析 .properties 配置文件的加载
转: Spring中property-placeholder的使用与解析 Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配 ...
- 项目实战SportsStore——订单处理模块
前面的步骤如果顺利完成,你的网站运行之后应该能够正常显示下面三个页面: 1.产品列表 2.购物车内容页面 在某个商品后面点击“添加到购物车”则出现下图页面: 此页面上点击“继续购物”按钮则返回到产品列 ...
- Python 基础数据类型之tuplu
Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 1.元组的定义 tuple1 = ("hello", "world&qu ...
- nova-api源码分析(APP的调用)
调用APIRouter的 __call__函数 nova/wsgi.py class Router(object): def __init__(self, mapper): self.map = ma ...
- Spark记录-spark-submit学习
#查看帮助:./bin/spark-submit --help ./bin/spark-shell --help 用法1: spark-submit [options] <app jar | ...
- POJ - 1094 Sorting It All Out(拓扑排序)
https://vjudge.net/problem/POJ-1094 题意 对于N个大写字母,给定它们的一些关系,要求判断出经过多少个关系之后可以确定它们的排序或者排序存在冲突,或者所有的偏序关系用 ...