Unity3d server auto discover, LAN only - client-server

I need to implement a game with a server in a local network, with no Internet access.
Provided that I can handle the connections and network communication between clients and server if they are connected, I'd like to know if there's a elegant way to make clients discover server's address at startup.
I'm providing my approach as an answer, in order to share the knowledge, but it would be nice to see if there is a more elegant/automatic approach.

Clients will use a specific port to connect to the game server. I implemented a UDP multicast in a different port so clients can get server's IP.
The following code is for both server and client, written in Unity Javascript. On the server side, it will start sending multicast messages every second at port 5100. Clients will listen to the same port, until they detect a new message. Then they identify sender's IP and establish a client-server connection the Unity3d way.
private var server_port : int = 5000;
private var server_ip : String;
// multicast
private var startup_port : int = 5100;
private var group_address : IPAddress = IPAddress.Parse ("224.0.0.224");
private var udp_client : UdpClient;
private var remote_end : IPEndPoint;
function Start ()
{
// loaded elsewhere
if (station_id == "GameServer")
StartGameServer ();
else
StartGameClient ();
}
function StartGameServer ()
{
// the Unity3d way to become a server
init_status = Network.InitializeServer (10, server_port, false);
Debug.Log ("status: " + init_status);
StartBroadcast ();
}
function StartGameClient ()
{
// multicast receive setup
remote_end = IPEndPoint (IPAddress.Any, startup_port);
udp_client = UdpClient (remote_end);
udp_client.JoinMulticastGroup (group_address);
// async callback for multicast
udp_client.BeginReceive (new AsyncCallback (ServerLookup), null);
MakeConnection ();
}
function MakeConnection ()
{
// continues after we get server's address
while (!server_ip)
yield;
while (Network.peerType == NetworkPeerType.Disconnected)
{
Debug.Log ("connecting: " + server_ip +":"+ server_port);
// the Unity3d way to connect to a server
var error : NetworkConnectionError;
error = Network.Connect (server_ip, server_port);
Debug.Log ("status: " + error);
yield WaitForSeconds (1);
}
}
/******* broadcast functions *******/
function ServerLookup (ar : IAsyncResult)
{
// receivers package and identifies IP
var receiveBytes = udp_client.EndReceive (ar, remote_end);
server_ip = remote_end.Address.ToString ();
Debug.Log ("Server: " + server_ip);
}
function StartBroadcast ()
{
// multicast send setup
udp_client = UdpClient ();
udp_client.JoinMulticastGroup (group_address);
remote_end = IPEndPoint (group_address, startup_port);
// sends multicast
while (true)
{
var buffer = Encoding.ASCII.GetBytes ("GameServer");
udp_client.Send (buffer, buffer.Length, remote_end);
yield WaitForSeconds (1);
}
}
Attaching this to your GameObject should do the trick.

Here is a c# version
Thanks
using System.Collections;
using UnityEngine;
using System.Net.Sockets;
using System;
using System.Net;
using System.Text;
public class OwnNetworkManager : MonoBehaviour
{
private int server_port = 5000;
private string server_ip;
// multicast
private int startup_port = 5100;
private IPAddress group_address = IPAddress.Parse("127.0.0.1");
private UdpClient udp_client ;
private IPEndPoint remote_end ;
void Start()
{
// loaded elsewhere
if (Loader.IsPC)
StartGameServer();
else
StartGameClient();
}
void StartGameServer()
{
// the Unity3d way to become a server
NetworkConnectionError init_status = Network.InitializeServer(10,
server_port, false);
Debug.Log("status: " + init_status);
StartCoroutine(StartBroadcast());
}
void StartGameClient()
{
// multicast receive setup
remote_end = new IPEndPoint(IPAddress.Any, startup_port);
udp_client = new UdpClient(remote_end);
udp_client.JoinMulticastGroup(group_address);
// async callback for multicast
udp_client.BeginReceive(new AsyncCallback(ServerLookup), null);
StartCoroutine(MakeConnection());
}
IEnumerator MakeConnection()
{
// continues after we get server's address
while (string.IsNullOrEmpty(server_ip))
yield return null;
while (Network.peerType == NetworkPeerType.Disconnected)
{
Debug.Log("connecting: " + server_ip + ":" + server_port);
// the Unity3d way to connect to a server
NetworkConnectionError error ;
error = Network.Connect(server_ip, server_port);
Debug.Log("status: " + error);
yield return new WaitForSeconds (1);
}
}
/******* broadcast functions *******/
void ServerLookup(IAsyncResult ar)
{
// receivers package and identifies IP
var receiveBytes = udp_client.EndReceive(ar, ref remote_end);
server_ip = remote_end.Address.ToString();
Debug.Log("Server: " + server_ip);
}
IEnumerator StartBroadcast()
{
// multicast send setup
udp_client = new UdpClient();
udp_client.JoinMulticastGroup(group_address);
remote_end = new IPEndPoint(group_address, startup_port);
// sends multicast
while (true)
{
var buffer = Encoding.ASCII.GetBytes("GameServer");
udp_client.Send(buffer, buffer.Length, remote_end);
yield return new WaitForSeconds (1);
}
}
}

Related

Microphone Audio Streaming from ESP8266 to C# Socket Server over wifi

I want to stream the audio recorded by microphone on my ESP8266MOD to my C# Socket Program using UDP packets. But I'm getting analog outputs on every second. If I try to convert that signal to .wav file in my C# program it says that: "The wave header is corrupt.". Anyone have solution for this?
This is my Code for C# server:
public class UDPAudioListner
{
private const int listenPort = 12000;
public static int Main()
{
bool done = false;
UdpClient listener = new UdpClient(listenPort);
listener.DontFragment = true;
IPAddress ipAddress = IPAddress.Parse("192.168.1.145");
IPEndPoint groupEP = new IPEndPoint(ipAddress, listenPort);
byte[] receive_byte_array;
List<byte[]> masterByteList = new List<byte[]>();
byte[] bt = new byte[512];
int i = 44;
try
{
while (!done)
{
Console.WriteLine("Waiting for broadcast");
receive_byte_array = listener.Receive(ref groupEP);
Console.WriteLine("Received a broadcast from {0}", groupEP.ToString());
var data = "";
if (receive_byte_array.Length > 0)
{
try
{
using (Stream s = new MemoryStream(receive_byte_array))
{
s.Position = 0;
System.Media.SoundPlayer myPlayer = new System.Media.SoundPlayer(s);
myPlayer.Stream = null;
myPlayer.Stream = s;
myPlayer.Play();
bt = new byte[512];
i = 0;
}
}
catch (Exception ex)
{
done = false;
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
listener.Close();
return 0;
}
}
Here is my Arduino code:
void sendAudio(){
IPAddress ip(255, 255, 255, 122);
unsigned int localPort = 12000;
UdpSA.beginPacket(ip, localPort);
mic = analogRead(A0);
analogWrite(3,mic>> 2);
UdpSA.write(mic);
UdpSA.endPacket();
}
I would not recommend to stream audio via ESP8266.
The maximum rate of reading the analog input is 200 per second.
This means, that you can play the audio at a maximum of 200Hz.
Usual .wav files are need a minimum (I would recommend) of 8000Hz.
Good quality is achievable at 44100Hz.
I tried the same aproach as you did and all i got out of my speakers, were higher and lower noises.

Learning Delegates and Event Handlers, having issue with message not being shown

I am getting a crash course in delegates and event handlers and I have been following a tutorial on the subject and trying to plug in what I have learned into a socket server program I am creating.
I am trying to decouple my server from knowing about the AlertConnectionOpened class here:
namespace AlertConnectionOpened
{
//This is a subscriber class
public class AlertConnectionOpened
{
public void OnConnectionOpened(string message)
{
Console.WriteLine("Connection is opened");
}
}
}
So I am using a delegate in my server class to accomplish this.
namespace Server
{
public class RunServer
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
// Thread signal.
public ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener(int port)
{
}
//This defines the delegate
//Agreement between publisher and subscriber
//object, source of event or class publishing or sending data,
//second param is any additional data we need to send.
public delegate void ConnectionOpenEventHandler(string Message);
//Indicates something has happened and finished.
//Event defined here, based on delegate
public event ConnectionOpenEventHandler ConnectionOpened;
//Raise the Event, need a method to do this.
//Responsible for notifying subscribers
protected virtual void OnConnectionOpened()
{
if (ConnectionOpened != null)
ConnectionOpened("Connection Opened");
}
public void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
int port = 11000;
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
//backlog of how many clients to take in
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
OnConnectionOpened();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
Random rand = new Random();
content = rand.ToString();
// Echo the data back to the client.
Send(handler, content);
}
else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
public void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
public void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
AsynchronousSocketListener a = new AsynchronousSocketListener(0);
a.StartListening();
return 0;
}
}
}
}
Here is also my main program:
namespace AppStart
{
class ServerStart
{
static void Main(string[] args)
{
RunServer.AsynchronousSocketListener svr1 = new RunServer.AsynchronousSocketListener(11000);//publisher
//Correct the port number so a second server can open
RunServer.AsynchronousSocketListener svr2 = new RunServer.AsynchronousSocketListener(350);
svr1.StartListening();//publisher
//This creates the subscriber
var alertConnectionOpened = new AlertConnectionOpened.AlertConnectionOpened();//subsciber
//make publisher register the handler for the event.
svr1.ConnectionOpened += alertConnectionOpened.OnConnectionOpened; //POinter to method
svr2.StartListening();
Console.ReadLine();
}
}
}
From the little I understand of this my call to OnConnectionOpened();, should be showing the message that there is now a connection, but it isn't.

How can I find the IP address of connected network in windows phone?

I am trying to find the IP address of connected network in windows phone. I was successful to find out the IP address of connected Wi-Fi. I have used the following class to find the IP address.
public class MyIPAddress
{
Action<IPAddress> FoundCallback;
UdpAnySourceMulticastClient MulticastSocket;
const int PortNumber = 50000; // pick a number, any number
string MulticastMessage = "FIND-MY-IP-PLEASE" + new Random().Next().ToString();
public void Find(Action<IPAddress> callback)
{
FoundCallback = callback;
MulticastSocket = new UdpAnySourceMulticastClient(IPAddress.Parse("239.255.255.250"), PortNumber);
MulticastSocket.BeginJoinGroup((result) =>
{
try
{
MulticastSocket.EndJoinGroup(result);
GroupJoined(result);
}
catch (Exception ex)
{
// Debug.WriteLine("EndjoinGroup exception {0}", ex.Message);
// This can happen eg when wifi is off
FoundCallback(null);
}
},
null);
}
void callback_send(IAsyncResult result)
{
}
byte[] MulticastData;
bool keepsearching;
void GroupJoined(IAsyncResult result)
{
MulticastData = Encoding.UTF8.GetBytes(MulticastMessage);
keepsearching = true;
MulticastSocket.BeginSendToGroup(MulticastData, 0, MulticastData.Length, callback_send, null);
while (keepsearching)
{
try
{
byte[] buffer = new byte[MulticastData.Length];
MulticastSocket.BeginReceiveFromGroup(buffer, 0, buffer.Length, DoneReceiveFromGroup, buffer);
}
catch (Exception ex)
{
// Debug.WriteLine("Stopped Group read due to " + ex.Message);
keepsearching = false;
}
}
}
void DoneReceiveFromGroup(IAsyncResult result)
{
string str = "";
IPEndPoint where;
int responselength = MulticastSocket.EndReceiveFromGroup(result, out where);
byte[] buffer = result.AsyncState as byte[];
if (responselength == MulticastData.Length && buffer.SequenceEqual(MulticastData))
{
str = where.Address.ToString();
keepsearching = false;
FoundCallback(where.Address);
}
Console.WriteLine(str);
}
}
So by using the above class I can find the IP address of connected Wi-Fi. Now I am try to find the address of network which is connected by Data Connection. In my windows phone I goto Settings --> System --> Cellular and turn on data connection on.
How can I get the IP address of Cellular Network(Data Connection)? Is there any API for that?
you can try this ....
it will work fine for many networks unlike your code which will work only on wifi network due to multicast IP
it will provide you the ip address of the phone ...
public static IPAddress Find()
{
List<string> ipAddresses = new List<string>();
var hostnames = NetworkInformation.GetHostNames();
foreach (var hn in hostnames)
{
if (hn.IPInformation != null)
{
string ipAddress = hn.DisplayName;
ipAddresses.Add(ipAddress);
}
}
IPAddress address = IPAddress.Parse(ipAddresses[0]);
return address;
}

Windows phone a socket operation encountered a dead network

I am trying to get the IP address of networks like Wi-Fi,Data Network. I use the following class to find the IP.
public class MyIPAddress
{
Action<IPAddress> FoundCallback;
UdpAnySourceMulticastClient MulticastSocket;
const int PortNumber = 50000; // pick a number, any number
string MulticastMessage = "FIND-MY-IP-PLEASE" + new Random().Next().ToString();
public void Find(Action<IPAddress> callback)
{
FoundCallback = callback;
MulticastSocket = new UdpAnySourceMulticastClient(IPAddress.Parse("239.255.255.250"), PortNumber);
MulticastSocket.BeginJoinGroup((result) =>
{
try
{
MulticastSocket.EndJoinGroup(result);
GroupJoined(result);
}
catch (Exception ex)
{
// Debug.WriteLine("EndjoinGroup exception {0}", ex.Message);
// This can happen eg when wifi is off
FoundCallback(null);
}
},
null);
}
void callback_send(IAsyncResult result)
{
}
byte[] MulticastData;
bool keepsearching;
void GroupJoined(IAsyncResult result)
{
MulticastData = Encoding.UTF8.GetBytes(MulticastMessage);
keepsearching = true;
MulticastSocket.BeginSendToGroup(MulticastData, 0, MulticastData.Length, callback_send, null);
while (keepsearching)
{
try
{
byte[] buffer = new byte[MulticastData.Length];
MulticastSocket.BeginReceiveFromGroup(buffer, 0, buffer.Length, DoneReceiveFromGroup, buffer);
}
catch (Exception ex)
{
// Debug.WriteLine("Stopped Group read due to " + ex.Message);
keepsearching = false;
}
}
}
void DoneReceiveFromGroup(IAsyncResult result)
{
string str = "";
IPEndPoint where;
int responselength = MulticastSocket.EndReceiveFromGroup(result, out where);
byte[] buffer = result.AsyncState as byte[];
if (responselength == MulticastData.Length && buffer.SequenceEqual(MulticastData))
{
str = where.Address.ToString();
keepsearching = false;
FoundCallback(where.Address);
}
Console.WriteLine(str);
}
}
I was successful to find out the IP address of connected Wi-Fi. I turn off Wi-Fi and turn on the Data Connection. I am not able to get the IP address of connected network. I got the error ** a socket operation encountered a dead network**. I have also refer this question A socket operation encountered a dead network. How can I solve this problem ?
Question is a bit old, but answer may be useful for someone:
You get this error, because your MyIPAddress class can only find a local IP (the address inside your internal WiFi network, behind router). To get an external IP address you should call an external server that will tell you your IP (eg. whatismyip.com).

Fastest way to write to multiple socket connections

I am using the following class to accept incoming connections from client applications - using the send function I want to write the same UTFBytes to each client at the same time - is this possible? or if not, what would be the fastest way to write to them sequentially.
public class ProjectorClients
{
private var _serverSocket:ServerSocket;
private var _clients:Vector.<Socket> = new Vector.<Socket>;
private function ProjectorClients():void
{
_serverSocket = new ServerSocket();
_serverSocket.addEventListener(ServerSocketConnectEvent.CONNECT, onConnect)
_serverSocket.bind(888);
_serverSocket.listen();
}
private function onConnect(e:ServerSocketConnectEvent):void
{
trace("Client is connected");
e.socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
e.socket.addEventListener(Event.CLOSE, onConnectionClosed);
_clients.push(e.socket);
trace("Number of connected clients: " + _clients.length);
}
public function send(command:String):void
{
for each(var clientSocket:Socket in _clients)
{
if (clientSocket.connected)
{
clientSocket.writeUTFBytes(command);
clientSocket.flush();
}
}
}
private function onData(e:ProgressEvent):void
{
trace("data received");
}
private function onConnectionClosed(e:Event):void
{
trace("Client Socket is Closed");
for (var i:int = 0; i < _clients.length; i++)
{
if (_clients[i] == e.currentTarget)
{
_clients.splice(i,1);
break;
}
}
trace("Number of connected clients: " + _clients.length);
}
}
As mentioned by #eSniff, you need a publish subscribe module here. Redis would be a better option as it requires bare minimal steps to set up. The incoming connections will subscribe to the queue and you can publish the data, so that all the client receive it the same time. Please refer to the link below for better understanding.
http://redis.io/topics/pubsub

Resources