⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpsock.pas

📁 Monster FTP Client 强大的ftp客户控件,支持Proxy等
💻 PAS
字号:
unit FTPSock;

{Microsoft Windows Socket implementation of Monster FTP Client}
interface

uses Classes, WinTypes, WinProcs, Messages, SysUtils, WinSock;

{$I mftp.inc}

{$ifdef WINSOCK2}
   const SockLibName = 'ws2_32.dll';
{$else}
   const SockLibName = 'wsock32.dll';
{$endif}

type
   sockaddr_in = record
      sin_family: SmallInt;
      sin_port: u_short;
      sin_addr: TInAddr;
      sin_zero: array[0..7] of Char;
   end;
      
   sockaddr = record
      sa_family: u_short;
      sa_data: array[0..13] of Char; { should be 0..13 ?}
   end;
      
   PInteger = ^Integer;
   PSockAddr = ^SockAddr;
   
   function accept(s: TSocket; addr: PSockaddr; addrlen: PInteger): TSocket; stdcall; external SockLibName;
   function bind(s: TSocket; addr: Psockaddr; namelen: Integer): Integer; stdcall; external SockLibName;
   function closesocket(s: TSocket): Integer; stdcall; external SockLibName;
   function connect(s: TSocket; name: Psockaddr; namelen: Integer): Integer; stdcall; external SockLibName;
   function gethostname(name: PChar; len: Integer): Integer; stdcall; external SockLibName;
   function getsocketname(s: TSocket; name: Psockaddr; namelen: PInteger): Integer; stdcall; external SockLibName name 'getsockname';
   function htons(hostshort: u_short): u_short; stdcall; external SockLibName;
   function inet_addr(cp: PChar): u_long; stdcall; external SockLibName;
   function inet_ntoa(inaddr: TInAddr): PChar; stdcall; external SockLibName;
   function listen(s: TSocket; backlog: Integer): Integer; stdcall; external SockLibName;
   function ntohs(netshort: u_short): u_short; stdcall; external SockLibName;
   function recv(s: TSocket; buf: PChar; len, flags: Integer): Integer; stdcall; external SockLibName;
   function send(s: TSocket; buf: PChar; len, flags: Integer): Integer; stdcall; external SockLibName;
   function socket(af, tp, protocol: Integer): TSocket; stdcall; external SockLibName;
   function WSAAsyncGetHostByName(HWindow: HWND; wMsg: u_int; name, buf: PChar; buflen: Integer): THandle; stdcall; external SockLibName;
   function WSAAsyncSelect(s: TSocket; HWindow: HWND; wMsg: u_int; lEvent: longint): Integer; stdcall; external SockLibName;
   function WSACancelAsyncRequest(hAsyncTaskHandle: THandle): Integer; stdcall; external SockLibName;
   function WSACleanup: Integer; stdcall; external SockLibName;
   function WSAGetLastError: Integer; stdcall; external SockLibName;
   function WSAStartup(wVersionRequired: Word; var lpWSData: TWSAData): Integer; stdcall; external SockLibName;

const
   WM_ARPDONE = WM_USER;
   WM_SOCKMSG = WM_USER + 1;

type TSSWndMethod = procedure(var Message: TMessage) of object;
   
type TMSocket = class(TComponent)
   private
      FHandle: HWND;
      FSocket: TSocket;
      FAddr: sockaddr_in;
      FConnected: boolean;
      FBytesSent: Integer;
      FDescription: String;
      FSystemStatus: String;
      FMaxSockets: Integer;
      FCustomMessage: TSSWndMethod;
      MyWSAData: TWSAData;
      FLookupNameDone, FOnConnected, FOnDisconnected, FTimeoutEvt: TNotifyEvent;
      FOnReadReady, FOnWriteReady, FOnAccept: TNotifyEvent;
      THostEntryBuf: array[1..MAXGETHOSTSTRUCT] of Byte;
      sa: TInAddr;
      ArpHandle: THandle;
      FConnTO, FArpTO, FTransTO: LongInt;
      Timer: LongInt;
      TimerID: LongInt;
      dnsbuf: array[1..64] of Char;
      procedure SockWndProc(var Message: TMessage);
   protected
      FVersion: String;
      procedure LookupNameDone; virtual;
      procedure Connected;
      procedure Disconnected;
   public
      Address, Host, FServer: String;
      FPort: u_short;
      LastError: Word;
      WantBlockingErrors: Boolean;
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;
      property Version: String read FVersion;

      procedure CreateTCPSocket;
      procedure ReCreateTCPSocket;
      procedure LookupName(host: String);
      procedure FillName;
      procedure FillAddress(address: String);
      procedure FillPort(port: Word);
      procedure Connect;
      procedure Disconnect;
      procedure Listen;
      procedure Accept(ListeningSocket: TMSocket);
      function GetAddressString: String;
      function GetLocalHost: String;
      function GetLocalAddress: String;
      function GetLocalPort: u_short;
      function SendBuf(buf: PChar; cnt: Integer): Integer;
      function SendBufOOB(buf: PChar; cnt: Integer): Integer;
      function RecvBuf(buf: PChar; cnt: Integer): Integer;
      procedure SetServer(s: String);
      procedure SetTimeout(seconds: LongInt);

      property Description: String read FDescription;
      property SystemStatus: String read FSystemStatus;
      property MaxSockets: Integer read FMaxSockets;
      property Handle: HWND read FHandle;
      property CustomMessage: TSSWndMethod read FCustomMessage write FCustomMessage;
      property IsConnected: Boolean read FConnected;
      property Socket: TSocket read FSocket;
      property OnLookupNameDone: TNotifyEvent read FLookupNameDone write FLookupNameDone;
      property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
      property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected;
      property OnReadReady: TNotifyEvent read FOnReadReady write FOnReadReady;
      property OnWriteReady: TNotifyEvent read FOnWriteReady write FOnWriteReady;
      property OnTimeOut: TNotifyEvent read FTimeOutEvt write FTimeOutEvt;
      property OnAccept: TNotifyEvent read FOnAccept write FOnAccept;
      property TimeOutConnect: LongInt read FConnTO write FConnTO;
      property TimeOutArp: LongInt read FArpTO write FArpTo;
      property TimeOutTransaction: LongInt read FTransTO write FTransTO;

      property Server: String read FServer write SetServer;
      property Port: u_short read FPort write FPort;
   published
   end;
      
var
   S_un: TInAddr;

implementation

uses Forms;

constructor TMSocket.Create;
begin
   inherited Create(AOwner);

   FAddr.sin_family := PF_INET;
   FAddr.sin_addr.s_addr := INADDR_ANY;
   FAddr.sin_port := 0;
   FHandle := AllocateHWND(SockWndProc);
   FSocket := INVALID_SOCKET;
   FConnected := False;
   FBytesSent := 0;
   FConnTO := 30;
   FArpTO := 15;

   if WSAStartup($0002, myWSAData) = 0 then
   begin
       with myWSAData do
       begin
          FDescription := StrPas(szDescription);
          FSystemStatus := StrPas(szSystemStatus);
          FMaxSockets := iMaxSockets;
       end;
   end;

   WantBlockingErrors := False;
end;

destructor TMSocket.Destroy;
begin
   DeallocateHwnd(FHandle);
   WSACleanUp;

   inherited Destroy;
end;

procedure TMSocket.SockWndProc;
var phe: PHostEnt;
    evt: Word;
begin
   if (Message.Msg > WM_SOCKMSG) and Assigned(FCustomMessage) then
   begin
      FCustomMessage(Message);
      Exit;
   end;
   case Message.Msg of
      {custom messages}
      WM_ARPDONE: {received after WSAAsyncGetHostByName}
      begin
         SetTimeout(0);
         LastError := HIWORD(Message.lParam);
         if LastError = 0 then
         begin
            phe := PHostEnt(@THostEntryBuf);
            with sa, phe^ do
            begin
               S_un_b.s_b1 := h_addr^[0];
               S_un_b.s_b2 := h_addr^[1];
               S_un_b.s_b3 := h_addr^[2];
               S_un_b.s_b4 := h_addr^[3];
            end;
         end;
         ArpHandle := 0;
         LookupNameDone;
      end;
      WM_SOCKMSG:  {received after connect, read, write, disconnect notification}
      begin
         evt := LOWORD(Message.lParam);
         LastError := HIWORD(Message.lParam);
         case evt of
            FD_CONNECT:
            begin
               FConnected := (LastError = 0);
               if FConnected then Connected;
            end;
            FD_CLOSE:
            begin
               if FConnected then closesocket(FSocket);
               FConnected := False;
               FSocket := INVALID_SOCKET;
               Disconnected;
            end;
            FD_READ:
            begin
               if Assigned(FOnReadReady) then FOnReadReady(Self);
               SetTimeOut(0);
            end;
            FD_WRITE: if Assigned(FOnWriteReady) then FOnWriteReady(Self);
            FD_ACCEPT: if Assigned(FOnAccept) then FOnAccept(Self);
         end;
      end;
      {end custom messages}
      WM_TIMER:
      begin
         Dec(Timer);
         if Timer = 0 then
         begin
            if Assigned(FTimeoutEvt) then
               FTimeoutEvt(Self)
            else
               Disconnect;
         end;
      end;
      WM_QUERYENDSESSION: Message.Result := 1; {end session bug}
      else DefWindowProc(FHandle, Message.Msg, Message.wParam, Message.lParam);
   end;
end;

procedure TMSocket.SetTimeout;
begin
   if TimerID <> 0 then KillTimer(FHandle, TimerID);
   if seconds <= 0 then
      TimerId := 0
   else
   begin
      Timer := seconds;
      TimerID := SetTimer(FHandle, 1, 1000, nil);
   end;
end;

procedure TMSocket.CreateTCPSocket;
begin
   if FSocket <> INVALID_SOCKET then Exit;
   FSocket := FTPSock.socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
   LastError := WSAGetLastError
end;

procedure TMSocket.ReCreateTCPSocket;
begin
   CloseSocket(FSocket);
   FSocket := INVALID_SOCKET;
   CreateTCPSocket;
end;

function TMSocket.GetAddressString;
begin
   Result := StrPas(inet_ntoa(FAddr.sin_addr));
end;

procedure TMSocket.LookupName;
begin
   if ArpHandle <> 0 then Exit;
   StrPCopy(@dnsbuf, host);
   ArpHandle := WSAAsyncGetHostByName(FHandle, WM_ARPDONE, @dnsbuf, @THostEntryBuf, MAXGETHOSTSTRUCT);
   LastError := WSAGetLastError;
   if LastError = 0 then SetTimeout(FArpTo);
end;

procedure TMSocket.FillName;
begin
   FAddr.sin_addr := sa;
end;

procedure TMSocket.FillAddress;
var
   s: array [1..32] of Char;
begin
   StrPCopy(@s, address);
   FAddr.sin_addr.s_addr := inet_addr(@s);
end;

procedure TMSocket.FillPort;
begin
   FAddr.sin_port := htons(port);
end;

procedure TMSocket.LookupNameDone;
begin
   if Assigned(FLookupNameDone) then FLookupNameDone(Self);
end;

procedure TMSocket.Connected;
begin
   if Assigned(FOnConnected) then FOnConnected(Self);
end;

procedure TMSocket.Disconnected;
begin
   if Assigned(FOnDisconnected) then FOnDisconnected(Self);
end;

procedure TMSocket.Connect;
begin
   WSAAsyncSelect(FSocket, FHandle, WM_SOCKMSG, FD_CONNECT or FD_CLOSE or FD_READ or FD_WRITE);
   if FTPSock.connect(FSocket, Psockaddr(@FAddr), SizeOf(FAddr)) <> 0 then
   begin
      LastError := WSAGetLastError;
      if not WantBlockingErrors then
         if LastError = WSAEWOULDBLOCK then LastError := 0;
   end;
   if LastError = 0 then SetTimeout(FConnTO);
end;

procedure TMSocket.Listen;
begin
   bind(FSocket, Psockaddr(@FAddr), SizeOf(FAddr));
   LastError := WSAGetLastError;

   if LastError = 0 then
   begin
      WSAAsyncSelect(FSocket, FHandle, WM_SOCKMSG, FD_ACCEPT);
      FTPSock.listen(FSocket, 2);
      LastError := WSAGetLastError;
   end;
end;

procedure TMSocket.Accept;
var
   nl: Integer;
begin
   nl := sizeof(sockaddr_in);
   FSocket := FTPSock.accept(ListeningSocket.Socket, PSockaddr(@FAddr), @nl);
   LastError := WSAGetLastError;

   if LastError = 0 then
   begin
      FConnected := True;
      WSAAsyncSelect(FSocket, FHandle, WM_SOCKMSG, FD_CLOSE or FD_READ or FD_WRITE);
   end;
end;

procedure TMSocket.Disconnect;
begin
   if ArpHandle <> 0 then WSACancelAsyncRequest(ArpHandle);
   SetTimeout(0);
   if FSocket <> INVALID_SOCKET then
   begin
      WSAAsyncSelect(FSocket, FHandle, WM_SOCKMSG, FD_CLOSE or FD_READ or FD_WRITE);
      CloseSocket(FSocket);
      LastError := WSAGetLastError;
      FSocket := INVALID_SOCKET;
      FConnected := False;
      Disconnected;
   end;
end;

function TMSocket.SendBuf;
var
   n: Integer;
begin
   Result := 0;
   n := send(FSocket, buf, cnt, 0);
   if n > 0 then
   begin
      Result := n;
      LastError := 0;
   end
   else if (n = SOCKET_ERROR) then
   begin
      LastError := WSAGetLastError;
      if not WantBlockingErrors then
         if LastError = WSAEWOULDBLOCK then LastError := 0;
   end;
end;

function TMSocket.SendBufOOB;
var
   n: Integer;
begin
   Result := 0;
   n := send(FSocket, buf, cnt, MSG_OOB);
   if n > 0 then
   begin
      Result := n;
      LastError := 0;
   end
   else if (n = SOCKET_ERROR) then
   begin
      LastError := WSAGetLastError;
      if not WantBlockingErrors then
         if LastError = WSAEWOULDBLOCK then LastError := 0;
   end;
end;

function TMSocket.RecvBuf;
var
   n: Integer;
begin
   Result := 0;
   n := recv(FSocket, buf, cnt, 0);
   if n > 0 then
   begin
      Result := n;
      LastError := 0;
   end
   else if (n = SOCKET_ERROR) then
   begin
      LastError := WSAGetLastError;
      if not WantBlockingErrors then
         if LastError = WSAEWOULDBLOCK then LastError := 0;
   end;
end;

function TMSocket.GetLocalHost;
var
   sh: array [0..255] of Char;
begin
   if gethostname(sh, 255) = 0 then Result := StrPas(sh)
   else Result := '';
   LastError := WSAGetLastError;
end;

function TMSocket.GetLocalAddress: String;
var
   sa: sockaddr_in;
   nl: Integer;
begin
   Result := '';
   nl := SizeOf(sa);
   if FSocket = INVALID_SOCKET then exit;
   if geTSocketname(FSocket, PSockaddr(@sa), @nl) = 0 then Result := StrPas(inet_ntoa(sa.sin_addr));
   LastError := WSAGetLastError;
end;

function TMSocket.GetLocalPort;
var
   sa: sockaddr_in;
   nl: Integer;
begin
   Result := 0;
   nl := Sizeof(sa);
   if FSocket = INVALID_SOCKET then exit;
   if geTSocketname(FSocket, PSockaddr(@sa), @nl) = 0 then Result := ntohs(sa.sin_port);
   LastError := WSAGetLastError;
end;

procedure TMSocket.SetServer;
begin
   FServer := Trim(S);
   if FServer <> '' then
   begin
      if (FServer[1] >= '0') and (FServer[1] <= '9') then
      begin
         Address := FServer;
         Host := '';
      end
      else
      begin
         Host := FServer;
         Address := '';
      end;
   end;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -