I wonder how I can detect LAN servers.
I made a client/server app and it all works fine but you have to manually enter the IP address of the server.
Currently I'm trying to use a DatagramSocket
on the client/server.
The server will send a packet containing its IP address so the client can connect to that address. The packet is send it to its local broadcast IP (192.168.1.255) port 4444.
The client will listen to port 4444. Then receive the packet.
This works if I'm on the same computer (so the is nothing wrong with my code), but not when I try it on another computer and try to connect to my computer.
I'm not that familiar with networking so I was hoping someone can explain to me how to do this properly.
Also, in AIR broadcasting on 255.255.255.255 is not supported: Note: Sending data to a broadcast address is not supported.
source
To discover other computers in your LAN your server must send UDP broadcasts, which AIR does not support. If your server must be implemented in AIR, you can use a native extension (android/ios) or call a system process via NativeProcess (desktop) that performs the UDP broadcast.
Here are some links that might be useful:
http://lucamezzalira.com/2011/07/29/multicast-udp-socket-in-adobe-air-with-python/
https://github.com/wouterverweirder/AIR-Mobile-UDP-Extension
In order to achieve this I've created my own solution (which isn't perfect, but the only working way I could think of).
On the server side I create a new DatagramSocket that will send a message to each Ip adress in a certain range.
import flash.utils.setInterval;
import flash.utils.clearInterval;
....
private const subnetMask:Array = NetworkUtil.getSubnetMask(NetworkUtil.getPrefixLength());
private const ip:Array = NetworkUtil.getIpAddress().split(".");
private const ipBroadcastSocket:DatagramSocket = new DatagramSocket();
private static var _broadcastIp:Boolean;
private static var intervalId:int;
....
{
....
intervalId = setInterval(broadcastIP, 2000, NetworkUtil.getIpAddress());//broadcast the servers Ip-Address every 2 seconds
}
private function broadcastIP(message:String):void
{
if(_broadcastIp){
try
{
if(subnetMask[1] != 255 || subnetMask[2] != 255){
trace("!!! WARNING: NOT A 'C' CLASS NETWORK !!! (Will not broadcast IP.)");
clearInterval(intervalId);
}
else
{
var data:ByteArray = new ByteArray();
data.writeUTFBytes(message);
for(var i4:int = subnetMask[3]; i4 <= 255; i4++){
var tempIp:String = ip[0] + "." + ip[1] + "." + ip[2] + "." + (subnetMask[3] == 255 ? ip[3] : i4);
if(tempIp != NetworkUtil.getBroadcastIp() && tempIp != NetworkUtil.getSubnetIp(ip, subnetMask)){
serverDatagramSocket.send(data, 0, 0, tempIp, port);
}
}
}
}
catch (error:Error)
{
trace(error.message);
}
}
}
Then on the client side, add another DatagramSocket. Then bind it to the same port used by the server and set it to receive mode.
private var ipBroadcastSocket:DatagramSocket = new DatagramSocket();
ipBroadcastSocket.bind(4444);
ipBroadcastSocket.addEventListener(DatagramSocketDataEvent.DATA, dataReceived);
ipBroadcastSocket.receive();
private function dataReceived(e:DatagramSocketDataEvent):void
{
var data:String = e.data.readUTFBytes(e.data.bytesAvailable);
trace("Server found with IP: "+ data);
}
Here is my network utils class for those that want to implement this:
import flash.net.InterfaceAddress;
import flash.net.NetworkInfo;
import flash.net.NetworkInterface;
public class NetworkUtil
{
private static var address:InterfaceAddress;
{//static constructor
getAddress(); //Get the adress of this host
}
public static function getAddress():void
{
var interfaceVector:Vector.<NetworkInterface> = NetworkInfo.networkInfo.findInterfaces();
for each (var networkInt:NetworkInterface in interfaceVector) {
if (networkInt.active && networkInt.displayName.toLowerCase() != "hamachi") { //Ignore the hamachi interface
for each (var addresss:InterfaceAddress in networkInt.addresses) {
if (addresss.ipVersion == "IPv4") {
if(addresss.address != "127.0.0.1"){
trace(networkInt.displayName); //Output ipAdress for debugging
address = addresss;
}
}
}
}
}
}
public static function getPrefixLength():int
{
return address.prefixLength;
}
public static function getBroadcastIp():String
{
return address.broadcast;
}
public static function getIpAddress():String
{
return address.address;
}
public static function getSubnetIp(currentIp:Array, subnetMask:Array):String
{
for(var i:int; i < 4; i++){
currentIp[i] = (subnetMask[i] == 255 ? currentIp[i] : 0);
}
return currentIp[0] + "." + currentIp[1] + "." + currentIp[2] + "." + currentIp[3];
}
public static function getAmountOfHosts(prefixLength:int):int
{
return (256 << (24-prefixLength)) -2;
}
public static function getSubnetMask(prefixLength:int):Array
{
var subnetMask:Array = [];
for(var i:int = 0; i < 4; i++){
var subnet:uint = 255;
if(prefixLength >= 8){
prefixLength-=8;
}else{
subnet = 255 - (255 >> prefixLength);
prefixLength=0;
}
subnetMask[i] = subnet;
}
return subnetMask;
}
public static function isValidIp(ip:String):Boolean {
ip = ip.replace( /\s/g, ""); //remove spaces for checking
var pattern:RegExp = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
if(pattern.test(ip)){
var octets:Array = ip.split(".");
if (parseInt(String(octets[0])) == 0) {
return false;
}
if (parseInt(String(octets[3])) == 0) {
return false;
}
return true;
}
return false;
}
}
While you can use 192.168.0.255 on Windows Systems you will get an exception on MacOS X. Instead, you can use Class D IP Broadcast on MacOS X ... 224.0.0.1