UDP network stuff

BlitzPlus Forums/BlitzPlus Programming/UDP network stuff

Stoop Solo(Posted 2004) [#1]
UDP network stuff:

I know it is possible to enumerate the number of network adapters and their IP addresses using the CountHostIPs("") and HostIP(n) functions, but when a UDP stream has been created, how does one actually tell the software which adapter to use for outgoing UDP messages?


semar(Posted 2004) [#2]
I guess there's no way to choose the network adapter at real time, from within Blitz standard command set. Perhaps with a windows API call.

The stream UDP creation is based on the card which gives the network functionality - that is, the network card which offers a valid IP address and make the UDP ports available.

I don't know if you can have more than one network card active at the same moment; how could they then share the same ports ?

I think, if you want to route the UDP messaging through a different network card, you have to set it in the windows network settings.

Hope this helps,
Sergio.


MagicalTux(Posted 2004) [#3]
When you create a UDP socket or a TCP server, it is binded to the ip "0.0.0.0". That mean that it will be reachable on all machine IPs.

When you try to send data on a UDP socket, it will be "routed" to the most appropriate interface. Try running "route print" from a MSDOS command line to see routes on a computer.

@semar: Every connected computer have at least two interfaces : network and loopback[127.0.0.1].


Stoop Solo(Posted 2004) [#4]
Cheers semar/magicaltux,

I wasn't after using more than one adapter at a time, I am trying to get my game, when server mode is selected, to display the adapter's IP address, in order that the player can tell the others. I'm a stickler for such details. Of course, if the player knows the host name, then that's easier, as the game can just look up the host table of the specified machine and try each connection. Looks like the best option as far as the IP is concerned is to just start the game server up and list all the host IPs in the server's chat window.

My somewhat vague understanding of TCP/UDP is that it was open to all adapters, but I wasn't sure how sending stuff was managed. So theoretically, the game should just dish UDP out of whatever adapter works.

Cheers guys.


semar(Posted 2004) [#5]
@MagicalTux,
thanks for your info - I've also guessed that; I call the loopback ip address as 'local' ip address.

@Stoop Solo,
I don't know if I've got what you want to achieve, but if you want that you run a server and the other clients should connect to it, then if you use udp you don't have to call your friend and specify your ip address.

Instead, you can send a broadcast message from your server, and make it listening for the client being connected.

A broadcast message is received from any pc with the ip address of the same domain.

So, if you have an address like, for example: 192.168.1.1
to know the broadcast ip is easy; the last triplet is always 255: 192.168.1.255

You have of course to filter out the broadcast sent from the server itself, and listen only the answer from the other clients. You can also retrieve the remote ip address of a client who sends a message, and the same could be done frome each client.

Using UDP you can write a simple but effective game engine for a LAN, where every client does not have to know the server's ip at all, in order to connect to it.

In the code archive there are good examples of udp communication, and one in particular, by Wayne, is very useful:
http://www.blitzbasic.com/codearcs/codearcs.php?code=152
that code is a simple chat, which needs the remote ip address to be specified; however, as wrote above, you can avoid this, and set up a broadcasting engine wich would make the client server connection a breeze.

Hope this has sense for you,
Sergio.


Stoop Solo(Posted 2004) [#6]
Ahhhh, now that broadcasting thing I did not know. I have the whole received IP retrieval thing down, as each client requests to join, if there is an available slot, it notes the client's IP from the incoming message and uses it in-game and for chat broadcast. This really is my first forray into any kind of network stuff. In fact, this is my first Blitz project (not far off completion either).

Currently, the server will send out the game packet only to the individual IPs one at a time. It's not enough to make a significant hit even on a crappy network, but I suppose this broadcast thing might slim things down even more by only having to send the packet once, although it may be better for me to leave the game as it is, so it keeps out of the way of other game servers on the LAN when running. Plus it works, and if it ain't broke etc.

What it does mean though is the pre-game chatroom can broadcast it's presence, so I can stuff a whole game browser front end in. This damnable thing is really starting to balloon now... must... finishhh....

Anyway, cheers for that little bit of info.


xlsior(Posted 2004) [#7]
So, if you have an address like, for example: 192.168.1.1 to know the broadcast ip is easy; the last triplet is always 255: 192.168.1.255


Actually, that is not true: the broadcast address is the last address in your netblock - which may or may not be 255. It is only .255 if you have the entire class C routed and made available to you

The key to determining your broadcast address is your netmask
If your IP is 192.168.1.12 and the netmask is 255.255.255.0 then yes, the broadcast address is 192.168.1.255.

However, your provider / administrator can also assign (much) smaller blocks of IP's to you -- if you do not have your own class C (256 addresses) but get a smaller block of addresses assigned (4, 8, 16, 32, 64 or 128)

You lose 2 usable IP addresses in this block:
- The last one is the broadcast IP that sends to all computers in your own netblock
- One arbitrary address (typically the first, or 2nd-to-last, but could be any depending on your router setup) is the gateway address that routes to your upstream network (often the internet)

I don't remember the exact math, so don't quote me on this, but it goes something like this:
If your IP address is 192.168.1.4, and your netmask is 255.255.255.248, it means you have a block of 8 IP's in your 'network'.

248 in binary = 1111000
Which leaves 8 possible addresses in this subnet. Since your own address ends in .4, the block of 8 it is in encompasses the IP's .0 through .7. That means that the broadcast IP would actually be 192.168.1.7, and not .255
If your own IP had been .12 it means that your range had gone from .8 to .15, and .15 would be the broadcast -- etc.

Netmasks *do* have a purpose, and you can't just count on always being able to broadcast to .255. The .255 IP may be in someone else's network entirely depending on your network layout, and be completely unreachable to you.

These broken up blocks are typical for people on a T1 (etc) frame connection that don't need / pay for an entire class C subnet of addresses, or high-end framed DSL connections where your ISP may give you get a small routable block of IP's.

Most internal company networks and such do lump the entire class C together, but while it is common, you still shouldn't *count* on it.


Stoop Solo(Posted 2004) [#8]
Goddammitall, I was wondering about the bloody netmask. Now I'll have to go and find THAT out from within the program. Oh, and for each host adapter. Oh stuff it, perhaps I should just stick to plan A and join by typing in hostname or IP. I can always put a game browser into version 2.1...


semar(Posted 2004) [#9]
Thanks very much xlsior, you've very interesting info here.

I still don't get the 'behind the scebe math' to calculate the broadcast IP though.

Because of that, I would make a loop from .1 to .255 (I mean the last triplet) and send an UDP message from within the program.

Then, when I get the UDP message, which has an IP address different from the one of the sender, that means that is the broadcast IP.

Perhaps is a bit more complicated than using the proper math; the problem is, how can we get the net mask from within Blitz ?

Sergio.


xlsior(Posted 2004) [#10]
I would think that you can obtain this info though the windows API, but no idea where to even start looking for it...

While it should work, keep in mind that 'scanning' all addresses in your netblock has some downsides:

1) Depending on the frequency of these scans, you are generating significantly more network traffic than when sing just the single broadcast IP

2) when targetting individual computers, you could trigger (software) firewalls that other people in your network block may have running, which may look like intrusion attempts and result in complaints to your administrator / ISP


semar(Posted 2004) [#11]
That's right xlsior, and it's also true that I would make that lan scanning only once, just to determine the broadcast IP address; anyway, I see your point.

The firewall topic I didn't though too, and is indeed another issue to consider...

Oh well, the learning process is an endless curve...

:)

Sergio.


Stoop Solo(Posted 2004) [#12]
Here we go. :D
(Bugger, don't know how to do the forum code example format thingy.)

8<----------------------------------------------------------------------------------------


;========================================
;BROADCAST IP DETERMINIFICATIONALIZOMATIC
;========================================


hadapters=CountHostIPs("")
iptest=HostIP(1) ;find broadcast IP of this adapter.
stream=CreateUDPStream(8123)


UDPTimeouts 5


Repeat
iptest=iptest+1
WriteString stream,"HELLO SAILOR"
SendUDPMsg stream,iptest,8123
recv=RecvUDPMsg(stream)
nmsg$=ReadString$(stream)
Until nmsg$="HELLO SAILOR" Or (255 And iptest)=0

CloseUDPStream stream
If nmsg$="HELLO SAILOR" Then Notify "Broadcast IP: "+DottedIP$(iptest)
If recv=0 Then Notify "Failed to determine broadcast IP. How did that happen I wonder?"


8<----------------------------------------------------------------------------------------

Crude, but effective! It seems to work. Just start off with the adapter's IP, as the broadcast IP has to be larger. Increment the IP by one, send something out, then listen to see if you get it back. If you do, you can assume you've found the broadcast IP. It means blasting a few packets out initially, but once the broadcast IP has been determined, it won't need to.

It'll probably work without that UDP timeout, but the worst case is a 1.2 second scan time, perfectly acceptable for a one-off scan time. The (255 AND IPTEST) makes sure to stop the loop in the event that the final IP digit rolls back to zero (went past 255), just as a failsafe in case something somewhere went awry.

Wahey! No nasty DLL this or DECLS that to hurt my brain! I'll put this aside for v2.1 of the game for it's browser, but no more new stuff for v2.0, or I'll NEVER get it finished...


xlsior(Posted 2004) [#13]
Hmmmm.

Pretty straightforward indeed!One thing though: Sometimes a single PC can be part of multiple 'local' networks, e.g. one network adapter with a 10.0.0.x IP address and one with a 192.168.x.x IP... Any way of determining which adapter/network to broadcast over? :-)


Stoop Solo(Posted 2004) [#14]
Hmmm, well it's easy enough to mod it to look on all adapters. If I run this version on our gateway machine, it finds the LAN connection (hostIP 1), and reports that it cannot find the broadcast IP for the ADSL modem (hostIP 2), and sets it to zero. On another macine (double LANned), it reports both adapter broadcast IPs. Now we have:

hadapters - number of adapters
bcastip(n) - broadcasting IP for adapter (n), set to zero if undetermined.

As for deciding which one to broadcast on, good question. I suppose it could be left to the user to decide, which sort of gives me the mechanism for selecting which adapter(s) will be used to host (though technically, it would be hosting on all of them, the server presence broadcast would only appear on one adapter). Perhaps send out the broadcast data on all the adapters that allow it, and assume that any bcastip(n) set to zero (such as the ADSL modem) ain't for the broadcastin'.


;===========================================
;BROADCAST IP DETERMINIFICATIONALIZOMATIC v2
;===========================================


hadapters=CountHostIPs("")
Dim bcastip(hadapters)
stream=CreateUDPStream(8123)
UDPTimeouts 5



For x=1 To hadapters
	
	bcastip(x)=HostIP(x)

	Repeat

		bcastip(x)=bcastip(x)+1
		WriteString stream,"HELLO SAILOR"
		SendUDPMsg stream,bcastip(x),8123
		recv=RecvUDPMsg(stream)
		nmsg$=ReadString$(stream)

	Until nmsg$="HELLO SAILOR" Or (255 And bcastip(x))=0

	If nmsg$="HELLO SAILOR" Then Notify "NIC ("+x+") Broadcast IP: "+DottedIP$(bcastip(x))
	If recv=0 Then bcastip(x)=0:Notify "Failed to determine NIC("+x+") broadcast IP."

Next

CloseUDPStream stream


Aha! Found the code highlighting thingy.

You're right of course about broadcasting possibly clashing horns with firewalls, but I guess that's firewalls for you. I suppose it should be fairly safe. I guess it would be more tidy to do it via an API call, although if this routine caused upset to firewalls, then the game would likely do the same. I suppose if there was an easy workaround for the firewall issues, it wouldn't really be much of a firewall, would it...

Man, I sure do alot of guessing and supposing.


[edit:] Oh, and in the event of checking for an ip like 192.168.x.x, you could change (255 AND BCASTIP(n)) to (65535 AND BCASTIP(n)). And change the UDPtimeout value. Or go away and medium-boil an egg (worst case) while you wait. It appears to work with UDP timeouts set to zero, and takes about 6 or so seconds to evaluate all 65535 possibilities. Man is it crude though!


xlsior(Posted 2004) [#15]
One additional tip (I'm not trying to be a pain in everyones behind here, just thinking out loud)

Since In *most* cases you can find the broadcast at .255, maybe you should make a small change to *first* try .255, and then do the scan if you get no response?

Hmmmmmm... Come to think of it, If nothing else: rather than counting up to 255, just count down from 255 - in most cases it will be an immediate match. On top of being more efficient, it would also make your code faster since there are less addresses to probe in most cases.

90%+ of the time you won't need to do your scan after all, it is only necesary to catch the exceptions.

you could even go as far as this, and probe the following IP's in order:

1) .255 (256 IP subnet)
2) .127 (128 IP subnet)
3) .63 and .191 (64 IP subnet)
4) .31, .95, .159, .223 (32 IP subnet)
5) .15, .47, .79, .111, .143, .175, .207, .239 (16 IP subnet)
6) .7, etc... (8 IP subnet)

Of course you can replace any of the above with the full scan, but since there aren't -that- many 'odd' broadcast IP's, if you probe the ones above first instead of brute-forcing the entire netblock you will find a match much faster, while keeping a relatively low profile on the network at the same time.

Of course your current example works as well, but you may want to consider the suggestion above as an improvement.

By the way, your multi-NIC code above does seem to work pretty well: it succesfuly determined the broadcast IP's on my real NIC, as well as two virtual (VMWare) ones.


semar(Posted 2004) [#16]
Nice one xlsior.


xlsior(Posted 2004) [#17]
Here's a modification of the code above that counts back fro the .255 address to the current IP, significantly reducing the work in most cases, as well as speeding things up:

;==================================================== 
;BROADCAST IP DETERMINIFICATIONALIZOMATICIFIERTRON v3
;==================================================== 
hadapters=CountHostIPs("") 
Dim bcastip(hadapters) 
stream=CreateUDPStream(8123) 
UDPTimeouts 5
For x=1 To hadapters 
	bcastip(x)=HostIP(x)-(HostIP(x) Mod 256) + 255
	Notify DottedIP$(bcastip(x))
	Repeat 
		WriteString stream,"HELLO SAILOR" 
		SendUDPMsg stream,bcastip(x),8123 
		recv=RecvUDPMsg(stream) 
		nmsg$=ReadString$(stream) 
		bcastip(x)=bcastip(x)-1
	Until nmsg$="HELLO SAILOR" Or bcastip(x)=HostIP(x)
	If nmsg$="HELLO SAILOR" Then Notify "NIC ("+x+") Broadcast IP: "+DottedIP$(bcastip(x)+1) 
	If recv=0 Then bcastip(x)=0:Notify "Failed to determine NIC("+x+") broadcast IP." 
Next 

CloseUDPStream stream 


As a side-note: you can see another significant speed increase by reducing the UDP timeout period, although I am not sure how that would be affected on slow networks adapters... during my tests UDPTimeout of 0 or 1 worked without problems as well, finishing the code above almost instantaneous for all adapters.


Stoop Solo(Posted 2004) [#18]
Good call, xlsior! It makes far more sense to try the most common connections first and count down from 255 to the computer's ip. Although counting up from the computer's IP should mean that the probing remains within the designated netblock, in case probing outside of it sets any ultra-paranoid network admin alarm bells ringing, though this is probably a ridiculous precaution. If it doesn't determine the IP it will likely venture outside the block anyway, especially with a 65535 check. And if one's network was really that paranoid, network games would have a pretty hard time of it. The speed increase and likely reduction in traffic is probably worth the trade-off. I can always put in a feature in the 'advanced' options for the server allowing the user to specify the broadcast IP if so desired. Weird, I tend to do my thinking out loud/on the fly as well... sometimes it makes for some amusing grammar. As well as obscenely excessive use of "guess", "probably" and "suppose".

And yeah, I've run the thing a few times without the UDPtimeouts, and it seems to work fine in every case, and several, several times faster than even the lowest timeout of 1ms. so it's probably better off without it. I guess I'd need an ultra ultra crap LAN to try it on to be sure, and mine's only quite crap.


Wayne(Posted 2004) [#19]
Hi, You can determine the broadcast address by doing the following bitwise operations:
Invert the network mask and OR the results with your IP address.


Stoop Solo(Posted 2004) [#20]
How do you get the netmask in Blitz?


Wayne(Posted 2004) [#21]
Winapi GetIpAddrTable should let you detect all the IP's installed on your computer, including the subnet mask, and broadcast address.


Wayne(Posted 2004) [#22]
Perhaps someone can convert this vb smaple to blitz:

'This project requires the following components:
' - a form (Form1) with a textbox (Text1, Multiline=True)
' and a command button (Command1)
' - a module (Module1)

'in Form1:
Private Sub Command1_Click()
    Module1.Start
End Sub

'In Module1:

'******************************************************************
'Created By Verburgh Peter.
' 07-23-2001
' verburgh.peter@...
'-------------------------------------
'With this small application , you can detect the IP's installed on your computer,
'including subnet mask , BroadcastAddr..
'
'I've wrote this because i've a programm that uses the winsock control, but,
'if you have multiple ip's installed on your pc , you could get by using the Listen
' method the wrong ip ...
'Because Winsock.Localip => detects the default ip installed on your PC ,
' and in most of the cases it could be the LAN (nic) not the WAN (nic)
'So then you have to use the Bind function ,to bind to your right ip..
'but how do you know & find that ip ?
'you can find it now by this appl.. it check's in the api.. IP Table..
'******************************************************************


Const MAX_IP = 5   'To make a buffer... i dont think you have more than 5 ip on your pc..

Type IPINFO
     dwAddr As Long   ' IP address
    dwIndex As Long ' interface index
    dwMask As Long ' subnet mask
    dwBCastAddr As Long ' broadcast address
    dwReasmSize  As Long ' assembly size
    unused1 As Integer ' not currently used
    unused2 As Integer '; not currently used
End Type

Type MIB_IPADDRTABLE
    dEntrys As Long   'number of entries in the table
    mIPInfo(MAX_IP) As IPINFO  'array of IP address entries
End Type

Type IP_Array
    mBuffer As MIB_IPADDRTABLE
    BufferLen As Long
End Type

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public Declare Function GetIpAddrTable Lib "IPHlpApi" (pIPAdrTable As Byte, pdwSize As Long, ByVal Sort As Long) As Long
Sub main()
Form1.Show
End Sub

'converts a Long to a string
Public Function ConvertAddressToString(longAddr As Long) As String
    Dim myByte(3) As Byte
    Dim Cnt As Long
    CopyMemory myByte(0), longAddr, 4
    For Cnt = 0 To 3
        ConvertAddressToString = ConvertAddressToString + CStr(myByte(Cnt)) + "."
    Next Cnt
    ConvertAddressToString = Left$(ConvertAddressToString, Len(ConvertAddressToString) - 1)
End Function

Public Sub Start()
Dim Ret As Long, Tel As Long
Dim bBytes() As Byte
Dim Listing As MIB_IPADDRTABLE

Form1.Text1 = ""

On Error GoTo END1
    GetIpAddrTable ByVal 0&, Ret, True

    If Ret <= 0 Then Exit Sub
    ReDim bBytes(0 To Ret - 1) As Byte
    'retrieve the data
    GetIpAddrTable bBytes(0), Ret, False
      
    'Get the first 4 bytes to get the entry's.. ip installed
    CopyMemory Listing.dEntrys, bBytes(0), 4
    'MsgBox "IP's found : " & Listing.dEntrys => Founded ip installed on your PC..
    Form1.Text1 = Listing.dEntrys & " IP addresses found on your PC !!" & vbCrLf
    Form1.Text1 = Form1.Text1 & "----------------------------------------" & vbCrLf
    For Tel = 0 To Listing.dEntrys - 1
        'Copy whole structure to Listing..
       ' MsgBox bBytes(tel) & "."
        CopyMemory Listing.mIPInfo(Tel), bBytes(4 + (Tel * Len(Listing.mIPInfo(0)))), Len(Listing.mIPInfo(Tel))
         Form1.Text1 = Form1.Text1 & "IP address : " & ConvertAddressToString(Listing.mIPInfo(Tel).dwAddr) & vbCrLf
         Form1.Text1 = Form1.Text1 & "IP Subnetmask : " & ConvertAddressToString(Listing.mIPInfo(Tel).dwMask) & vbCrLf
         Form1.Text1 = Form1.Text1 & "BroadCast IP address : " & ConvertAddressToString(Listing.mIPInfo(Tel).dwBCastAddr) & vbCrLf
         Form1.Text1 = Form1.Text1 & "**************************************" & vbCrLf
    Next

'MsgBox ConvertAddressToString(Listing.mIPInfo(1).dwAddr)
Exit Sub
END1:
MsgBox "ERROR"
End Sub