Indy我用的比较少。我一般用的是TTcpClient 和 TTCpServer, 这2个Delphi自带的控件。接收数据 一般是 命令头 + 数据,这样的结构。先接收命令头,判断是否为有效协议,然后才是接收数据,接收数据要注意,最后接收的数据不一定正好是缓冲区的大小。 比如: //Msg.dwLen = 1028; 需要接收数据长度 const BuffLen = 512; var Buffer: array [0..511] of byte; iLeft: Integer; //剩余长度 iTotal: integer; //接收数据总长度 iRcv: integer; //接收数据长度 begin iLeft := 0; iTotal := 0; while iTotal < Msg.dwLen do begin iLeft := Msg.dwLen - iTotal; //如果剩余数据长度小于缓冲区长度,则按剩余数据长度接收。否则按缓冲区长度接收 if iLeft < BufferLen then ilen := Rcv(Buffer, iLeft) else ilen := Rcv(Buffer, BufferLen);
if ilen = -1 then begin closesocket(s); break; end;//如果有错误发生,则退出接收。并关闭socket itotal := itotal + ilen; end; end; VCL 控件 在线程 最好使用Sychonize() 来调用它。
TTcpClient可以工作于非阻塞模式吗?我把它设置为非阻塞后,连接就不成功了?有什么好建议吗?
TTcpClient 可以用于非阻塞模式,不过,得要用异步选择去操作。示例代码如下所示 ://socket模型使用 异步选择uses WinSock;//定义消息 const WM_SOCKET = WM_USER + 2034; //消息处理 procedure OnSocket(var Msg: TMessage); message WM_SOCKET;//sokcet 事件处理 function OnFDRead(const s: TSocket): Boolean; function OnFDClose(const s: TSocket): Boolean; function InitSocket(const s: TSocket): Boolean; function OnRcv(const s: TSocket): Boolea;implementationfunction InitSocket(const s: TSocket): Boolean; begin //监测socket的事件,详见MSDN Result := (0 = WSAAsyncSelect(s, Handle, WM_SOCKET, FD_READ or FD_CLOSE)) ; end; function OnFDClose(const s: TSocket): Boolean; begin Result := closesocket(s) = 0; end;function OnFDRead(const s: TSocket): Boolean; var iRcv: integer; protocol: TProtocol; //协议结构体。 begin //读取数据 Zeromemory(@protocol, sizeof(TProtocol)); iRcv := recv(s, protocol, sizeof(TProtocol), 0); if (-1 = iRcv) and (WSAGetLastError = WSAECONNRESET) then begin onFDClose(s); Exit; end; //如果发生错误,则关闭socket case protocol.dwCommand of .... end; //命令判别 OnRcv(s); end;function OnRcv(const s: TSocket): boolean; const BufLen = 512; var ilen: Integer; iTotal: Integer; Buffer: array [0 .. BufLen] of Byte; iLeft: Integer; //FStream: TMemoryStream; 是存放接收到数据。 begin ilen := 0; iTotal := 0; while iTotal < FMsgLen do begin iLeft := FMsgLen - iTotal; if iLeft > BufLen then iLeft := BufLen; ilen := recv(FSockt, Buffer, BufLen, 0); //WSAEWOULDBLOCK if (SOCKET_ERROR = iLeft) and (WSAGetLastError = WSAECONNRESET) then begin closesocket(FSockt); break; end; //异步时,返回-1,并不一定是socket发生错误。 FStream.WriteBuffer(Buffer, ilen); iTotal := iTotal + ilen; end; Result := iTotal = FMsgLen; begin end;
procedure OnSocket(var Msg: TMessage); var sockt: TSocket; begin sockt := Msg.WParam; if WSAGetAsyncError(Msg.LParam) <> 0 then begin OnFDClose(sockt); Exit; end; //socket是否有错误发生。 case WSAGetSelectEvent(Msg.LParam) of FD_READ: OnFDRead(sockt); FD_CLOSE: OnFDClose(sockt); end; //判断此socket的事件。 以上代码需要写在TForm对象中。
function OnRcv(const s: TSocket): boolean; const BufLen = 512; var ilen: Integer; iTotal: Integer; Buffer: array [0 .. BufLen - 1 ] of Byte;
比如:
//Msg.dwLen = 1028; 需要接收数据长度
const
BuffLen = 512;
var
Buffer: array [0..511] of byte;
iLeft: Integer; //剩余长度
iTotal: integer; //接收数据总长度
iRcv: integer; //接收数据长度
begin
iLeft := 0;
iTotal := 0;
while iTotal < Msg.dwLen do
begin
iLeft := Msg.dwLen - iTotal; //如果剩余数据长度小于缓冲区长度,则按剩余数据长度接收。否则按缓冲区长度接收
if iLeft < BufferLen then
ilen := Rcv(Buffer, iLeft)
else
ilen := Rcv(Buffer, BufferLen);
if ilen = -1 then
begin
closesocket(s);
break;
end;//如果有错误发生,则退出接收。并关闭socket itotal := itotal + ilen;
end;
end;
VCL 控件 在线程 最好使用Sychonize() 来调用它。
const
WM_SOCKET = WM_USER + 2034;
//消息处理
procedure OnSocket(var Msg: TMessage); message WM_SOCKET;//sokcet 事件处理
function OnFDRead(const s: TSocket): Boolean;
function OnFDClose(const s: TSocket): Boolean;
function InitSocket(const s: TSocket): Boolean;
function OnRcv(const s: TSocket): Boolea;implementationfunction InitSocket(const s: TSocket): Boolean;
begin
//监测socket的事件,详见MSDN
Result := (0 = WSAAsyncSelect(s, Handle, WM_SOCKET, FD_READ or FD_CLOSE)) ;
end;
function OnFDClose(const s: TSocket): Boolean;
begin
Result := closesocket(s) = 0;
end;function OnFDRead(const s: TSocket): Boolean;
var
iRcv: integer;
protocol: TProtocol; //协议结构体。
begin
//读取数据
Zeromemory(@protocol, sizeof(TProtocol));
iRcv := recv(s, protocol, sizeof(TProtocol), 0);
if (-1 = iRcv) and (WSAGetLastError = WSAECONNRESET) then
begin
onFDClose(s);
Exit;
end; //如果发生错误,则关闭socket case protocol.dwCommand of
....
end; //命令判别 OnRcv(s);
end;function OnRcv(const s: TSocket): boolean;
const
BufLen = 512;
var
ilen: Integer;
iTotal: Integer;
Buffer: array [0 .. BufLen] of Byte;
iLeft: Integer;
//FStream: TMemoryStream; 是存放接收到数据。
begin
ilen := 0;
iTotal := 0;
while iTotal < FMsgLen do
begin
iLeft := FMsgLen - iTotal;
if iLeft > BufLen then
iLeft := BufLen; ilen := recv(FSockt, Buffer, BufLen, 0); //WSAEWOULDBLOCK if (SOCKET_ERROR = iLeft) and (WSAGetLastError = WSAECONNRESET) then
begin
closesocket(FSockt);
break;
end; //异步时,返回-1,并不一定是socket发生错误。 FStream.WriteBuffer(Buffer, ilen);
iTotal := iTotal + ilen;
end; Result := iTotal = FMsgLen;
begin
end;
procedure OnSocket(var Msg: TMessage);
var
sockt: TSocket;
begin
sockt := Msg.WParam; if WSAGetAsyncError(Msg.LParam) <> 0 then
begin
OnFDClose(sockt);
Exit;
end; //socket是否有错误发生。 case WSAGetSelectEvent(Msg.LParam) of
FD_READ:
OnFDRead(sockt);
FD_CLOSE:
OnFDClose(sockt);
end; //判断此socket的事件。
以上代码需要写在TForm对象中。
function OnRcv(const s: TSocket): boolean;
const
BufLen = 512;
var
ilen: Integer;
iTotal: Integer;
Buffer: array [0 .. BufLen - 1 ] of Byte;