http://blog.csdn.net/quicmous/article/details/4263115
最近使用了Delphi7提供的TcpCLient和TcpServer两个VCL控件,发现这两个控件使用非常简单,不像网上不少帖子说的那样难用、性能差。
TcpClient和TcpServer没有采用事件驱动的模式,而是采取了同步方法调用的策略(网上有帖子称之为阻塞模式),大大简化了编写Sockets通讯程序的过程。如果我们还停留在事件驱动的思维习惯上,就会发现很难使用这两个控件。
Delphi7提供了一个名为NetChat的演示程序,该程序展示了TcpClient和TcpServer的初步用法。
一、 数据的发送与接收
//数据发送代码: procedure TForm1.btnSendClick(Sender: TObject); var I: Integer; begin TcpClient1.RemoteHost := edtRemoteHost.Text; //服务器IP TcpClient1.RemotePort := edtRemotePort.Text; //服务器端口号 try if TcpClient1.Connect then for I := 0 to memSend.Lines.Count - 1 do TcpClient1.Sendln(memSend.Lines[I]); finally TcpClient1.Disconnect; end; end; //在OnAccept事件中接收数据(此处作了简化): procedure TForm1.TcpServer1Accept(sender: TObject; ClientSocket: TCustomIpClient); var s: string; begin s := ClientSocket.Receiveln; while s <> '' do begin //此处对s做适当处理,代码略。 s := ClientSocket.Receiveln; end; end;
TcpServer在接收到数据后,只产生一个OnAccept事件。在OnAccept时间响应函数中,我们循环调用ClientSocket参数的Receiveln方法接受数据。让该方法返回空字符串时,意味着连接已经断开。
需要注意的是:
1.Receiveln方法在客户端没有传送数据时一直处于等待状态。
2.演示程序用s <> ''判断连接是否断开,下面我们可以看到其它判断方法。
二、 TcpServer的OnAccept事件中的参数ClientSocket
OnAccept事件响应函数带一个名为ClientSocket的参数,该参数是一个TCustomIpClient类型的对象。利用该参数可以方便我们访问客户端信息(客户端IP、端口等),并管理连接。
前面我们看到可以利用Receiveln返回空值的条件判断连接是否结束。如果希望实现超时断开连接功能的话,可以采用ClientSocket对象的WaitForData方法,等待数据。该方法允许指定等待时间,方法声明如下:
function WaitForData(TimeOut: Integer = 0): boolean;
可以调用ClientSocket的Disconnect方法在退出前断开连接。
三、 关于OnAccept多线程的问题
在同时收到多个客户端信息时,OnAccept应该在内存存在多个线程实例。因此,在OnAccept的代码,必须符合多线程设计原则。
Delphi的VCL库和其它一些组件是面向单线程设计的。这表明对VCL控件等组件的调用,必须通过TThread类的Synchronize方法调用。在NetChat这个演示程序中,提供了相应的代码供参考。更多资料请参考Delphi多线程编程的相关资料。
四、从服务器返回数据
如果需要从服务器返回数据的话,只需要在OnAccept中调用ClientSocket.Sendln方法即可。
客户端需要做的就是在执行完TcpClient1.Sendln后,直接调用 TcpClient1.Receiveln即可。该方法会等待服务器传回的信息。当然如果希望超时断开的话,也可以调用WaitForData方法,判断在给定时间内是否有数据返回。
四、 常见问题
问题:为什么TcpClient的OnReceive事件不响应服务器传回的数据?
原因很简单,OnReceive事件是由TcpClient的Receiveln等函数激发的,用于在Receiveln等函数接受到数据之前过滤数据用。
因此,如果你不调用TcpClient的Receiveln等函数,OnReceive事件就永远不会被激发。