简体   繁体   English

在其他设备上运行时,TcpListener不接受TcpClient [C#,Unity]

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

Server Code (Written in .NET-Core) 服务器代码 (以.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. ServThread是从其他脚本启动的。

Client Code (script in Unity) 客户端代码 (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. 在连接之前,会向播放器显示一个菜单,其中包含IP输入字段和一个“连接”按钮,该按钮可从其他脚本触发OnConnectToServer()并从此脚本触发ConnectEnd。

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) 只有当我构建应用程序并在Android设备上播放该应用程序时,它才会连接(ServerApp不会显示适当的消息)

I already tried disabling Firewall on the laptop, running the server as Admin and testing both apps while connected to a different Network; 我已经尝试在笔记本电脑上禁用防火墙,以Admin身份运行服务器,并在连接到另一个网络时测试这两个应用程序;

Is it something to do with the Unity build process or netowrk settings on my computer? 这与我计算机上的Unity构建过程或netowrk设置有关吗? 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 编辑: LoggerDevice,ItemModel,CommunicationModel是包含在不同脚本中的所有类,并且它们不包含直接连接到服务器的方法

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. 您正在侦听来自此( IPAddress.Parse(GetLocalIPAddress()) )IP地址的连接。 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 : 正如您可以在msdn页面上阅读的:

This constructor allows you to specify the local IP address and port number on which to listen for incoming connection attempts. 该构造函数允许您指定用于侦听传入连接尝试的本地IP地址和端口号。 Before calling this constructor you must first create an IPAddress using the desired local address. 在调用此构造函数之前,必须首先使用所需的本地地址创建一个IPAddress。 Pass this IPAddress to the constructor as the localaddr parameter. 将此IPAddress作为localaddr参数传递给构造函数。 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. 如果您不关心分配了哪个本地地址,请为localaddr参数指定IPAddress.Any,基础服务提供商将分配最合适的网络地址。 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. 如果您不关心使用哪个本地端口,则可以将端口号指定为0。 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. 在这种情况下,服务提供商将分配一个介于1024和5000之间的可用端口号。如果使用此方法,则可以使用LocalEndpoint属性发现已分配了哪些本地网络地址和端口号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM