Delphi→ Создаем компонент TCP на Winsock (Часть 2)
Окт 8, 2010

В прошлой части мы построили каркас нашего компонента, разобрались со свойствами и написали две процедуры — connect/disconnet. Теперь попробуем передавать и принимать данные.
Для начала приведем полный листинг из первой части:
unit WinTCP; interface uses Winsock; type TWinTCP = class(TObject) private FSocket: TSocket; FWSData: TWSAData; FHostname: PHostEnt; FAddress: TSockAddrIn; procedure FSetHost(AHost: string); procedure FSetPort(APort: integer); public constructor Create; destructor Destroy; override; procedure Connect; procedure Disconnect; property Host: string write FSetHost; property Port: integer write FSetPort; end; implementation { TWinTCP } constructor TWinTCP.Create; begin WSAStartup($202, FWSData); end; destructor TWinTCP.Destroy; begin inherited; WSACleanup; end; procedure TWinTCP.FSetHost(AHost: string); begin FHostname := GetHostByName(PChar(AHost)); FAddress.sin_addr := PInAddr(FHostname^.h_addr^)^; end; procedure TWinTCP.FSetPort(APort: integer); begin FAddress.sin_family := AF_INET; FAddress.sin_port := htons(APort); end; procedure TWinTCP.Connect; begin FSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); Winsock.connect(FSocket, FAddress, SizeOf(FAddress)) end; procedure TWinTCP.Disconnect; begin CloseSocket(FSocket); end; end.
Winsock умеет отправлять и принимать данные через буфер — обычно это динамический массив, каких-либо данных. Добавим функцию передачи SendBuf(var ABuffer; ACount: integer): integer в область паблик. Благодаря записи var ABuffer, функция сможет принимать в качестве аргумента ABuffer любые переменные. ACount количество данных, которые нужно передать — если нужно передать все данные, то в качестве аргумента можно указать SizeOf(ABuffer). Прием данных делаем по той же схеме.
unit WinTCP; interface uses Winsock; type TWinTCP = class(TObject) private FSocket: TSocket; FWSData: TWSAData; FHostname: PHostEnt; FAddress: TSockAddrIn; procedure FSetHost(AHost: string); procedure FSetPort(APort: integer); public constructor Create; destructor Destroy; override; procedure Connect; procedure Disconnect; function SendBuf(var ABuffer; ACount: integer): integer; function RecvBuf(var ABuffer; ACount: integer): integer; property Host: string write FSetHost; property Port: integer write FSetPort; end; implementation { TWinTCP } constructor TWinTCP.Create; begin WSAStartup($202, FWSData); end; destructor TWinTCP.Destroy; begin inherited; WSACleanup; end; procedure TWinTCP.FSetHost(AHost: string); begin FHostname := GetHostByName(PChar(AHost)); FAddress.sin_addr := PInAddr(FHostname^.h_addr^)^; end; procedure TWinTCP.FSetPort(APort: integer); begin FAddress.sin_family := AF_INET; FAddress.sin_port := htons(APort); end; procedure TWinTCP.Connect; begin FSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); Winsock.connect(FSocket, FAddress, SizeOf(FAddress)) end; procedure TWinTCP.Disconnect; begin CloseSocket(FSocket); end; function TWinTCP.SendBuf(var ABuffer; ACount: integer): integer; begin Result := Send(FSocket, ABuffer, ACount, 0); end; function TWinTCP.RecvBuf(var ABuffer; ACount: integer): integer; begin Result := Recv(FSocket, ABuffer, ACount, 0); end; end.
Допишем модифицированные функции передачи, так сказать часто используемые — WriteLn и ReadLn.
function TWinTCP.Writeln(ABuffer: string; ATerminator: string = #$D#$A): integer; begin ABuffer := ABuffer + ATerminator; Result := Send(FSocket, ABuffer[1], ACount, 0); end; function TWinTCP.ReadLn(ATerminator: string = #$D#$A): integer; var ARecvText: string; ARecvChar: Char; begin ARecvText := ''; while (true) do begin Recv(FSocket, ARecvChar, 1, 0); {принимаем 1 символ Char} ARecvText := ARecvText + ARecvChar; {добавляем его к переменной ARecvText} if Pos(ATerminator, ARecvText) > 0 then break; {ищем в ARecvText текст ATerminator} end; Result := ARecvText end; end.
В компонент можно добавить работу с потоками (stream), символами и еще много чего. Описывать все нет смысла — наш компонент почти готов, осталось только добавит в него возможность установки таймаута. Для этого создадим свойство Timeout (integer) и соответствующую ей private процедуру FSetTimeout.
unit WinTCP; interface uses Winsock; type TWinTCP = class(TObject) private FSocket: TSocket; FWSData: TWSAData; FHostname: PHostEnt; FAddress: TSockAddrIn; FTimeout: TTimeVal; procedure FSetHost(AHost: string); procedure FSetPort(APort: integer); procedure FSetTimeout(ATimeout: integer); public constructor Create; destructor Destroy; override; procedure Connect; procedure Disconnect; function SendBuf(var ABuffer; ACount: integer): integer; function RecvBuf(var ABuffer; ACount: integer): integer; function Writeln(ABuffer: string; ATerminator: string = #$D#$A): integer; function ReadLn(ATerminator: string = #$D#$A): integer; property Host: string write FSetHost; property Port: integer write FSetPort; property Timeout: integer write FSetTimeout; end; implementation { TWinTCP } constructor TWinTCP.Create; begin WSAStartup($202, FWSData); end; destructor TWinTCP.Destroy; begin inherited; WSACleanup; end; procedure TWinTCP.FSetHost(AHost: string); begin FHostname := GetHostByName(PChar(AHost)); FAddress.sin_addr := PInAddr(FHostname^.h_addr^)^; end; procedure TWinTCP.FSetPort(APort: integer); begin FAddress.sin_family := AF_INET; FAddress.sin_port := htons(APort); end; procedure TWinTCP.Connect; begin FSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); Winsock.connect(FSocket, FAddress, SizeOf(FAddress)) end; procedure TWinTCP.Disconnect; begin CloseSocket(FSocket); end; function TWinTCP.SendBuf(var ABuffer; ACount: integer): integer; begin Result := Send(FSocket, ABuffer, ACount, 0); end; function TWinTCP.RecvBuf(var ABuffer; ACount: integer): integer; begin Result := Recv(FSocket, ABuffer, ACount, 0); end; function TWinTCP.Writeln(ABuffer: string; ATerminator: string = #$D#$A): integer; begin ABuffer := ABuffer + ATerminator; Result := Send(FSocket, ABuffer[1], ACount, 0); end; function TWinTCP.ReadLn(ATerminator: string = #$D#$A): integer; var ARecvText: string; ARecvChar: Char; begin ARecvText := ''; while (true) do begin Recv(FSocket, ARecvChar, 1, 0); {принимаем 1 символ Char} ARecvText := ARecvText + ARecvChar; {добавляем его к переменной ARecvText} if Pos(ATerminator, ARecvText) > 0 then break; {ищем в ARecvText текст ATerminator} end; Result := ARecvText end; procedure TWinTCP.FSetTimeout(ATimeout: integer); begin FTimeout.tv_usec := 0; FTimeout.tv_sec := ATimeout; end; end.
Свой компонент (WinTCP) я буду постоянно использовать в проектах, добавлять новые функции. После того как он обрастет приличным функционалом, я выложу его в общественный доступ.
Enjoy!
Похожие статьи: