wojtekk23 wojtekk23 - 1 year ago 65
Android Question

TcpListener doesn't accept TcpClient when run on a different device [C#, Unity]

Server Code (Written in .NET-Core)

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using Models;
using System.Collections.Generic;
using Newtonsoft.Json;
using Logger;
using System.IO;

public class Server
{
private TcpListener listen;
private int port;
private IPAddress localAddress;
private List<ClientModel> clients;
private NetworkStream stream;
private LoggerDevice logger;
private Dictionary<IPAddress, PlayerModel> lockedPlayers;
private string response;
private bool isLuminol;
private bool isFixative;

/// <summary>
/// Server constructor
/// </summary>
/// <param name="log">Object used to create server logs</param>
public Server(LoggerDevice log)
{
port = 8000;
localAddress = IPAddress.Parse(GetLocalIPAddress());
listen = new TcpListener(localAddress, port);
//listen = new TcpListener(IPAddress.Any, port);
IPEndPoint iep = listen.LocalEndpoint as IPEndPoint;
//localAddress = iep.Address;
clients = new List<ClientModel>();

response = string.Empty;
logger = log;

lockedPlayers = new Dictionary<IPAddress, PlayerModel>();

isLuminol = false;
isFixative = false;
}


/// <summary>
/// Returns a local IP Address of the host
/// </summary>
/// <returns>Local IP Address of the host</returns>
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntryAsync(Dns.GetHostName());
Console.WriteLine("DNS HOSTNAME: " + Dns.GetHostName());
foreach (var ip in host.Result.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("Local IP Address Not Found!");
}

/// <summary>
/// Main server thread method
/// Displays current IP Address and connects with clients
/// </summary>
public void ServThread()
{
response = "Adres IP serwera: " + ((IPEndPoint)(listen.LocalEndpoint)).Address.ToString() + '\n';
Console.WriteLine(response);
logger.WriteLine(response);

ClientModel currentClient;

listen.Start();

while (true)
{
Console.WriteLine("Czekam na polaczenie...");
currentClient = new ClientModel();

currentClient.clientSender = listen.AcceptTcpClientAsync().Result;
ThreadPool.QueueUserWorkItem(HandleClient, currentClient);
}
}

/// <summary>
/// Method creating and configuring a new instance of ClientModel
/// Assigns new ClientModel.Id when needed and matches ClientModel with the assigned player if reconnecting
/// </summary>
/// <param name="c">Client to be handled (explicitly converted to ClientModel in the method)</param>
private void HandleClient(object c)
{
ClientModel currentClient = (ClientModel)c;

response = "Polaczono!";
Console.WriteLine(response);
logger.WriteLine(response);

CommunicationModel currentCommunication = new CommunicationModel();
Random rand = new Random();

string currentID;
if (string.IsNullOrEmpty(currentClient.Id))
{
Guid g = Guid.NewGuid();
currentID = g.ToString();

currentClient.Id = g.ToString();

response = "Nadano nowe ID: " + currentClient.Id;
Console.WriteLine(response);
logger.WriteLine(response);
clients.Add(currentClient);
}
currentID = currentClient.Id;

currentCommunication.Id = currentID;

IPEndPoint ipep = (IPEndPoint)currentClient.clientSender.Client.RemoteEndPoint;
IPAddress ipa = ipep.Address;
if (lockedPlayers.ContainsKey(ipa))
{
CommunicationModel mess = new CommunicationModel();
string currentId = currentClient.Id;

response = "Dolaczyl gracz ze zwiazanym playerem (ID: "+currentID+")";
Console.WriteLine(response);
logger.WriteLine(response);

currentClient.clientSender.GetStream();
currentClient.clientSender.GetStream().Flush();

mess.Id = currentId;
mess.Message = JsonConvert.SerializeObject(lockedPlayers[ipa]);

mess.Type = 101;

string comMess = JsonConvert.SerializeObject(mess);

byte[] data = UTF8Encoding.UTF8.GetBytes(comMess);
currentClient.clientSender.GetStream().Write(data, 0, data.Length);

response = "Pomyslnie przypisano gracza z ID: "+currentId+"do jego domyslnego playera";
Console.WriteLine(response);
logger.WriteLine(response);
}

servMessReceived(clients.Find(x => x.Id.Equals(currentID)));
}


public class ItemModel
{
public string Name = "";
public bool Have;
}


ServThread is started from other script.

Client Code (script in Unity)

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Models;
using UnityEngine;
using System.Collections;

class Client : MonoBehaviour
{
TcpClient clientSocket;
CommunicationModel mess;
IPEndPoint end;
PlayerNetworkController player;

NetworkStream clientStream;
int state = 0;
byte[] rec = new byte[100000];
int bytes;

bool reading;

private void Start()
{
//clientSocket = new TcpClient(AddressFamily.InterNetwork);
mess = new CommunicationModel();
player = IOC.Resolve<WorshipGameManager>("WorshipGameManager").playerNetworkController;
bool reading = true;
}

private void Update()
{

}

public void ConnectEnd(string ip)
{
end = new IPEndPoint(IPAddress.Parse(ip), 8000);

clientSocket = new TcpClient(AddressFamily.InterNetwork);
clientSocket.Connect(end.Address, end.Port);

clientStream = clientSocket.GetStream();
StartCoroutine(ReceiveMessage());
}

private IEnumerator ReceiveMessage()
{
//Odczytywanie odpowiedzi
//clientStream = clientSocket.GetStream();
float timePassed = 0.0f;
rec = new byte[100000];
while (true)
{
if (timePassed > 5.0f)
timePassed = 0.0f;
timePassed += Time.deltaTime;

clientStream.BeginRead(rec, 0, rec.Length, new AsyncCallback(this.HandleMessage), this);

yield return new WaitWhile(() => reading == true || timePassed >= 5.0f);
}

}

public void HandleMessage(IAsyncResult ar)
{
reading = true;
bytes = clientStream.EndRead(ar);
clientStream.Flush();
if (bytes > 0)
{
string responseMessage = UTF8Encoding.UTF8.GetString(rec, 0, bytes);
Debug.Log("Message received: " + responseMessage);


CommunicationModel cm = JsonUtility.FromJson<CommunicationModel>(responseMessage);
Debug.Log("cm.Name: " + cm.Name);
Debug.Log("cm.Type: " + cm.Type);
Debug.Log("cm.Id: " + cm.Id);
Debug.Log("cm.Message: " + cm.Message);

if (player == null)
player = IOC.Resolve<WorshipGameManager>("WorshipGameManager").playerNetworkController;

switch (cm.Type)
{
case 100:
player.OnConnectToServer(""); break;
case 101:
player.GetPlayerData(cm); break;
case 111:
player.GetItemFromServer(cm); break;
case 112:
player.SetLuminolActive(cm); break;
case 113:
player.SetItemFromServer(cm); break;
case 114:
player.SetType(cm); break;
}
}
reading = false;
}
public void SendMessageAsync(short type, string message)
{
{
byte[] rec = new byte[100000];

mess.Id = "0";
mess.Type = type;
mess.Message = message;

rec = Encoding.ASCII.GetBytes(JsonUtility.ToJson(mess));

NetworkStream clientStream = clientSocket.GetStream();

clientStream.Write(rec, 0, rec.Length);
clientStream.Flush();

message = Console.ReadLine();
}
}

public void OnServerConnect()
{
Debug.Log("OnServerConnect");
player.OnConnectToServer("");
}
}


This is the script managing all of the connections with the server.

Before connecting, the player is presented a menu with an IP input field and a Connect Button that triggers OnConnectToServer() from other script that triggers ConnectEnd from this script.

When I run the game in the Editor, server connection works fine. It's only when I build the app and play it on my Android device, it doesn't connect (ServerApp doesn't show appropriate message)

I already tried disabling Firewall on the laptop, running the server as Admin and testing both apps while connected to a different Network;

Is it something to do with the Unity build process or netowrk settings on my computer? Or maybe something wrong in my script?

EDIT: LoggerDevice, ItemModel, CommunicationModel are all classes contained in different script and they don't contain methods directly connecting to the Server

Answer Source

Here's your problem :

port = 8000;
localAddress = IPAddress.Parse(GetLocalIPAddress());
listen = new TcpListener(localAddress, port);

You're listening for connections from this ( IPAddress.Parse(GetLocalIPAddress()) ) ip address. Change it to this piece of code :

port = 8000;
localAddress = IPAddress.Any;
listen = new TcpListener(localAddress, port);

And you should be able to connect from any point.


As you can read on msdn page :

This constructor allows you to specify the local IP address and port number on which to listen for incoming connection attempts. Before calling this constructor you must first create an IPAddress using the desired local address. Pass this IPAddress to the constructor as the localaddr parameter. If you do not care which local address is assigned, specify IPAddress.Any for the localaddr parameter, and the underlying service provider will assign the most appropriate network address. This might help simplify your application if you have multiple network interfaces. If you do not care which local port is used, you can specify 0 for the port number. In this case, the service provider will assign an available port number between 1024 and 5000. If you use this approach, you can discover what local network address and port number has been assigned by using the LocalEndpoint property.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download