Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.01.16;
Скачать: CL | DM;

Вниз

UDP и множество сетевых интерфейсов   Найти похожие ветки 

 
SkyNet   (2004-10-27 03:35) [0]

Здравствуйте мастера...
хочу задати один каверзный вопросец...
каким образом можно привязать UDP клиента (неважно какой компонент или winsock) к определённому IP адресу локального сетевого интерфейса...если интерфейс не один...если один то к определённому IP этого интерфейса...
заранее благодарен за пояснения...


 
SkyNet   (2004-10-27 03:40) [1]

как извесно существует правило по которому выбирается сеть с более высоким приоритетом, классом А
а у нас сеть класса В
если ктото ставит ADSL либо прописывает дополнительный IP, UDP клиент отсылает текущий IP  сети класса А...
чтото вроде 10.0.0.6 хотя должен 192.168.4.123


 
Verg ©   (2004-10-27 06:59) [2]

bind()


 
Verg ©   (2004-10-27 07:02) [3]


> как извесно существует правило по которому выбирается сеть
> с более высоким приоритетом, классом А
> а у нас сеть класса В
> если ктото ставит ADSL либо прописывает дополнительный IP,
> UDP клиент отсылает текущий IP  сети класса А...
> чтото вроде 10.0.0.6 хотя должен 192.168.4.123


Выбирается IP адрес того интерфейса, через который выбран путь по таблице маршрутизации до адреса назначения пакета.


 
SkyNet   (2004-10-28 00:44) [4]

ага...понял спасиб...большое...


 
SkyNet   (2004-10-28 01:29) [5]

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 wsae<>WSAEWOULDBLOCK
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 fhandle<>SOCKET_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); // !!! &#207;&#206;&#208;&#210; &#207;&#208;&#206;&#209;&#203;&#211;&#216;&#200;&#194;&#192;&#205;&#200;&#223; &#209;&#197;&#208;&#194;&#197;&#208;&#192;!!!
 sin_addr.S_addr:=INADDR_ANY;
 end;
if not errortest(bind(fhandle,adr,sizeof(adr)))
then
 begin
 wsaasyncselect(fhandle,HwndF,WM_SOCKET,FD_READ);
 // !!! &#199;&#196;&#197;&#209;&#220; form1.form.Handle - &#244;&#238;&#240;&#236;&#224;,
 //&#234;&#238;&#242;&#238;&#240;&#238;&#233; &#225;&#243;&#228;&#229;&#242; &#239;&#238;&#241;&#235;&#251;&#224;&#242;&#252;&#241;&#255; WM_SOCKET &#239;&#240;&#232; &#239;&#238;&#235;&#243;&#247;&#229;&#237;&#232;&#232; &#228;&#224;&#237;&#237;&#251;&#245;
 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 01:30) [6]

я так понимаю чтоб его привязать к определённому интерфейсу...нада в методе создания сокета пробиндить чтото с чемто.....а как это хоть примерно делается ???


 
Verg ©   (2004-10-28 07:32) [7]

Точно так же, как ты это делаешь с принимаеющим сокетом
После его создания.


> 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


 
Verg ©   (2004-10-28 08:04) [8]

Кстати,


> procedure CreateSocket;
> begin
> fhandle:=SOCKET_ERROR;  // INVALID_SOCKET ?
> fcHandle:=SOCKET_ERROR; // INVALID_SOCKET ?
> wsastartup($202,wsadata);
> Inited:=True;
> bind(1,,15);
> end


Поясни?


 
SkyNet   (2004-10-28 21:04) [9]

Да это я тут пытался пробиндить сокет :) и забыл стереть ...это просто оставшийся мусор в коде.... :)


 
SkyNet   (2004-10-29 01:45) [10]

дело в том что мне нужно принимать и отправлять широковещательные пакеты... и по заголовку пришедшего пакета определять 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-29 01:47) [11]

но этот код всего-лиш забивает все имеющиеся интерфейсы в динамический массив....
можно ли использовать данные которые я получаю посредством этого кода...если да то как ?


 
Verg ©   (2004-10-29 02:33) [12]


> дело в том что мне нужно принимать и отправлять широковещательные
> пакеты... и по заголовку пришедшего пакета определять 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-30 00:19) [13]

Грамотно изложено... :)
я так и предпологал... бродкаст один для всех интерфейсов...
можно немного уточнить как это реализовывается
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
}


 
SkyNet   (2004-10-30 00:21) [14]

нужно передать датаграмму по бродкасту только на первый интерфейс... 4.123
игнорируя все остальные...


 
Verg ©   (2004-10-30 13:46) [15]


> можно немного уточнить как это реализовывается
> 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 18:08) [16]

а...вот тото и оно что я не понял этого, теперь догоняю...:)
спасибо большое...!
вы мне очень помогли товарисчь Verg.


 
Андрей 2004   (2004-10-30 19:21) [17]

Вы бы не могли подсказать судя по первому коду при создании UDP-сокета порт на каком ему надо работать не обязательно или я не прав?


 
SkyNet   (2004-10-31 00:38) [18]

вообщето порт сервера обязателен....
иначе он не поймает датаграмму посланную ему...
сервер слушает порт на предмет поступившего сигнала...
так что без порта непонятно как он вообще работать может...



Страницы: 1 вся ветка

Текущий архив: 2005.01.16;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.052 c
14-1103922545
PURGEN
2004-12-25 00:09
2005.01.16
JavaScript


1-1103755615
Project1
2004-12-23 01:46
2005.01.16
Задачка. Какой код правильнее и почему ?


1-1104155784
Gear
2004-12-27 16:56
2005.01.16
Delphi 2005 и Compact Framework .NET


3-1103199107
stud
2004-12-16 15:11
2005.01.16
ошибка при попытке выполнения процедуры


3-1102945591
alex145
2004-12-13 16:46
2005.01.16
GUI-Оболочка