怎样通过idtcpclient 发送数据发送和接收16进制

DELPHI中如果是静态调用的话,要在前面做一个声明的,动态就不用
静态调用:
unit Unit1;
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdC
TForm1 = class(TForm)
Button1: TB
procedure Button1Click(Sender: TObject);
{ Private declarations }
{ Public declarations }
Form1: TForm1;
External 'Project1.dll';{静态调用}
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
动态调用:
unit Unit1;
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdC
TForm1 = class(TForm)
Button1: TB
procedure Button1Click(Sender: TObject);
{ Private declarations }
{ Public declarations }
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
Sabout := LoadLibrary('Project1.dll');
if Sabout=0 then begin
Application.MessageBox('动态连接库Project1.dll文件不存在!','错误',64);
ShowA := GetProcAddress(Sabout,'showform');
FreeLibrary(Sabout);
本文主要讲解了利用IdTCPClient发送图片,IdTCPServer接受图片的方法,下面是关键代码:客户端: //连接到服务器procedure TForm1.btn1Click(Sender: TObject);begin with idtcpclient1 do begin Host:='127.0.0.1'; Port:=7788; C if Connected then btn1.Enabled:=F //发送图片procedure TForm1.btn2Click(Sender: TObject);var stream:TMemoryS x:Ibegin if idtcpclient1.Connected then begin stream:=TMemoryStream.C img1.Picture.Graphic.SaveToStream(stream); x:=stream.S idtcpclient1.WriteStream(stream,True,True,x); ShowMessage(idtcpclient1.ReadLn); stream.F服务端: //激活服务器procedure TForm1.btn1Click(Sender: TObject);begin idtcpserver1.DefaultPort:=7788; idtcpserver1.Active:=T btn1.Enabled:=F//接受图片procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);var strem:TMemoryS jpegimage:begin strem:=TMemoryStream.C AThread.Connection.ReadStream(strem,-1,False); try strem.Position:=0; img1.Picture.Graphic := jpegimage := tjpegimage.C jpegimage.LoadFromStream(strem); img1.Picture.Graphic:= finally strem.F jpegimage.F AThread.Connection.WriteLn('收到图片');
delphi 接收心跳包怎么写
 有开发网络应用经历的人都知道,网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?&
  有人一定想到使用Send函数中的返回结果来进行判断。如果返回的长度和自己发送出去的长度一致,那就说明这个套接字是可用的,否则此套接字一定出现了问题。但是我们并不是无时无刻的发送数据呀。如何解决呢?&
  其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳,那TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),并且此信息不会影响你自己定义的协议。&
  在VC中实现心跳的例子很多,可是在DLEPHI中一直没有相应的代码。下面我是我使用DELPHI编写的关于心跳的代码(以IOCP为例),希望对大家有帮助。&
定义心跳常量&
IOC_IN =$;&
IOC_VENDOR =$;&
IOC_out =$;&
SIO_KEEPALIVE_VALS =IOC_IN or IOC_VENDOR or 4;&
inKeepAlive,OutKeepAlive:TTCP_KEEPALIVE;&
实现代码是在Acceptsc:= WSAAccept(Listensc, nil, nil, nil, 0);代码的后面加入:&
if setsockopt(Acceptsc,SOL_SOCKET,SO_KEEPALIVE,@opt,sizeof(opt))=SOCKET_ERROR then&
closesocket(Acceptsc);&
inKeepAlive.onoff:=1;&
//设置3秒钟时间间隔&
  inKeepAlive.keepalivetime:=3000;&
//设置每3秒中发送1次的心跳&
inKeepAlive.keepaliveinterval:=1;&
insize:=sizeof(TTCP_KEEPALIVE);&
outsize:=sizeof(TTCP_KEEPALIVE);&
if WSAIoctl(Accept,SIO_KEEPALIVE_VALS,@inKeepAlive,insize,@outKeepAlive,outsize,@outByte,nil,nil)=SOCKET_ERROR then&
closesocket(Acceptsc);&
如果加入以上的代码以后,系统会每3秒中加入一次的心跳。并且如果客户端断线以后(网线断),函数GetQueuedCompletionStatus会返回FALSE。&
if (GetQueuedCompletionStatus(CompletionPort, BytesTransferred,DWORD(PerHandleData), POverlapped(PerIoData), INFINITE) = False) then&
//在这里处理客户端断线信息。&
以上就是我使用心跳的方法,此方法我已经在我的网络游戏中使用。情况稳定!
TIdUDPClient控件中文指南 &
22:48:28| &分类: delphi程序备忘 |字号 订阅
IDUDPClient
本控件的通信标准是RFC 768,网址是:http://www.rfc-editor.org/rfc/rfc768.txt
本控件使用Send方法发送数据,远程主机由Host和Port属性指定。
远程计算机名称。用来指定远程计算机系统。该名称可以是IP地址,如“129.71.2.4”,也可以是计算机
名称,如“wvnvm.wvnet.edu”。如果你要访问的目标计算机是UDP客户端所在计算机,Host属性应为“
127.0.0.1”。
远程计算机端口号。
ReceiveTimeout: I
指定绑定的socket的访问等待时间。单位毫秒。
指定绑定的socket分派是否可用。
值得注意的是,在IDE环境下,active属性总是返回false。
Binding: TIdSocketH
是只读属性。用来描述发送和接收数据socket。不像TCP,UDP不与远程计算机创建永久的连接。
BroadcastEnabled: B
该属性用来指定是否广播发送。
BufferSize: I
指定UDP包的最大尺寸。
LocalName:
指定本地计算机名称。
指定Indy版本号。只读属性。
procedure Send(AData: string);
向远程计算机发送数据。
procedure SendBuffer(var AB const AByteCount: integer);
向远程计算机发送数据。
Broadcast(const AData: const APort: integer);
向网络上的所有计算机发送数据。(广播)
function ReceiveBuffer(var AB const ABufferSize: I const AMSec: Integer =
IdTimeoutDefault):
function ReceiveBuffer(var AB const ABufferSize: I var VPeerIP: var
VPeerPort: AMSec: Integer = IdTimeoutDefault):
从远程计算机读取数据。
function ReceiveString(const AMSec: Integer = IdTimeoutDefault):
function ReceiveString(var VPeerIP: var VPeerPort: const AMSec: Integer =
IdTimeoutDefault):
从远程计算机读取数据
procedure Send(AHost: const APort: I const AData: string);
向远程计算机发送数据
procedure SendBuffer(AHost: const APort: I var AB const AByteCount:
向远程计算机发送数据
property OnStatus: TIdStatusE
当前连接状态事件。
被用来构造当前状态文本信息的格式化参数。
当前连接状态。是以下状态之一:
& & & & &hsResolving - 主机名称被解析成IP地址
hsConnecting - 一个连接正在被打开
hsConnected - & 一个连接已经被打开
hsDisconnecting - 一个连接正在关闭
hsDisconnected - 一个连接已经被关闭
hsText - 该连接正在产生含信息的消息
TIdUDPServer控件中文指南 &
22:46:28| &分类: delphi程序备忘 |字号 订阅
IdUDPServer
property Bindings: TIdSocketH
用来收集f TIdSocketHandle实例。包含ID_SOCK_DGRAM类型的socket。
property DefaultPort:
监听新连接的端口。
property ThreadedEvent:
指示UDP读事件的执行方式。是否以线程的形式执行
property Active: B
是否开始监听。
property Binding: TIdSocketH
只读属性。指示读写传送的socket句柄。
property BroadcastEnabled: B
是否广播传送数据。
property BufferSize: I
UDP包的尺寸。
property ReceiveTimeout: I
从一个服务中读取数据的超时时间。
property LocalName:
本地计算机名称。
property Version:
只读属性,用来获取Indy部件的版本号码。
procedure Broadcast(const AData: const APort: integer);
在网络上发送数据到所有的计算机。
unction ReceiveBuffer(var AB const ABufferSize: I const AMSec: Integer =
IdTimeoutDefault):
function ReceiveBuffer(var AB const ABufferSize: I var VPeerIP: var
VPeerPort: AMSec: Integer = IdTimeoutDefault):
从远程连接中读取数据
function ReceiveString(const AMSec: Integer = IdTimeoutDefault):
function ReceiveString(var VPeerIP: var VPeerPort: const AMSec: Integer =
IdTimeoutDefault):
从远程连接中读取数据
procedure Send(AHost: const APort: I const AData: string);
向远程计算机系统发送数据。
procedure SendBuffer(AHost: const APort: I var AB const AByteCount:
向远程计算机系统发送数据
property OnUDPRead: TUDPReadE
UDP读取事件发生时执行。
接受UDP数据报的socket。
在UDP数据报中接受数据的流。
property OnStatus: TIdStatusE
指示当前连接状态的句柄。
用来构造当前状态文本消息的格式化参数。
当前连接状态。取值范围与TIdUDPClient类中的OnStatus事件相同。
procedure BeginWork(AWorkMode: TWorkM const ASize: Integer = 0);
当OnBeginWork事件触发时执行
AWorkMode可以取的值:
wmRead--从远程连接中读取数据。
wmWrite-- 向远程连接发送数据。
procedure DoWork(AWorkMode: TWorkM const ACount: Integer);
当OnWork事件触发时执行。
procedure EndWork(AWorkMode: TWorkMode);
当OnEndWork事件触发时执行。
TIdTCPServer控件中文指南 .
13:53 236人阅读 评论(0) 收藏 举报&
&IdTCPServer&
该控件包含一个完整的、多线程TCP服务器。该控件使用一个或者多个线程监听(listen)客户机连接,使用
时与TIdThreadMgr联合使用,将每个线程分配给与客户机连接的连接上。
TIdTCPServer提供允许配置服务器监听线程的功能,包括:
DefaultPort
ListenQueue
OnListenException
ReuseSocket
MaxConnections
MaxConnectionReply
该控件也提供控制协议特殊功能的属性和方法,包括:
ReplyExceptionCode
ReplyUnknownCommand
该控件用来实现两机之间的连接,支持以下事件:
OnDisconnect
OnException
该控件支持协议命令的控制,包括:
CommandHandlers
CommandHandlersEnabled
OnNoCommandHandler
OnAfterCommandHandler
OnBeforeCommandHandler
该控件是以下控件的父类:
TIdChargenServer, TIdDayTimeServer, TIdDICTServer, TIdEchoServer, TIdFingerServer,
TIdGopherServer, TIdHostNameServer, TIdHTTPServer, TIdIRCServer, TIdNNTPServer, TIdQUOTDServer,
TIdTelnetServer, TIdWhoisServer
一些重要的属性
property ListenQueue:
允许排队未解决的最大监听连接数。
property ReuseSocket: TIdReuseS
本地地址中被重新使用的监听线程。
property MaxConnections: I
最大允许的连接数。
property MaxConnectionReply: TIdRFCR
到达最大连接后,返回给其它请求的连接的消息。
property ReplyExceptionCode: I
在发生异常后,返回给连接的代码。
property ReplyTexts: TIdRFCR
服务器实现的协议响应。
property ReplyUnknownCommand: TIdRFCR
对未知命令的响应。
property CommandHandlers: TIdCommandH
命令处理器集合。
property CommandHandlersEnabled:
在监听线程连接时是否使用命令处理器。
property Greeting: TIdRFCR
当监听线程连接成功后发送的标题信息。
TIdTCPClient控件中文指南(转) .
14:48 343人阅读 评论(0) 收藏 举报&
06:37 P.M.
IdTCPClient&
该控件包装了一个完整的TCP客户端。该客户端包括sock支持。该控件是以下客户端控件的父类:
TIdDayTime, TIdEcho, TIdFinger, TIdFTP, TIdGopher, TIdHTTP, TIdNNTP, TIdPOP3, TIdQUOTD,
TIdSMTP, TIdTelnet, and TIdWhois。
property BoundIP:
客户端使用的IP地址。
property BoundPort: I
当前连接的本地端口号。
property BoundPortMax: I
当前连接时可以使用的最大本地端口号。
property BoundPortMin: I
当前连接可以使用的最小本地端口号。
property Host:
远程主机名。可以是IP地址,也可以是域名。
property Port:
远程主机端口号。
property Intercept: TIdConnectionI
对连接进行读写操作时插入的中途截取者(插件)。
property IOHandler: TIdIOH
发送和接受数据时的输入输出源(插件)。
property MaxLineAction: TIdMaxLineA
当读数据时已经到了最大缓冲区长度时触发的动作(事件处理器)。
property MaxLineLength: I
读取数据时最大缓冲区长度(字节数)。
property ReadTimeout: I
读取数据时的超时事件,单位毫秒。
property Socket: TIdIOHandlerS
连接时所用的socket。
procedure Connect(const ATimeout: Integer = IdTimeoutDefault);
打开客户端连接。
function ConnectAndGetAll:
打开客户端连接并获取所有的数据。
function AllData:
得到连接中所有的数据。
procedure CancelWriteB
停止写入数据。
procedure Capture(ADest: TS const ADelim: string = '.'; const AIsRFCMessage: Boolean =
procedure Capture(ADest: TS out VLineCount: I const ADelim: string = '.'; const
AIsRFCMessage: Boolean = True);
procedure Capture(ADest: TS const ADelim: string = '.'; const AIsRFCMessage: Boolean
procedure Capture(ADest: TS out VLineCount: I const ADelim: string = '.';
const AIsRFCMessage: Boolean = True);
将连接中的数据读取到特殊的对象中去。
procedure CheckForDisconnect(const ARaiseExceptionIfDisconnected: boolean =&
const AIgnoreBuffer: boolean = false);
决定是否关闭连接。
procedure CheckForGracefulDisconnect(const ARaiseExceptionIfDisconnected: Boolean = True);
决定是否正常地关闭连接。
function CheckResponse(const AResponse: SmallI const AAllowedResponses: array&
of SmallInt): SmallI
检查允许的响应中的合法的响应。
procedure ClearWriteB
清除写缓冲区。
procedure CloseWriteB
关闭写缓冲区。
function Connected: B
当前连接是否可用。
function CurrentReadBuffer:
从协议栈中更新缓冲区。
procedure D
关闭连接。
procedure FlushWriteBuffer(const AByteCount: Integer = -1);
写入缓存的数据,并清除写缓冲区。
procedure ReadBuffer(var AB const AByteCount: Longint);
从读取缓冲区中读取数据。
procedure Write(const AOut: string);
向连接写入数据。
procedure WriteBuffer(const AB AByteCount: L const AWriteNow: Boolean = False);
向当前连接写入缓冲数据。
procedure WriteStream(AStream: TS const AAll: Boolean = T const AWriteByteCount:
Boolean = F const ASize: Integer = 0);
向当前连接写入流数据。
procedure ReadStream(AStream: TS AByteCount: LongInt = -1; const AReadUntilDisconnect:
boolean = false);
从当前连接中读取流数据
关于idtcpserver的使用 .
20:18 2199人阅读 评论(0) 收藏 举报&
&用idTCPServer,客户端接上来时,如何取得客户端的IP? &
IP:=AThread.Connection.Binding.PeerIP;
Port:=AThread.Connection.Binding.PeerP &
尝试解答你的疑问:
在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: TIdServerThreadE
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&
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.R
&LIOHandler: TIdIOH
&LPeer: TIdTCPServerC
&LThread: TIdPeerT
& &if Assigned(Server) then begin &// This is temporary code just to test one exception
& & &while True do begin
& & & &LThread :=
& & & &LPeer := TIdTCPServerConnection.Create(Server);
& & & &LIOHandler := Server.IOHandler.Accept(Binding.Handle, SELF);
& & & &if LIOHandler = nil then begin
& & & & &FreeAndNil(LPeer);
& & & & &S
& & & & &E
& & & &end
& & & &else begin
& & & & &LThread := TIdPeerThread(Server.ThreadMgr.GetThread);
& & & & &LThread.FConnection := LP
& & & & &LThread.FConnection.IOHandler := LIOH
& & & & &LThread.FConnection.FFreeIOHandlerOnDisconnect :=
& & & &// LastRcvTimeStamp := N &// Added for session timeout support
& & & &// ProcessingTimeout := F
& & & &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.D
& & & & &FreeAndNil(LThread); &// This will free both Thread and Peer.
& & & &end else begin
& & & & &Server.Threads.Add(LThread); //APR
& & & & &// Start Peer Thread
& & & & &LThread.S
& & & & &B
& &on E: Exception do begin
& & &if Assigned(LThread) then
& & & &FreeAndNil(LThread);
& & &Server.DoListenException(Self, E);
由上述源码可以看到,TCPServer每次侦听到一个连接,就会新建一个idPeerThread,
而当这个idPeerThread触发OnExecute事件的时候,就会调用IdTCPServer1Execute,
所以indy支持多进程是无疑的,而在IdTCPServer1Execute中一定要考虑同步问题 &
indy的idTcpServer, 大量的client不正常断开造成的问题,求大家帮忙查原因?
首先定义了如下一个记录和指针
&TSimpleClient = Record
& &id: & & & & & &//系统编号
& &utype: & & & & &//gprs, emp, unknow
& &Name: & & & & & //手机号,登录操作员名称
& &IP: & & & & & & //IP
& &Port: & & & & &//端口
& &Status: & & & & //NULL &登录中 &操作中
& &LastTime: & & &//登录时间
& &UpdateTime: I & &//更新时间
& &HardWare: S & & & //硬件类型
& &DataBackTime: I &//监控时间, 超时则断开
&PClient = ^TSimpleC
//客户新建链接时记录客户端信息到记录中
procedure TfrmNet.TCPServerConnect(AThread: TIdPeerThread);
var Client: PC
&Client := new( PClient );
&Client.id & & & & & &:= GetTickCount + Random(1000);
&Client.uType & & & & := 'GUEST';
&Client.IP & & & & & &:= AThread.Connection.Socket.Binding.PeerIP;
&Client.Port & & & & &:= AThread.Connection.Socket.Binding.PeerP
&Client.LastTime & & &:= GetTickC
&Client.UpdateTime & &:= Client.LastT
&Client.Status & & & &:= '登录中';
&Client.Name & & & & &:= 'GUEST';
&Client.HardWare & & &:= GPS_NAME_UNKNOW;
&Client.DataBackTime &:= 3600; &//监控周期
&AThread.Data := Pointer( client ); & &&指到 athread的指针中
//客户端断开事件中释放
procedure TfrmNet.TCPServerDisconnect(AThread: TIdPeerThread);
var Client: PC
&Client := Pointer(AThread.Data);
&AThread.Data :=
//与客户通讯的处理过程
procedure TfrmNet.TCPServerExecute(AThread: TIdPeerThread);
&if (AThread.Connection.Connected) and (not Athread.Terminated) then
& &sStr := AThread.Connection.CurrentReadB
&//其他不会造成任何死循环或者异常的处理代码 &
//关掉当前用户以前打开的tcp/ip链接
//当用户突然断线时,所打开的tcp/ip链接有可能继续保持不断线
procedure TfrmNet.Clients_CloseGprsBeforeConnect( curId: I sName: String );
&Client: PC
&List := tcpServer.Threads.LockL
& &for i := 0 to List.Count -1 do begin
& & & &Client := Pointer( TIdPeerThread(List.Items[i]).Data );
& & & &if Client =
& & & &if (Client.Name && sName) or (Client.id = curId ) or (Client.utype && 'GPRS') then C
& & & &if TIdPeerThread(List.Items[i]).Connection.Connected then
& & & & &TIdPeerThread(List.Items[i]).Connection.D
& & &except
& & & &TIdPeerThread(List.Items[i]).S
& &tcpServer.Threads.UnlockL
大量的终端设备通过TCP/IP连接到服务器,由于设备硬件以及使用的是GPRS网络的原因.
设备经常会像断电那样,重新连接服务器,然而之前的连接没有断开(不知道是不是GPRS的原因)
虽然程序已经做了判断,断开这种异常的连接,
但是服务器程序还是经常会死掉,原因不明.请大家帮助分析分析.
求:使用IdTcpClient和IdTcpServer相互发送数据
======================server============================
unit Unit1;
&Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
&Dialogs, IdBaseComponent, IdComponent, IdTCPServer, StdC
&TForm1 = class(TForm)
& &Memo1: TM
& &Edit1: TE
& &Button1: TB
& &IdTCPServer1: TIdTCPS
& &procedure IdTCPServer1Connect(AThread: TIdPeerThread);
& &procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
& &procedure Button1Click(Sender: TObject);
& &procedure IdTCPServer1Execute(AThread: TIdPeerThread);
& &{ Private declarations }
& &{ Public declarations }
&PSocketThread=^TSocketT
&TSocketThread=Record
& &SocketThread:TIdPeerT
& &Next:PSocketT
&Form1: TForm1;
&ST_Head,ST_End:PSocketT
&ST_Count:
implementation
{$R *.dfm}
procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
&PST_:PSocketT
&New(PST_);
&PST_^.SocketThread:=AT
&PST_^.Next:=
&if ST_Count=0 then
& &ST_Head:=PST_;
& &ST_End:=ST_H
& &ST_End^.Next:=PST_;
& &ST_End:=PST_;
&ST_Count:=ST_Count+1;
&Edit1.Text:=IntToStr(ST_Count);
procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
&PST_,PST_0:PSocketT
&PST_:=ST_H
&PST_0:=ST_H
&while PST_&&nil do
& &if PST_^.SocketThread.ThreadID=AThread.ThreadID then
& & &PST_0^.Next:=PST_^.N
& & &Dispose(PST_);
& & &ST_Count:=ST_Count-1; & & &
& & &Edit1.Text:=IntToStr(ST_Count);
& &end else
& & &PST_0:=PST_;
& & &PST_:=PST_^.N
procedure TForm1.Button1Click(Sender: TObject);
&PST_:PSocketT
&PST_:=ST_H
&while PST_&&nil do
& &PST_^.SocketThread.Connection.WriteLn('To U '+IntToStr(PST_^.SocketThread.ThreadID)+#$A);
& &PST_:=PST_^.N
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
&Memo1.Lines.Add(AThread.Connection.ReadLn);
================================client====================================
unit Unit1;
&Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
&Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
&TForm1 = class(TForm)
& &IdTCPClient1: TIdTCPC
& &Button1: TB
& &Memo1: TM
& &Edit1: TE
& &Button2: TB
& &Button3: TB
& &procedure Button1Click(Sender: TObject);
& &procedure IdTCPClient1Connected(Sender: TObject);
& &procedure Button2Click(Sender: TObject);
& &procedure IdTCPClient1Disconnected(Sender: TObject);
& &procedure FormClose(Sender: TO var Action: TCloseAction);
& &procedure Button3Click(Sender: TObject);
& &{ Private declarations }
& &{ Public declarations }
&Form1: TForm1;
implementation
{$R *.dfm}
procedure ReadT
&form1.Memo1.Lines.Add('Begin reading...');
&s:=Form1.IdTCPClient1.ReadLn;
&while doRead do
& &s:=Form1.IdTCPClient1.ReadLn;
& &form1.Memo1.Lines.Add(s);
& &sleep(100);
procedure TForm1.Button1Click(Sender: TObject);
&IdTCPClient1.Connect(3000);
&CreateThread(nil,0,@ReadThread,nil,0,td);
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
&Memo1.Lines.C
&Memo1.Lines.Add('connected to server');
procedure TForm1.Button2Click(Sender: TObject);
&IdTCPClient1.WriteLn(Edit1.Text);
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
&ExitThread(td);
&Memo1.Lines.Add('disConnected from server');
procedure TForm1.FormClose(Sender: TO var Action: TCloseAction);
&IdTCPClient1.D
&ExitThread(td);
procedure TForm1.Button3Click(Sender: TObject);
// &IdTCPClient1.D
如何解决使用IdTcpServer时CPU利用率很高的问题?
程序主要功能就是使用IdTcpServer将数据发送给每个连接的IdTcpClient我试过两种方法.
但是服务端程序的CPU利用率很高,占用了所有资源.我试过用Application.ProcessMessages
但是无效,我又禁止了所有界面的操作也没用.没发数据的时候也一直居高不下,我用了IdAntiFreeze也没什么效果,还有用了IdThreadMgrPool也
程序虽然能正常运行,但CPU利用率却这么高,没道理呀,难道是我的程序处理逻辑有问题?还是有什么其他地方没考虑或设置周到?
那位有类似的代码参考一下,或者有没有什么其他更好的方法,功能要求简单:只要将数据从服务端单方向发送给每给客户端就可以了(不考虑用U
DP,不考虑用广播包,因为需要他能在INTERNET上运行)
方法一:SERVER端使用EXECUTE发送,客户端建立一个线程接收
procedure TCastProxy.TCPServerExecute(AThread: TIdPeerThread);
// &application.ProcessM
& & athread.Connection.WriteStream(tempclient.ClientData,true,true,0);
& & tempclient.ClientData.C
& & &on e:exception do begin
& & & & &Athread.Connection.D
procedure TCastProxy.TcpClientThreadRun(Sender: TIdCustomThreadComponent);
&Adata:TmemoryS
&AData:=TmemoryStream.C
// &application.ProcessM
&if assigned(Adata) then begin
& & if Tcpclient.Connected then begin
// & & & &Adata.C
& & & &try
& & & & & tcpclient.ReadStream(Adata,-1,false);
& & & // &.........
& & & &except
& & & & & on e:exception &do begin
& & & & & & & &tcpclient.D
& & end else begin
& & & & try
& & & & & &Tcpclient.Connect(1000);
& & & & except
& & & & & &on e:exception &do
& & & & & & & & &statusbar.SimpleText:=e.M
方法二:SERVER端使用循环发送给每个客户,客户端使用线程接收
if TcpServer.Active then begin
& & &Threads:=Tcpserver.Threads.LockL
& & &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.D
& & &TcpServer.Threads.UnlockL
哈哈,自己解决了.
怪自己没有好好看例子. &
很久了,不太记得了
好像是在server端的execute里面加了一句sleep(100);这个100可以自己改,大于18就可以。就是说发送数据了要休息一下,不要不停的发,你
试试先,不行就把你的代码发给我帮你看看
内容整理来自:&
用户掉线的检测方法
如果客户端异常掉线或拔掉网线,那么在服务端会留下一个TCP连接,这个连接会变成死连接。出现的死TCP连接过多,服务器内存和端口将会增加,直到占满服务器的端口和耗尽内存为止。如果这样的话,服务器无法健壮稳定的运行。使用TCP协议自带的心跳包功能解决这个问题。
TCP keep-alive原理
一个TCP keep-alive 包是一个简单的ACK,该ACK包内容为一个比当前连接sequence number 小于一的包。主机接受到这些ACKs会返
回一个包含当前sequence number 的ACK包。 Keep-alives一般被用来验证远端连接是否有效。如果该连接上没有其他数据被传输,或者更高level 的 keep-alives被传送,keep-alives 在每个KeepAliveTime被发送。(默认是 7,200,000 milliseconds ,也就是2个小时)。
如果没有收到 keep-alive 应答,keep-alive 将在每 KeepAliveInterval 秒重发一次。KeepAliveInterval 默认为1秒。如 Microsoft 网络功能中很多部分中采用的 NETBT 连接,更常见的是发送 NETBios keep-alives,所以,在 NetBios 连接中通常不发送TCP keep-alives。 TCP保持连接默认被禁用,但是微软Sockets应用程序可以使用SetSockOpt函数去启用他们。
请看下面的类
type & TCP_KeepAlive = record &&& OnOff: C &&& KeepAliveTime: C // 多长时间(ms)没有数据就开始send心跳包& &&& KeepAliveInterval: Cardinal // 每隔多长时间(ms)send一个心跳包,发5次(系统值)
KeepAliveTime: TCP连接多长时间(毫秒)没有数据就开始发送心跳包,有数据传递的时候不发送心跳包 KeepAliveInterval: 每隔多长时间(毫秒)发送一个心跳包,发5次(系统默认值)
如果客户端网络中断,服务器系统发送心跳包后,服务器会自动解除TCP连接。这一点,大家可以使用 netstat -p -tcp 命令查看
—建立稳定服务程序之TCP心跳包的使用
为了能让我们的服务程序更加稳定,有些细节问题必须解决。就如上一讲中提到的客户端拔掉网线,造成服务器上TCP变成死连接,如果死连接数量过多,对服务器能长期稳定运行是一个巨大的威胁。
另外,经过测试,如果服务器上有TCP死连接,那么服务程序连接数据库,也会产生那个一个死连接。这样的话,给数据库服务器也造成威胁。所以,服务器程序编写的好坏,直接影响系统的稳定性!
如何解决TCP死连接的问题,有多种方法,其中最有效的就是心跳包技术。
我们在DSServer的OnConnect事件中加入心跳包代码
uses IdTCPConnection,IdWinsock2
type & TCP_KeepAlive = record &&& OnOff: C &&& KeepAliveTime: C &&& KeepAliveInterval: C
procedure TServerContainer1.DSServer1Connect & (DSConnectEventObject: TDSConnectEventObject); var & Val: TCP_KeepA & Ret: DW & ClientConnection: TIdTCPC begin & ClientConnection := TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id); & Val.OnOff := 1; & Val.KeepAliveTime := 5000; & Val.KeepAliveInterval := 3000; & WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4, &&& @Val, SizeOf(Val), nil, 0, @Ret, nil, nil);
观察上述代码, 我们把心跳包放到服务端上执行,如果服务器的某个TCP连接在5秒钟没有收到数据,将会发送向对端发送心跳包,间隔3秒钟,连续发送5次(参数详解见上一 讲高级技术4)。如果5次以后对端还没有应答,服务器将结束该TCP连接。TCP的连接可以使用 netstat -p tcp 命令查看。
当该TCP结束后,delphi编写的服务程序会自动结束和数据库的连接。我用的是FireBird数据库,大家可以使用命令查看 SELECT MON$USER, MON$REMOTE_ADDRESS, & MON$REMOTE_PID, & MON$TIMESTAMP &FROM MON$ATTACHMENTS
现在服务器的tcp死连接和数据库的死连接都清除了,我们的系统将能长期稳定的运行。
加强服务程序对访问者的控制能力
1)作为一个服务程序,如果不限制客户端访问数量,后果将是很可怕的。如果有人恶搞,服务器不堪重负,内存将耗尽,最终服务器将宕机。如何限制访问者的数量呢?
我们可以设置一个变量,来记录来访者的数量,如果超过我们既定的数字,那么后续的连接服务器请求,都将被断掉。
2)限制了访问数量,但是如果不做密码身份认证,无关的人员也将能登陆服务器!解决办法是客户端传入用户名和密码,如果用户名和密码不正确,连接将被挂断。
在客户端的SQLConnection1中driver分类的username和password属性设置好用户名和密码。
3)尽量不要设置DSTCPServerTransport1的Maxthreads属性,还有数据库连接池也不要设置,delphi2010会有内存泄露,这两个参数保存默认即可。
在dsserver1控件的onconnect事件中加入如下代码(使用的是tcp/ip连接):
procedure TMainForm.DSServer1Connect & (DSConnectEventObject: TDSConnectEventObject); var & val: TCP_KeepA & Ret: I & ClientConnection: TIdTCPC begin & // 最大连接数量,验证来访者密码 & if (DSConnectEventObject.ChannelInfo = nil) or (Connections &= 500) or &&& (DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName] &&&&& && 'sunstone') or (DSConnectEventObject.ConnectProperties &&&&& [TDBXPropertyNames.Password] && 'mypassword') then & begin &&& DSConnectEventObject.DbxConnection.D &&& // ClientConnection.D & end & else & begin &&& // 获取socket连接 &&& ClientConnection := TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id); &&& ClientConnection.OnDisconnected := ClientDisconnectE
&&& // 记录来访者数量 &&& inc(Connections); &&& lblShowConnections.Caption := IntToStr(Connections);
&&& if Trim(ShowConnections.Cells[0, 1]) && '' then &&&&& ShowConnections.RowCount := ShowConnections.RowCount + 1;
&&& ShowConnections.Cells[0, ShowConnections.RowCount - 1] := IntToStr &&&&& (DSConnectEventObject.ChannelInfo.Id); &&& ShowConnections.Cells[1, ShowConnections.RowCount - 1] := &&&&& ClientConnection.Socket.Binding.PeerIP + ':' + IntToStr &&&&& (ClientConnection.Socket.Binding.PeerPort); &&& ShowConnections.Cells[2, ShowConnections.RowCount - 1] := &&&&& DSConnectEventObject.ConnectProperties[TDBXPropertyNames.UserName]; &&& ShowConnections.Cells[3, ShowConnections.RowCount - 1] := &&&&& DSConnectEventObject.ConnectProperties[TDBXPropertyNames.Password]; &&& ShowConnections.Cells[4, ShowConnections.RowCount - 1] := FormatDateTime &&&&& ('yyyy-mm-dd hh:nn:ss', Now); &&& // ShowConnections.Cells[6, ShowConnections.RowCount - 1] := &&& // DSConnectEventObject.ConnectProperties &&& // [TDBXPropertyNames.ServerConnection];
&&& // 设置心跳包 &&& val.OnOff := 1; &&& val.KeepAliveTime := 5000; &&& val.KeepAliveInterval := 1000; &&& WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4, &&&&& @val, SizeOf(val), nil, 0, @Ret, nil, nil); &
& &在学习idTCPServer的监听机制前,先认识两个对象:
& & & &1.TidListenerThread:下文称之为监听对象。这是一个线程对象。
& & & &2.TListenerThreads,下文称之为监听列表对象。负责保存并管理监听对象。
& & &在idTCPServer内部,使用一个监听列表对象,来保存监听对象。具体的监听任务由监听对象负责。为什么要用监听列表对象来管理监听对象呢?是因为,一台服务器可能有多个网卡,针对每个网卡idTCPServer都可以建立一个监听对象,负责监听每个网卡收到的客户端的请求,这样的话,就会有多个监听对象需要统一进行管理。当然,这取决于你设置idTCPServer使用几块网卡,下文会继续说明。
& & & &接下来,还要认识两个新的对象: & & & &3.Bindings,下文称之为网卡绑定集合对象,类型为TIdSocketHandles。这是一个集合对象,集合元素为 网卡绑定对象。同时,这个对象被idTCPServer公布为Bindings属性,用户在设计期就可以对这个对象进行设置,下图为设置 idTCPServer的Bindings属性的窗口。通过这个窗口,可以看到,可以为idTCPServer设置多个IP与端口,同时可以指定IP的版 本,是IPv4,还是IPv6。通过这个窗口提供的信息,可以理解为Server定义一个或多个Binding对象,即网卡绑定对象。
& & & &4.Binding,下文称之为网卡绑定对象,类型为TIdSocketHandle。这个对象,可以理解为通过网卡与客户端进行通讯操作的对象,接收与发送信息。
& & & &现在,简单了解了网卡绑定对象Binding后,就可以看看监听对象的具体职责了。
& & & &配置好TidTCPServer的Bindings后,打开idTCPServer.Active=True,这时候,Server会初始化内部用的对象,如网卡绑定列表对象,最后要调用StartListening方法,开始监听。对于上面的说明,通过这个方法的实现代码,可以更清晰的解读。
& & & &StartListening方法的实现逻辑可以分为两部分: & & & &第一部分,预处理Binding对象。通过循环,取出网卡绑定集合对象Bindings中的每一个网卡绑定对象Binding,然后调用Binding对象的AllocateSocket及Bind方法做预处理。
& & & &第二部分,建立监听对象,将预处理后的Binding对象置入监听对象,让监听对象对Binding进行监听。同时,将新建的监听对象放入监听列表对象,由监听列表对象统一管理。在这里,我们能看到,所谓的监听对象,实质就是监听网卡绑定对象Binding,进一步深究细节,可查看监听对象的Run方法,这个方法实际上就是Thread对象的Execute方法,在这个方法中,不断的检测网卡绑定对象Binding。
&&&&& 下面一起看看StartListening方法的实现过程,为了方便理解上述内容,我去掉了一些代码。
procedure TIdCustomTCPServer.StartL
& LListenerThreads: TIdListenerL
& LListenerThread: TIdListenerT
& LBinding: TIdSocketH
begin //第一部分的预处理代码:
//取出监听列表对象
& LListenerThreads := FListenerThreads.LockL
& try &&& //第一次I=0
& & I := LListenerThreads.C
& & & //第一次启动Server时,从0开始循环网卡绑定列表对象
& & & while I & Bindings.Count do begin &&&&&&& //取出一个Binding,即一个TIdSocketHandle对象
& & & & LBinding := Bindings[I];
& & & & LBinding.AllocateS//分配Socket & & & & LBinding.B//绑定
& & & & Inc(I);
& & except
& & & Dec(I);
& & & while I &= 0 do begin
& & & & Bindings[I].CloseS
& & & & Dec(I);
& & &//在预处理过程中,如果异常则关闭所有的Binding
& & &//关闭Binding后再提出异常!idTCPServer表现为无法启动。
//第二部分建立监听对象的代码:
//针对每个Binding建立一个
& & for I := LListenerThreads.Count to Bindings.Count - 1 do
& & & LBinding := Bindings[I];//取出一个网卡绑定对象
//建立一个新的监听对象ListenerThread, //这里:Create的参数:Self是idTCPServer,LBinding:网卡绑定对象Binding //可以理解为监听对象监听的是当前Server的一个Binding对象。
& & & LListenerThread := TIdListenerThread.Create(Self, LBinding);
& & & try //加到监听列表对象中
& & & & LListenerThreads.Add(LListenerThread);
& & & except //如果发生异常则关闭刚才的Binding,释放刚建立的监听对象,然后再提升异常,表现为服务无法正常启动.
& & & & LBinding.CloseS
& & & & FreeAndNil(LListenerThread);
& & & //启动刚建立的监听对象,开始对其Binding进行监听.(如何监听的Binding,可转到监听对象的Run方法)。
& & & LListenerThread.S
& & FListenerThreads.UnlockL
&&&&& 通过阅读上面简化的StartListening方法,相信你能够明白监听对象是如何产生的了!那么监听对象是如何消失的呢?答案是在StopListening方法里,利用监听列表对象,对每个监听对象进行了释放。这个方法,又是在停止Server时被调用的。 &&&&&& 下面是简化后的StopListening方法,便于理解线程监听列表对象的作用及监听对象如何被释放。 procedure TIdCustomTCPServer.StopL var & LListenerThreads: TIdListenerL & LListener: TIdListenerT begin & LListenerThreads := FListenerThreads.LockL & try &&& //利用循环监听列表对象,释放所有监听对象 &&& while LListenerThreads.Count & 0 do begin &&&&& LListener :=LListenerThreads[0]; &&&&& //停止监听对象线程 &&&&& LListener.T &&&&& //调用网卡绑定对象,关闭Socket &&&&& LListener.Binding.CloseS &&& & finally &&& FListenerThreads.UnlockL &
&&&&&& 实话说,也是初学Indy,写的不对的地方,还请多多指正。

我要回帖

更多关于 idtcpclient onwork 的文章

 

随机推荐