我需要设置自定义超时TTcpClient.我认为默认超时时间约为20-25秒,但我需要将其更改为500毫秒.有可能吗?怎么样?
procedure TForm1.Button1Click(Sender: TObject);
begin
TcpClient2.RemoteHost := '192.168.1.1';
TcpClient2.RemotePort := '23';
TcpClient2.Connect;
tcpclient2.Receiveln();
tcpclient2.Sendln('admin');
tcpclient2.Receiveln;
end;
Run Code Online (Sandbox Code Playgroud)
我尝试了non-blocking选项,但是在我点击按钮后软件返回错误而且我必须再次执行4-5次.有帮助吗?
谢谢 :)
Winsock没有连接超时,但这可以克服.
你有几个选择:
没有线程:
使用非阻塞模式:调用Connect,然后使用Winsock select函数等待(封装在TTcpClient继承的TBaseSocket Select方法中).
使用阻止模式:暂时更改为非阻塞模式并按前一种情况继续进行.
使用线程:请参阅Remy Lebeau对如何使用Winsock API控制连接超时的回答?.
使用Indy.
使用阻塞或非阻塞模式是一个非常重要的设计决策,它将影响您的许多代码,之后您无法轻易更改.
例如,在非阻塞模式下,接收函数(as Receiveln)不会等到有足够的可用输入并且可以返回空字符串.如果这是您所需要的,这可能是一个优势,但您需要实现一些策略,例如TcpClient.WaitForData在调用receive函数之前等待使用(在您的示例中,Receiveln-Sendln-Receiveln将不会按原样工作).
对于简单的任务,阻塞模式更容易处理.
以下函数将等待连接成功或超时结束:
function WaitUntilConnected(TcpClient: TTcpClient; Timeout: Integer): Boolean;
var
writeReady, exceptFlag: Boolean;
begin
// Select waits until connected or timeout
TcpClient.Select(nil, @writeReady, @exceptFlag, Timeout);
Result := writeReady and not exceptFlag;
end;
Run Code Online (Sandbox Code Playgroud)
如何使用:
// TcpClient.BlockMode must be bmNonBlocking
TcpClient.Connect; // will return immediately
if WaitUntilConnected(TcpClient, 500) then begin // wait up to 500ms
... your code here ...
end;
Run Code Online (Sandbox Code Playgroud)
还要注意TTcpClient的非阻塞模式设计中存在以下缺点/缺陷:
OnError与SocketError设置为WSAEWOULDBLOCK(10035).Connected属性将false因为被分配Connect.连接超时可以通过在创建套接字之后但在调用之前更改为非阻塞模式Connect,并在调用之后恢复为阻塞模式来实现.
这有点复杂,因为TTcpClient如果我们改变就关闭连接和套接字BlockMode,并且没有直接的方法来创建套接字而不是连接它.
要解决这个问题,我们需要在创建套接字之后但在连接之前挂钩.这可以使用DoCreateHandle受保护的方法或OnCreateHandle事件来完成.
最好的方法是从TTcpClient派生一个类并使用DoCreateHandle,但是如果由于任何原因你需要在没有派生类的情况下直接使用TTcpClient,那么代码可以很容易地重写使用OnCreateHandle.
type
TExtendedTcpClient = class(TTcpClient)
private
FIsConnected: boolean;
FNonBlockingModeRequested, FNonBlockingModeSuccess: boolean;
protected
procedure Open; override;
procedure Close; override;
procedure DoCreateHandle; override;
function SetBlockModeWithoutClosing(Block: Boolean): Boolean;
function WaitUntilConnected(Timeout: Integer): Boolean;
public
function ConnectWithTimeout(Timeout: Integer): Boolean;
property IsConnected: boolean read FIsConnected;
end;
procedure TExtendedTcpClient.Open;
begin
try
inherited;
finally
FNonBlockingModeRequested := false;
end;
end;
procedure TExtendedTcpClient.DoCreateHandle;
begin
inherited;
// DoCreateHandle is called after WinSock.socket and before WinSock.connect
if FNonBlockingModeRequested then
FNonBlockingModeSuccess := SetBlockModeWithoutClosing(false);
end;
procedure TExtendedTcpClient.Close;
begin
FIsConnected := false;
inherited;
end;
function TExtendedTcpClient.SetBlockModeWithoutClosing(Block: Boolean): Boolean;
var
nonBlock: Integer;
begin
// TTcpClient.SetBlockMode closes the connection and the socket
nonBlock := Ord(not Block);
Result := ErrorCheck(ioctlsocket(Handle, FIONBIO, nonBlock)) <> SOCKET_ERROR;
end;
function TExtendedTcpClient.WaitUntilConnected(Timeout: Integer): Boolean;
var
writeReady, exceptFlag: Boolean;
begin
// Select waits until connected or timeout
Select(nil, @writeReady, @exceptFlag, Timeout);
Result := writeReady and not exceptFlag;
end;
function TExtendedTcpClient.ConnectWithTimeout(Timeout: Integer): Boolean;
begin
if Connected or FIsConnected then
Result := true
else begin
if BlockMode = bmNonBlocking then begin
if Connect then // will return immediately, tipically with false
Result := true
else
Result := WaitUntilConnected(Timeout);
end
else begin // blocking mode
// switch to non-blocking before trying to do the real connection
FNonBlockingModeRequested := true;
FNonBlockingModeSuccess := false;
try
if Connect then // will return immediately, tipically with false
Result := true
else begin
if not FNonBlockingModeSuccess then
Result := false
else
Result := WaitUntilConnected(Timeout);
end;
finally
if FNonBlockingModeSuccess then begin
// revert back to blocking
if not SetBlockModeWithoutClosing(true) then begin
// undesirable state => abort connection
Close;
Result := false;
end;
end;
end;
end;
end;
FIsConnected := Result;
end;
Run Code Online (Sandbox Code Playgroud)
如何使用:
TcpClient := TExtendedTcpClient.Create(nil);
try
TcpClient.BlockMode := bmBlocking; // can also be bmNonBlocking
TcpClient.RemoteHost := 'www.google.com';
TcpClient.RemotePort := '80';
if TcpClient.ConnectWithTimeout(500) then begin // wait up to 500ms
... your code here ...
end;
finally
TcpClient.Free;
end;
Run Code Online (Sandbox Code Playgroud)
如前所述,Connected对于非阻塞套接字不起作用,所以我添加了一个新IsConnected属性来克服这个问题(仅在连接时才有效ConnectWithTimeout).
双方ConnectWithTimeout并IsConnected会与阻塞和非阻塞套接字工作.
| 归档时间: |
|
| 查看次数: |
8026 次 |
| 最近记录: |