Здравствуйте!
Мне потребовалось организовать работу с сетью (чтение и передача данных) с использованием RAW-сокетов. Вызвано это было тем, что при работе требовалось задавать в IP-заголовке тип протокола FF. Как обрабатывать это стандартными indy-компонентами я не смог найти.
Воспользовался двумя примерами, на основе которых сделал модули на прием и передачу и запустил две программы на разных ПК в локальной сети.
Но при работе в произвольный момент времени возникает ошибка, которая "крашит" одну из программ. При этом вторая программа также прекращает свою работу. Я не могу понять как такое возможно - между ними ведь находится несколько устройств, через которые проходит сигнал!
К тому же оказалось, что модуль отвечающий за прием использует процедуры, объявленные в модуле Winsock из поставки среду разработки из библиотеки wsock32.dll. А модуль передачи - те же функции, но объявленные в "самописном" модуле и импортированные из WS2_32.DLL
Попытка заставить их работать через одну и ту же DLL приводит к тому, что либо не идет прием данных через WS2_32.DLL , либо при инициализации передачи в функции SetSockOpt из wsock32.dllпоявляется ошибка "Требуемый адрес для своего контекста неверен".
Отсюда вопросы:
1. Что может вызывать такой синхронный крах двух программ на разных ПК?
2. Можно ли принимать и передавать мой нестандартный протокол какими-либо готовыми компонентами?
3. Почему одни и те же по синтаксису функции из разных DLL работают по разному?
Ну и коды из модулей ниже.
Передача
unit uTxThread;
interface
uses
Windows, Classes, SysUtils, uWinSock, Messages, uCRC32, uGlobal;
type
TTxThread = class(TThread)
public
function InitSocket: Boolean;
procedure DeInitSocket(const ExitCode: Integer);
procedure Fill_IP_HDR(iLocalHost, iRemoteHost: String);
private
Local: String;
Remote: String;
FFrameNum: int64;
Addr_in: sockaddr_in;
Addr_out: sockaddr_in;
IPhdr: TIPHeader;
LogData: String;
EthFrame: array[0..IPFrameSize - 1] of Byte;
MediaFrame: TMediaFrame;
hSocket: TSocket;
function GetData: Boolean;
function SendData: Boolean;
procedure ShowLog;
protected
procedure Execute; override;
end;
implementation
uses
rmMain;
procedure TTxThread.DeInitSocket(const ExitCode: Integer);
begin
if ExitCode <> 0 then
begin
LogData := 'Ошибка: ' + SysErrorMessage(WSAGetLastError);
Synchronize(ShowLog);
end;
if hSocket <> INVALID_SOCKET then closesocket(hSocket);
WSACleanup;
end;
procedure TTxThread.Execute;
var
i: Byte;
begin
inherited;
if InitSocket then begin
isReadyToRcv:=true;
Fill_IP_HDR(LocalHost, RemoteHost);
while not Terminated do begin
if isReadyToRcv then begin
if GetData then begin
SendData;
end;
isReadyToRcv:=false;
end;
end;
end;
DeInitSocket(NO_ERROR);
end;
procedure TTxThread.Fill_IP_HDR(iLocalHost, iRemoteHost: String);
var
dwFromIP : LongWord;
dwToIP : LongWord;
iIPVersion : Word;
iIPSize : Word;
testCRC: Cardinal;
begin
dwFromIP := inet_Addr(PChar(iLocalHost));
dwToIP := inet_Addr(PChar(iRemoteHost));
iIPVersion := 4;
iIPSize := sizeof(ipHdr) div sizeof(LongWord);
FillChar(ipHdr, SizeOf(TIPHeader), 0);
ipHdr.ip_verlen := (iIPVersion shl 4) or iIPSize;
ipHdr.ip_tos := 0;
ipHdr.ip_totallength := htons(IPFrameSize);
ipHdr.ip_id := 1;
ipHdr.ip_offset := 0;
ipHdr.ip_ttl := 128;
ipHdr.ip_protocol := $FF;
ipHdr.ip_checksum := 0 ;
ipHdr.ip_srcaddr := dwFromIP;
ipHdr.ip_destaddr := dwToIP;
FillChar(Addr_out, SizeOf(sockaddr_in), 0);
Addr_out.sin_family := AF_INET;
Addr_out.sin_port := htons(50000);
Addr_out.sin_addr.s_addr := dwToIP;
FillChar(EthFrame, IPFrameSize, $CC);
Move(ipHdr, EthFrame[0], SizeOf(ipHdr));
end;
function TTxThread.GetData: Boolean;
var
FramePoint: PMediaFrame;
begin
Result:=false;
if TxAFrames.Count>0 then begin
FramePoint:=TxAFrames.Extract(TxAFrames.First);
MediaFrame:=FramePoint^;
Dispose(FramePoint);
Result:=true;
Exit;
end;
if TxVFrames.Count>0 then begin
FramePoint:=TxVFrames.Extract(TxVFrames.First);
MediaFrame:=FramePoint^;
Dispose(FramePoint);
Result:=true;
Exit;
end;
end;
function TTxThread.InitSocket: Boolean;
var
WSA: TWSAData;
bOpt: Integer;
begin
Result := WSAStartup(WSA_VER, WSA) = NOERROR;
if not Result then
begin
LogData := 'Ошибка: ' + SysErrorMessage(WSAGetLastError);
Synchronize(ShowLog);
Exit;
end;
hSocket := Socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if hSocket = INVALID_SOCKET then
begin
LogData := 'Ошибка: ' + SysErrorMessage(WSAGetLastError);
Synchronize(ShowLog);
DeInitSocket(WSAGetLastError);
Exit;
end;
bOpt:=1;
if (SetSockOpt(hSocket, IPPROTO_IP, IP_HDRINCL, @bOpt, SizeOf(bOpt)) <> 0) then
begin
LogData := 'Ошибка: ' + SysErrorMessage(WSAGetLastError);
Synchronize(ShowLog);
DeInitSocket(WSAGetLastError);
Exit;
end;
FFrameNum:=0;
Result := True;
end;
function TTxThread.SendData: Boolean;
begin
inc(FFRameNum);
MediaFrame.Num:=FFrameNum;
MediaFrame.CRC:=CRC32FULL(MediaFrame, FrameFullSize - 4);
try
Move(MediaFrame, EthFrame[SizeOf(ipHdr)], FrameFullSize);
if (SendTo(hSocket, ethFrame, IPFrameSize, 0, Addr_out, SizeOf(sockaddr_in)) <> IPFrameSize) then begin
Result:=false;
end
else begin
Result:=true;
end;
except
end;
end;
procedure TTxThread.ShowLog;
begin
fmMain.Memo1.Lines.Add(LogData);
SendMessage(fmMain.Memo1.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;
end.
Прием
unit uRxThread;
interface
uses
Classes, Windows, SysUtils, Messages, WinSock, uGlobal;
const
MAX_PACKET_SIZE = $10000;
SIO_RCVALL = $98000001;
WSA_VER = $202;
MAX_ADAPTER_NAME_LENGTH = 256;
MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
MAX_ADAPTER_ADDRESS_LENGTH = 8;
IPHelper = 'iphlpapi.dll';
type
USHORT = WORD;
ULONG = DWORD;
time_t = Longint;
TIPHeader = packed record
iph_verlen: UCHAR;
iph_tos: UCHAR;
iph_length: USHORT;
iph_id: USHORT;
iph_offset: USHORT;
iph_ttl: UCHAR;
iph_protocol: UCHAR;
iph_xsum: USHORT;
iph_src: ULONG;
iph_dest: ULONG;
end;
PIPHeader = ^TIPHeader;
IP_ADDRESS_STRING = record
S: array [0..15] of Char;
end;
IP_MASK_STRING = IP_ADDRESS_STRING;
PIP_MASK_STRING = ^IP_MASK_STRING;
PIP_ADDR_STRING = ^IP_ADDR_STRING;
IP_ADDR_STRING = record
Next: PIP_ADDR_STRING;
IpAddress: IP_ADDRESS_STRING;
IpMask: IP_MASK_STRING;
Context: DWORD;
end;
PIP_ADAPTER_INFO = ^IP_ADAPTER_INFO;
IP_ADAPTER_INFO = record
Next: PIP_ADAPTER_INFO;
ComboIndex: DWORD;
AdapterName: array [0..MAX_ADAPTER_NAME_LENGTH + 3] of Char;
Description: array [0..MAX_ADAPTER_DESCRIPTION_LENGTH + 3] of Char;
AddressLength: UINT;
Address: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
Index: DWORD;
Type_: UINT;
DhcpEnabled: UINT;
CurrentIpAddress: PIP_ADDR_STRING;
IpAddressList: IP_ADDR_STRING;
GatewayList: IP_ADDR_STRING;
DhcpServer: IP_ADDR_STRING;
HaveWins: BOOL;
PrimaryWinsServer: IP_ADDR_STRING;
SecondaryWinsServer: IP_ADDR_STRING;
LeaseObtained: time_t;
LeaseExpires: time_t;
end;
TRxThread = class(TThread)
private
WSA: TWSAData;
hSocket: TSocket;
Addr_in: sockaddr_in;
Packet: array[0..MAX_PACKET_SIZE - 1] of Byte;
PacketSize: Integer;
LogData: String;
procedure ReceiveAudio;
procedure ReceiveVideo;
protected
MediaFrame: TMediaFrame;
function InitSocket: Boolean; virtual;
procedure DeInitSocket(const ExitCode: Integer); virtual;
procedure Execute; override;
public
Host: String;
OnAir: Boolean;
end;
Const
IPHeaderSize = SizeOf(TIPHeader);
implementation
uses rmMain, uCRC32;
procedure TRxThread.DeInitSocket(const ExitCode: Integer);
begin
if ExitCode <> 0 then
begin
LogData := 'Ошибка Rx: ' + SysErrorMessage(ExitCode);
Synchronize(ShowPacket);
end;
if hSocket <> INVALID_SOCKET then closesocket(hSocket);
WSACleanup;
end;
procedure TRxThread.Execute;
var
IPHeader: TIPHeader;
IPDataLen: Word;
bL: Word;
CRC, CuCRC, hCRC: Cardinal;
begin
if InitSocket then begin
try
while not Terminated do begin
try
PacketSize := recv(hSocket, Packet, MAX_PACKET_SIZE, 0);
except
PacketSize:=0;
end;
OnAir:=True;
if PacketSize > SizeOf(TIPHeader) then begin
Move(Packet[0], IPHeader, IPHeaderSize);
if IPHeader.iph_protocol = $FF then begin
Case PacketSize of
46: begin
isReadyToRcv:=true;
bL:=(Packet[44] shl 8) OR Packet[45];
ModStat.ModBuferLoaded:=bl div 637;
end;
657: begin
CopyMemory(@MediaFrame, @Packet[20], SizeOf(TMediaFrame));
CRC:=MediaFrame.CRC;
if CRC = 0 then begin
end
else begin
CuCRC:=CRC32FULL(MediaFrame, SizeOf(TMediaFrame)-4);
if (CRC <> CuCRC) AND (not isRcvBroken) then begin
end
else begin
Case MediaFrame.PacketType of
mfsTxAData: ReceiveAudio;
mfsRxAData: ReceiveAudio;
mfsTxVStart: ReceiveVideo;
mfsTxVData: ReceiveVideo;
mfsTxVClose: ReceiveVideo;
end;
end;
end;
end;
end;
end;
end;
end;
finally
DeInitSocket(NO_ERROR);
end;
end;
end;
function TRxThread.InitSocket: Boolean;
var
PromiscuousMode: Integer;
begin
Result := WSAStartup(WSA_VER, WSA) = NOERROR;
if not Result then
begin
LogData := 'Ошибка Rx: ' + SysErrorMessage(WSAGetLastError);
Synchronize(ShowPacket);
Exit;
end;
hSocket := socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if hSocket = INVALID_SOCKET then
begin
DeInitSocket(WSAGetLastError);
Exit;
end;
FillChar(Addr_in, SizeOf(sockaddr_in), 0);
Addr_in.sin_family:= AF_INET;
Addr_in.sin_addr.s_addr := inet_addr(PChar(Host));
if bind(hSocket, Addr_in, SizeOf(sockaddr_in)) <> 0 then
begin
DeInitSocket(WSAGetLastError);
Exit;
end;
OnAir:=True;
Result := True;
end;
procedure TRxThread.ReceiveAudio;
var
FramePoint: PMediaFrame;
begin
if (RxAFrames.Count<10) then begin
New(FramePoint);
FramePoint^:=MediaFrame;
try
RxAFrames.Add(FramePoint);
except
Dispose(FramePoint);
FramePoint:=nil;
end;
end;
end;
procedure TRxThread.ReceiveVideo;
var
FramePoint: PMediaFrame;
begin
if (RxVFrames.Count<1000) then begin
New(FramePoint);
FramePoint^:=MediaFrame;
try
RxVFrames.Add(FramePoint);
except
Dispose(FramePoint);
FramePoint:=nil;
end;
end;
end;
procedure TRxThread.ShowPacket;
begin
fmMain.Memo1.Lines.Add(LogData);
end;
end.
И обращение к DLL
unit uWinSock;
interface
uses
Windows;
Const
WinSocket = 'WS2_32.DLL';
IPHelper = 'iphlpapi.dll';
WSA_VER = $202;
Max_Message = 4068;
MAX_ADAPTER_NAME_LENGTH = 256;
MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
MAX_ADAPTER_ADDRESS_LENGTH = 8;
WSADESCRIPTION_LEN = 256;
WSASYS_STATUS_LEN = 128;
AF_INET = 2;
IP_HDRINCL = 2;
SOCK_RAW = 3;
IPPROTO_IP = 0;
IPPROTO_TCP = 6;
IPPROTO_UDP = 17;
IPPROTO_RAW = 255;
SOCKET_ERROR = -1;
Type
TIPHeader = record
ip_verlen : Byte;
ip_tos : Byte;
ip_totallength : Word;
ip_id : Word;
ip_offset : Word;
ip_ttl : Byte;
ip_protocol : Byte;
ip_checksum : Word;
ip_srcaddr : LongWord;
ip_destaddr : LongWord;
end;
u_char = Char;
USHORT = WORD;
u_int = Integer;
ULONG = DWORD;
time_t = Longint;
SunB = packed record
s_b1, s_b2, s_b3, s_b4: u_char;
end;
SunW = packed record
s_w1, s_w2: USHORT;
end;
in_addr = record
case integer of
0: (S_un_b: SunB);
1: (S_un_w: SunW);
2: (S_addr: ULONG);
end;
TInAddr = in_addr;
Sockaddr_in = record
case Integer of
0: (sin_family: USHORT;
sin_port: USHORT;
sin_addr: TInAddr;
sin_zero: array[0..7] of Char);
1: (sa_family: USHORT;
sa_data: array[0..13] of Char)
end;
TSockAddr = Sockaddr_in;
TSocket = u_int;
PWSAData = ^TWSAData;
WSAData = record
wVersion: Word;
wHighVersion: Word;
szDescription: array[0..WSADESCRIPTION_LEN] of Char;
szSystemStatus: array[0..WSASYS_STATUS_LEN] of Char;
iMaxSockets: Word;
iMaxUdpDg: Word;
lpVendorInfo: PChar;
end;
TWSAData = WSAData;
IP_ADDRESS_STRING = record
S: array [0..15] of Char;
end;
IP_MASK_STRING = IP_ADDRESS_STRING;
PIP_MASK_STRING = ^IP_MASK_STRING;
PIP_ADDR_STRING = ^IP_ADDR_STRING;
IP_ADDR_STRING = record
Next: PIP_ADDR_STRING;
IpAddress: IP_ADDRESS_STRING;
IpMask: IP_MASK_STRING;
Context: DWORD;
end;
PIP_ADAPTER_INFO = ^IP_ADAPTER_INFO;
IP_ADAPTER_INFO = record
Next: PIP_ADAPTER_INFO;
ComboIndex: DWORD;
AdapterName: array [0..MAX_ADAPTER_NAME_LENGTH + 3] of Char;
Description: array [0..MAX_ADAPTER_DESCRIPTION_LENGTH + 3] of Char;
AddressLength: UINT;
Address: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
Index: DWORD;
Type_: UINT;
DhcpEnabled: UINT;
CurrentIpAddress: PIP_ADDR_STRING;
IpAddressList: IP_ADDR_STRING;
GatewayList: IP_ADDR_STRING;
DhcpServer: IP_ADDR_STRING;
HaveWins: BOOL;
PrimaryWinsServer: IP_ADDR_STRING;
SecondaryWinsServer: IP_ADDR_STRING;
LeaseObtained: time_t;
LeaseExpires: time_t;
end;
const
INVALID_SOCKET = TSocket(not(0));
function closesocket(s: TSocket): Integer; stdcall;
function socket(af, Struct, protocol: Integer): TSocket; stdcall;
function sendto(s: TSocket; var Buf; len, flags: Integer; var addrto: TSockAddr; tolen: Integer): Integer; stdcall;
function setsockopt(s: TSocket; level, optname: Integer; optval: PChar; optlen: Integer): Integer; stdcall;
function inet_addr(cp: PChar): ULONG; stdcall;
function htons(hostshort: USHORT): USHORT; stdcall;
function WSAGetLastError: Integer; stdcall;
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
function WSACleanup: Integer; stdcall;
function GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD; stdcall; external IPHelper;
implementation
function closesocket; external winsocket name 'closesocket';
function socket; external winsocket name 'socket';
function sendto; external winsocket name 'sendto';
function setsockopt; external winsocket name 'setsockopt';
function inet_addr; external winsocket name 'inet_addr';
function htons; external winsocket name 'htons';
function WSAGetLastError; external winsocket name 'WSAGetLastError';
function WSAStartup; external winsocket name 'WSAStartup';
function WSACleanup; external winsocket name 'WSACleanup';
end.