![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
UDP и множество сетевых интерфейсов [D7, WinXP]
SkyNet © (2004-10-27 03:35:00)Здравствуйте мастера...
хочу задати один каверзный вопросец...
каким образом можно привязать UDP клиента (неважно какой компонент или winsock) к определённому IP адресу локального сетевого интерфейса...если интерфейс не один...если один то к определённому IP этого интерфейса...
заранее благодарен за пояснения...
SkyNet (2004-10-31 00:38:00) [1]вообщето порт сервера обязателен....
иначе он не поймает датаграмму посланную ему...
сервер слушает порт на предмет поступившего сигнала...
так что без порта непонятно как он вообще работать может...
Андрей 2004 (2004-10-30 19:21:00) [2]Вы бы не могли подсказать судя по первому коду при создании UDP-сокета порт на каком ему надо работать не обязательно или я не прав?
SkyNet (2004-10-30 18:08:00) [3]а...вот тото и оно что я не понял этого, теперь догоняю...:)
спасибо большое...!
вы мне очень помогли товарисчь Verg.
Verg (2004-10-30 13:46:00) [4]
> можно немного уточнить как это реализовывается
> I>
> iface_IP[i] or not iface_NETMASK[i].
> ???
> IP=192.168.4.123
> Mask=255.255.255.0
Адрес броадкаста подсети = IP or not Mask.
Я не врубаюсь, чес слово, что тут непонятно?
Может ты не поимаешь, что IPv4 адреса - это просто, 32-х разрядные числа?
Маски подсетей представляются так же....
SkyNet (2004-10-30 00:21:00) [5]нужно передать датаграмму по бродкасту только на первый интерфейс... 4.123
игнорируя все остальные...
SkyNet (2004-10-30 00:19:00) [6]Грамотно изложено... :)
я так и предпологал... бродкаст один для всех интерфейсов...
можно немного уточнить как это реализовывается
I>
iface_IP[i] or not iface_NETMASK[i].
???
вот у меня массив:
type TInterfacesList=record //тип данных для массива интерфейсов системы
IP,//:)
Mask,//Маска
Broadcast:string[15];//Широковещательный адрес
Status:string[20];//Статус интерфейса Up/Down
BroadcastSupp,//Поддерживается ли бродкаст
InterfCycle:boolean;//Цикличиский ли интерфейс...
end;
array ilist of TInterfacesList;
...//тут я его заполнил
...//и получил допустим 3 IP адреса
{
ilist[1]
IP=192.168.4.123
Mask=255.255.255.0
Broadcast=255.255.255.255
ilist[2]
IP=10.0.0.1
Mask=255.0.0.0
Broadcast=255.255.255.255
ilist[3]
IP=10.0.0.2
Mask=255.0.0.0
Broadcast=255.255.255.255
}
Verg (2004-10-29 02:33:00) [7]
> дело в том что мне нужно принимать и отправлять широковещательные
> пакеты... и по заголовку пришедшего пакета определять IP
> удалённого компа..
Скорее всего, все дело в отправке бродкастов. А точнее в выборе сетевым ядром и-фейса отправителя на передающей стороне при формировании бродкастовой датаграммы.
Если у отправителя такой ДГ несколько сетевых (логических) инт-сов, замапленных на один физический (несколько IP адресов на одной сетевой карте), то в сеть отправляется одна (по крайней мере в Win2K) датаграмма с адресом-источника = выбранному по таблице маршрутизации адресом логич. и-фйса (к которому относится запись в маршрутной таблице Dst=255.255.255.255 и запись-то эта всего одна в Win2K сколько бы сетевых карт не стояло). Допустим, что эта датаграмма улетела в сеть с адресом источника таким, который не принадлежит ни одной подсети на и-фейсе приемного хоста (сервера).
Что произойдет. Приемник все равно примет эту датаграмму, т.к. адрес назначения ее = INADDR_BROADCAST. А в адресе источника получается как бы "левак", с точки зрения самого принявшего хоста.
Как быть?
А надо просто грамотно передавать броадкасты, а не преславутым INADDR_BROADCAST.
Эту датаграмму надо посылать через каждый сетевой интерфейс отдельно.
Для этого, в адресе назначения датаграммы (sendto) указывают бродкастовый адрес подсети, который равен
iface_IP[i] or not iface_NETMASK[i]. Где i = номер сет инт-са.
Физически в сети такие датаграммы будут распространятся точно так же, т.е. с MAC адресом ff:ff:ff:ff:ff:ff, но в IP заголовках будут указаны валидные адреса данной подсети. Что позволит приемному хосту отфильтровать их, а когда подходящая будет принята (RecvFrom), то и адрес источника у ней будет из доступной для приемника подсети.
При этом наоборот, передающий сокет ни в коем случае нельзя bind-ить.
SkyNet (2004-10-29 01:47:00) [8]но этот код всего-лиш забивает все имеющиеся интерфейсы в динамический массив....
можно ли использовать данные которые я получаю посредством этого кода...если да то как ?
SkyNet (2004-10-29 01:45:00) [9]дело в том что мне нужно принимать и отправлять широковещательные пакеты... и по заголовку пришедшего пакета определять IP удалённого компа..
если там множество интерфейсов...оно почему-то левый определяет...
я ужо замутил выбор сетевого интерфейса...
вот код...
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, ImgList, Winsock, IdBaseComponent,
IdComponent, IdUDPBase, IdUDPClient, IdUDPServer, IdSocketHandle,
IdAntiFreezeBase, IdAntiFreeze;
type
TForm1 = class(TForm)
Button1: TButton;
ComboBoxEx1: TComboBoxEx;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
ImageList1: TImageList;
Label6: TLabel;
Label7: TLabel;
Edit1: TEdit;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
Label12: TLabel;
Label13: TLabel;
Label14: TLabel;
Button3: TButton;
ListBox1: TListBox;
UDPClient: TIdUDPClient;
UDPServer: TIdUDPServer;
IdAntiFreeze1: TIdAntiFreeze;
Button4: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure ComboBoxEx1Change(Sender: TObject);
procedure UDPServerUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type TInterfacesList=record //тип данных для массива интерфейсов системы
IP,//:)
Mask,//Маска
Broadcast:string[15];//Широковещательный адрес
Status:string[20];//Статус интерфейса Up/Down
BroadcastSupp,//Поддерживается ли бродкаст
InterfCycle:boolean;//Цикличиский ли интерфейс...
end;
function EnumInterfaces:Boolean;
{функция WSAIOCtl импортируется из Winsock 2.0}
function WSAIoctl(
s:TSocket;
cmd:DWORD;
lpInBuffer:PCHAR;
dwInBufferLen:DWORD;
lpOutBuffer:PCHAR;
dwOutBufferLen:DWORD;
lpdwOutBytesReturned:LPDWORD;
lpOverLapped:POINTER;
lpOverLappedRoutine:POINTER):Integer; stdcall; external 'WS2_32.DLL';
var
Form1:TForm1;
InterfacesList:array of TInterfacesList; //список интерфейсов
implementation
{$R *.dfm}
function EnumInterfaces:Boolean;
const
SIO_GET_INTERFACE_LIST=$4004747F;
IFF_UP=$00000001;
IFF_BROADCAST=$00000002;
IFF_LOOPBACK=$00000004;
IFF_POINTTOPOINT=$00000008;
IFF_MULTICAST=$00000010;
type sockaddr_gen=packed record
AddressIn:sockaddr_in;
filler:packed array[0..7]of char;
end;
type INTERFACE_INFO=packed record
iiFlags:u_long;//Флаги интерфейса
iiAddress:sockaddr_gen;//Адрес интерфейса
iiBroadcastAddress:sockaddr_gen;//Broadcast адрес
iiNetmask:sockaddr_gen;//Маска подсети
end;
var
s:TSocket;
wsaD:WSADATA;
NumInterfaces:Integer;
BytesReturned,SetFlags:u_long;
pAddrInet:SOCKADDR_IN;
pAddrString:PCHAR;
PtrA:pointer;
Buffer:array[0..20]of INTERFACE_INFO;
i:Integer;
begin
result:=true;//Инициализируем переменную
WSAStartup($0101,wsaD);//Запускаем WinSock
//Здесь можно дабавить различные обработчики ошибки :)
s:=Socket(AF_INET,SOCK_STREAM,0);//Открываем сокет
if(s=INVALID_SOCKET)
then exit;
try//Вызываем WSAIoCtl
PtrA:=@bytesReturned;
if(WSAIoCtl(s,SIO_GET_INTERFACE_LIST,nil,0,@Buffer,1024,PtrA,nil,nil)<>SOCKET_ERROR)
then
begin//Если OK, то определяем количество существующих интерфейсов
NumInterfaces:=BytesReturned div SizeOf(INTERFACE_INFO);
SetLength(InterfacesList,NumInterfaces);
for i:=0 to NumInterfaces-1
do// Для каждого интерфейса
begin
pAddrInet:=Buffer[i].iiAddress.addressIn;//IP адрес
pAddrString:=inet_ntoa(pAddrInet.sin_addr);
InterfacesList[i].IP:=pAddrString;
pAddrInet:=Buffer[i].iiNetMask.addressIn;//Маска подсети
pAddrString:=inet_ntoa(pAddrInet.sin_addr);
InterfacesList[i].Mask:=pAddrString;
pAddrInet:=Buffer[i].iiBroadCastAddress.addressIn;//Broadcast адрес
pAddrString:=inet_ntoa(pAddrInet.sin_addr);
InterfacesList[i].Broadcast:=pAddrString;
SetFlags:=Buffer[i].iiFlags;
if(SetFlags and IFF_UP)=IFF_UP
then InterfacesList[i].Status:='Interface UP'//Статус интерфейса up/down
else InterfacesList[i].Status:='Interface DOWN';
if(SetFlags and IFF_BROADCAST)=IFF_BROADCAST
then InterfacesList[i].BroadcastSupp:=true//Broadcasts - поддерживает или
else InterfacesList[i].BroadcastSupp:=false;//не поддерживается
if(SetFlags and IFF_LOOPBACK)=IFF_LOOPBACK
then InterfacesList[i].InterfCycle:=true //Циклический или
else InterfacesList[i].InterfCycle:=false;//нормальный
end;
end;
except
end;
//Закрываем сокеты
CloseSocket(s);
WSACleanUp;
result:=false;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i,InCount:integer;
begin
EnumInterfaces;//Заполняем массив InterfacesList данными...
InCount:=length(InterfacesList);//Узнаём количество записей в списке;
if InCount<>0
then for i:=0 to InCount-1//заполняем выпадающий список ComboBoxEx1
do
begin
if InterfacesList[i].InterfCycle //если интерфейс циклический рисуем значёк !
then
begin
ComboBoxEx1.ItemsEx.Add.ImageIndex:=2;
ComboBoxEx1.ItemsEx.Items[i].Caption:=InterfacesList[i].IP;
end
else //если интерфейс не циклический
begin
if InterfacesList[i].Status='Interface UP' //если интерфейс включен
then ComboBoxEx1.ItemsEx.Add.ImageIndex:=0 //рисуем значёк 0
else ComboBoxEx1.ItemsEx.Add.ImageIndex:=1;//или рисуем значёк 1
ComboBoxEx1.ItemsEx.Items[i].Caption:=InterfacesList[i].IP;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
application.Terminate;
end;
procedure TForm1.ComboBoxEx1Change(Sender: TObject);
var
j:integer;
begin //при выборе элемента списка...ищем и выводим данные...
for j:=0 to length(InterfacesList)-1
do if ComboBoxEx1.Text=InterfacesList[j].IP
then
begin
Label9.Caption:=InterfacesList[j].IP;
Label10.Caption:=InterfacesList[j].Mask;
Label11.Caption:=InterfacesList[j].Broadcast;
Label12.Caption:=InterfacesList[j].Status;
if InterfacesList[j].BroadcastSupp
then Label13.Caption:='Да'
else Label13.Caption:='Нет';
if InterfacesList[j].InterfCycle
then Label14.Caption:='Да'
else Label14.Caption:='Нет';
end;
end;
SkyNet (2004-10-28 21:04:00) [10]Да это я тут пытался пробиндить сокет :) и забыл стереть ...это просто оставшийся мусор в коде.... :)
Verg (2004-10-28 08:04:00) [11]Кстати,
> procedure CreateSocket;
> begin
> fhandle:=SOCKET_ERROR; // INVALID_SOCKET ?
> fcHandle:=SOCKET_ERROR; // INVALID_SOCKET ?
> wsastartup($202,wsadata);
> Inited:=True;
> bind(1,,15);
> end
Поясни?
Verg (2004-10-28 07:32:00) [12]Точно так же, как ты это делаешь с принимаеющим сокетом
После его создания.
> with adr
> do
> begin
> sin_family:=AF_INET;
> sin_port:= 0; // Порт задавать не обязательно
> sin_addr.S_addr:=<IP адрес нужного тебе интерфейса>;
> end;
> if not errortest(bind(fchandle,adr,sizeof(adr)))
> then
> begin
.........................................
.............................................
> end;
> end;
IP адреса интерфейсов можно получить при помощи GetIpAddrTable из IPHLPAPI.DLL
SkyNet (2004-10-28 01:30:00) [13]я так понимаю чтоб его привязать к определённому интерфейсу...нада в методе создания сокета пробиндить чтото с чемто.....а как это хоть примерно делается ???
SkyNet (2004-10-28 01:29:00) [14]
unit UDPSock;
interface
uses WinSock,windows,messages,SysUtils,dialogs;
var
wsadata:twsadata;
FHandle,FCHandle:TSocket;
inited:boolean=False;
procedure Listen(lport:integer;const PeerIP:string;HwndF:Hwnd);
procedure CreateSocket; // Èíèöèàëèçàöèÿ
procedure DestroySocket; // Çàêðûâàåì õýíäëû
function receivebuf(var buf:array of char):integer; // Ïðèèÿòü èíôîðìàöèþ
procedure SendPeer2Peer(var data;len:integer);
procedure SendPeer2PeerStr(str:string);
procedure StopServer;
{Ýòà ôóíêöèÿ äîëæíà âûçûâàòüñÿ ïðè ïîëó÷åíèè ôîðìîé ñîîáùåíèÿ WM_SOCKET Íàïðèìåð:
function TForm1.KOLForm1Message(var Msg:tagMSG;var Rslt:Integer):Boolean;
var
len: integer;
buf: array [1..2048] of byte;
begin
Result:=False;
case Msg.message
of
//..........
WM_SOCKET:
begin
len:=ReceiveBuf(buf); // Len - êîë-âî çàïèñàííûõ äàííûõ â áóôåð
end;
end;
end;}
implementation
function ErrorTest(const errcode:integer):boolean;
var
wsae:integer;
begin
result:=(errcode=SOCKET_ERROR)or(errcode=INVALID_SOCKET);
if result
then
begin
wsae:=wsagetlasterror;
if wsaeWSAEWOULDBLOCK
then
begin
//showmessage('Network Error!');
end
else result := false;
end;
end;
procedure Listen(lport:integer;const PeerIP:string;HwndF:Hwnd);
var
adr:tsockaddr;
begin
fchandle:=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if not errortest(fchandle)
then
begin
with adr
do
begin
sin_family:=AF_INET;
sin_port:=htons(lPort);
integer(sin_addr):=inet_addr(pchar(PeerIP));
end;
errortest(integer(adr.sin_addr));
errortest(winsock.connect(fchandle,adr,sizeof(adr)));
end;
//if fhandleSOCKET_ERROR then exit;
fhandle:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if not errortest(fhandle)
then
begin
with adr
do
begin
sin_family:=AF_INET;
sin_port:=htons(LPort); // !!! ÏÎÐÒ ÏÐÎÑËÓØÈÂÀÍÈß ÑÅÐÂÅÐÀ!!!
sin_addr.S_addr:=INADDR_ANY;
end;
if not errortest(bind(fhandle,adr,sizeof(adr)))
then
begin
wsaasyncselect(fhandle,HwndF,WM_SOCKET,FD_READ);
// !!! ÇÄÅÑÜ form1.form.Handle - ôîðìà,
//êîòîðîé áóäåò ïîñëûàòüñÿ WM_SOCKET ïðè ïîëó÷åíèè äàííûõ
end;
end;
end;
function receivebuf(var buf:array of char):integer;
begin
Result:=WinSock.recv(Fhandle,Buf,sizeof(buf),0);
end;
procedure SendPeer2PeerStr(str:string);
begin
SendPeer2Peer(str[1],length(str));
end;
procedure SendPeer2Peer(var data; len:integer);
begin
winsock.send(fchandle,Data,len,0);
end;
procedure CreateSocket;
begin
fhandle:=SOCKET_ERROR;
fcHandle:=SOCKET_ERROR;
wsastartup($202,wsadata);
Inited:=True;
bind(1,,15);
end;
procedure StopServer;
begin
winsock.closesocket(fhandle);
end;
procedure DestroySocket;
begin
if Inited
then
begin
winsock.closesocket(fhandle);
winsock.closesocket(fcHandle);
wsacleanup;
inited:=False;
end;
end;
end.
код который шлёт и принимает пакеты по UDP
SkyNet (2004-10-28 00:44:00) [15]ага...понял спасиб...большое...
Verg (2004-10-27 07:02:00) [16]
> как извесно существует правило по которому выбирается сеть
> с более высоким приоритетом, классом А
> а у нас сеть класса В
> если ктото ставит ADSL либо прописывает дополнительный IP,
> UDP клиент отсылает текущий IP сети класса А...
> чтото вроде 10.0.0.6 хотя должен 192.168.4.123
Выбирается IP адрес того интерфейса, через который выбран путь по таблице маршрутизации до адреса назначения пакета.
Verg (2004-10-27 06:59:00) [17]bind()
SkyNet (2004-10-27 03:40:00) [18]как извесно существует правило по которому выбирается сеть с более высоким приоритетом, классом А
а у нас сеть класса В
если ктото ставит ADSL либо прописывает дополнительный IP, UDP клиент отсылает текущий IP сети класса А...
чтото вроде 10.0.0.6 хотя должен 192.168.4.123