如何使用TTcpClient控件(100分)

  • 主题发起人 主题发起人 tvu99
  • 开始时间 开始时间
T

tvu99

Unregistered / Unconfirmed
GUEST, unregistred user!
TcpClient1.BlockMode :=bmBlocking
能发字节时有OnSend时事发生,但收字节是没有OnReceive时事发生。
请问那位大虾有用有过这个控件。发一个例子说明。
 
应该还有个Server端的控件吧?
 
不会吧,你是怎么收的,看源代码:

function TBaseSocket.ReceiveBuf(var Buf; BufSize: Integer; Flags: Integer): Integer;
begin
Result := ErrorCheck(recv(FSocket, Buf, BufSize, Flags));
if Result <> SOCKET_ERROR then
DoReceive(pchar(@Buf), Result);
end;
procedure TBaseSocket.DoReceive(Buf: pchar; var DataLen: Integer);
begin
if Assigned(FOnReceive) then
OnReceive(Self, Buf, DataLen);
inc(FBytesReceived, DataLen);
end;
如果你调用ReceiveBuf就有OnReceive事件产生,如果你直接recv(FSocket, Buf, BufSize, Flags),
试问何事件之有?
 
如果是bmBlocking需要写线程的
还是用TIdTcpClient和TIdTcpServer简单一点
 
我不同意Tassadar的看法,请看delphi帮助:
bmBlocking The socket is set to block mode.
bmNonBlocking The socket is set to non-blocking mode.
bmThreadBlocking A separate thread is created so that listening can be performed in the background without blocking the entire server application.
所以,如果是bmBlocking模式,则TTcpServer会创建一个TServerSocketThread对象,该线程用来接收客户端
连接,并且当一个请求来时,TTcpServer会传递TServerSocketThread的OnGetThread事件,该事件用来获得一个线程,
如果没有在事件中赋予一个线程,则缺省的TClientSocketThread会被创建
procedure TCustomTcpServer.Open;
begin
inherited Open;

if Bind then
if Listen then
if BlockMode = bmThreadBlocking then
begin
GetServerSocketThread; //得到一个服务线程,如果事件里没有赋予则缺省的线程被创建
if Assigned(FServerSocketThread) and FServerSocketThread.Suspended then
FServerSocketThread.Resume;
end;
end;
function TCustomTcpServer.GetServerSocketThread: TServerSocketThread;
begin
if not Assigned(FServerSocketThread) then
FServerSocketThread := TServerSocketThread.Create(Self);
if Assigned(FServerSocketThread) then
FServerSocketThread.OnGetThread := GetThread;
Result := FServerSocketThread;
end;
 
Delphi的帮助我看过了,如果用ThreadBlocking不写自己的线程还不如不用
TServerSocket和TClientSocket的好处就是可以写线程自己控制,
如果懒得写,还不如用TIdTcpServer,如果id的那一套东西还不如delphi原来
有的,borland公司就不会把它买下来了
 
demo里有详细的程序呀!
 
我个人认为,TClientSocket和TServerSocket他们的收发函数都是线程安全的,所以你可以创建
独自的线程进行发送数据,可以在OnReceive事件里进行接收数据,因为他们通过select或异步
事件来产生收数据事件所以如果简单的收发操作,用它们是个不错的选择。
而TTcpServer和TTcpClient并没有创建线程来收发数据,TTcpServer是有一个线程,但它是用来
管理请求连接的。至于收发数据并没有原始触发事件,也就是说,数据来了TTcpClient并不能接收
数据并触发OnReceive事件,这对你自己控制你的归约非常有利。
所以我认为,它们用的场合不同,如果具体做windows窗口程序,它们并不一定有用,可能你需要
开发基于api的组件,并象TClientSocket和TServerSocket那样创建窗口来接收Socket异步事件。
 
引用:einstrain的话
TTcpServer和TTcpClient并没有创建线程来收发数据,TTcpServer是有一个线程,但它是用来
管理请求连接的

我想我以前的理解可能是错误的因此我做了一个简单的测试程序,代码如下
服务器:
procedure TfmServer.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
AThread.Connection.ReadLn;
Memo1.Lines.Append('Thread:' + IntToHex(Integer(AThread), 8)
+ ' Id:' + IntToStr(Integer(AThread.Data)) + ' Executed');
AThread.Data := nil;//如果Data不等于空在Disconnect的时候tcpServer会自动释放,会出错
end;

{ TThreadData }

procedure TfmServer.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
AThread.Data := Pointer(Fid);
Inc(FId);
Memo1.Lines.Append('Thread:' + IntToHex(Integer(AThread), 8)
+ ' Id:' + IntToStr(Integer(AThread.Data)) + ' Connected');
end;

procedure TfmServer.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
Memo1.Lines.Append('Thread:' + IntToHex(Integer(AThread), 8)
+ ' Disconnected');
end;

procedure TfmServer.FormCreate(Sender: TObject);
begin
FId := 0;
end;
客户端:
procedure TfmClient.Button1Click(Sender: TObject);
begin
IdTCPClient1.Connect;
IdTCPClient1.WriteLn('a');
end;

procedure TfmClient.Button2Click(Sender: TObject);
begin
IdTCPClient1.Disconnect;
end;

结果:
我用三个客户端同时连接同一个服务器
然后再依次断开
最后再用第一个链接一次,结果如下
Thread:00D10860 Id:0 Connected
Thread:00D10860 Id:0 Executed
Thread:00D107D0 Id:1 Connected
Thread:00D107D0 Id:1 Executed
Thread:00D13E24 Id:2 Connected
Thread:00D13E24 Id:2 Executed
Thread:00D10860 Disconnected
Thread:00D107D0 Disconnected
Thread:00D13E24 Disconnected
Thread:00D10860 Id:3 Connected
Thread:00D10860 Id:3 Executed

从结果可以看出来,每一个连接的Thread的地址都是不一样的
但是也可能三个地址指向同一个Thread对象,可是连Data的值都不是
一样的,因此从这个试验我认为,对于TTcpServer来说每一个连接都是
对应独立的一个Thread对象的
而且从最后一次的连接可以看到,其地址跟第一次连接的地址是一样的
因此我们可以断定,TTcpServer使用了线程池技术。

试验代码可以在下面下载:
ftp://61.152.210.98/Socket.rar
 
>>TClientSocket和TServerSocket他们的收发函数都是线程安全的
确实如此,你可以看他的SendBuf什么的都是try lock finally unlock
什么的,不过我认为如果是用TServerSocket还是用线程模式要好一些
ClientSocket当然是非组塞要容易控制一些
 
我们可以看以下源代码可以看出TTcpServer有一个管理请求的线程,如果有连接请求会为每一个请求
在线程缓冲池里取出(如没有则创建一个)一个接收请求线程:
procedure TServerSocketThread.Execute;
var
T: TClientSocketThread;
begin
while not Terminated and Assigned(FServerSocket) and FServerSocket.Listening do
begin
if FServerSocket.WaitForConnection then
if not Terminated then
begin
T := FetchClientSocketThread;
if not Assigned(T) then
T := AddClientSocketThread;
if Assigned(T) then
T.Resume;
Sleep(0);
end;
end;
end;
procedure TClientSocketThread.Execute;
begin
ThreadObject := Self;
while not Terminated do
begin
if Assigned(FServerSocketThread) and not FServerSocketThread.Terminated and
Assigned(FServerSocketThread.ServerSocket) then
begin
FClientSocket := TCustomIpClient.Create(nil);
try
FServerSocketThread.ServerSocket.Accept(FClientSocket);
finally
FClientSocket.Free;
FClientSocket := nil;
end;
end;
if not Terminated then
Suspend;
end;
end;

对于TServerSocket我认为无论是线程模式还是非阻塞模式都是挺好用的,
用法没有什么不同,只不过触发事件的方式不同,对于线程模式事件触发是有线程调用Select
检查Socket来触发事件,而对于非阻塞模式则是通过异步事件来触发事件,而线程模式既可以把
socket设置为阻塞和非阻塞模式,你可以查一查socket io模式,select检查事件上是检查socket
当前是否可读写及有否异常.
 
tvu98 说的TCPserver 应该是Delphi 7带的那个. 不是 Indy的
 
后退
顶部