[置顶] Unity(C#.net)网络通信问题解决(服务器开启失败,Socket下的“由于目标机器积极拒绝,无法连接”异常)

想进行Unity网络通信我们可以有多种办法:

1.使用Unity3d内置的Network方法,采用RPC(远程过程调用)的方式来进行网络编程。
2.使用第三方的网络服务器构件,如smartFox,netDog(c++)等等。
3.Unity3d 支持插件开发。
4.对于小规模的网络IO,可以查看WWW这个对象,用HTTP协议来通信。

我们平时所说的C# .net套接字编程就属于插件开发,可以用.netFrameWork支持的套接字功能来开发。把生成的dll文件,放到Asset文件目录下即可。这些内容我会在之后的博客里讲解。


先给出问题的解决方案:

这种情况很可能是可能是服务器根本没有正常开启没有启动监听

首先对服务器工程进行调试,看看获取 IPAddress的地方是不是出现了异常。

foreach (IPAddress address in Dns.GetHostEntry(ip).AddressList)
{
        try
        {                  
          IPAddress hostIP = address;
          IPEndPoint ipe = new IPEndPoint(address, _port);

          _listener.Bind(ipe);
          _listener.Listen(_maxConnections);
          _listener.BeginAccept(new System.AsyncCallback(ListenTcpClient), _listener);

           break;

           }
          catch (System.Exception)
         {
            return false;//这里一般会抛出一个异常,可以通过打印等操作显示
          }
 }

查看AddressList里面是不是不止一个IP,如果第一个IP是一个16进制的数据。则会出现异常,System.Net.Sockets.SocketException:参考的对象类型不支持尝试操作。这表示AddressList里面有一个IPv6地址,无法使用socket处理。

所以对ip进行筛选代码修改为如下(加一个条件判断筛选)
foreach (IPAddress address in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
{
      if (address.AddressFamily.ToString() == "InterNetwork")
      {
         try
        {                  
          IPAddress hostIP = address;
          IPEndPoint ipe = new IPEndPoint(address, _port);

          _listener.Bind(ipe);
          _listener.Listen(_maxConnections);
          _listener.BeginAccept(new System.AsyncCallback(ListenTcpClient), _listener);

           break;

          }
          catch (System.Exception)
         {
            return false;//这里一般会抛出一个异常,可以通过打印等操作显示
         }
     }
 }
不过这里还要注意一点,这样筛选出的IP不是127.0.0.1,所以在客户端和服务器的监听请求的IP要改为本机的实际IP。


下面我来仔细分析提到的问题与解决思路:

 

㈠起始

由于本人对套接字编程不是很清楚,所以开始免不了参考一些书籍——Unity3D手机游戏开发(金玺曾著 2013出版)。我参考这这本书开始搭建一个套接字的客户端与服务器的环境,按照其给出的代码写完后,发现客户端无法链接服务器(链接失败)。

注意:这种情况我们可以有几种猜测

         1.客户端代码的处理逻辑有问题

         2.服务器没有开启

         3.服务器处理逻辑有问题

然而对于一本书的范例(人家肯定测试过的)来说,其实2的可能性要大一些。这种在别人机器曾经可以运行的代码在自己机器上不行的原因很可能是由于环境配置,框架差异而导致的。

 

㈡寻找问题可能发生的原因

a.那这时候我们最好先对比一下工程的差异,比如由于.netFramework版本的差异会不会导致一些问题?不过一般情况下,这样的问题编译器都会比较智能的给你warning或者error。

b.查看客户端链接失败的相关信息,我调试的时候就发现了Socket下的“由于目标机器积极拒绝,无法连接”异常的提示。于是查找,发现可能是服务器根本没有正常开启

c.查看服务器有没有开启,你的黑框框运行不代表你的服务器帧开启了。运行你的服务器程序后,在windows下Win+R打开cmd命令行,输入netstat  -an查看一下端口的占用情况

这里简单说一下一行里这几项的意思

协议——TCP,UDP,就是我们计算机网络运输层的两大协议了。

本地地址——就是本地上这个某个链接所连接的地址,由于我们看的就是自己机器,所以这里显示的就是自己的地址,冒号后面是这个链接所占用的端口号

(为什么自己的地址有三种?因为0.0.0.0这个地址不真实存在,网络中0.0.0.0IP地址表示整个网络,即网络中的所有主机。它的作用是帮助路由器发送路由表中无法查询的。而127.0.0.1代表本地地址,最后一个地址才是你的机器的实际IP地址)

外部地址——表示这个链接的另一方,比如你打开一个百度的网页,那么百度的地址就是外部地址

状态——tcp协议链接需要进行三次握手,中间每个时候都有一个状态,Listening就表示正在监听时候有链接请求(可以参考Netstat命令详解

 

㈢开始测试

我在这里测试的时候,发现根本找不到我在代码中开启的端口,证明没有任何程序占用了我的端口,而且我的服务器开启也失败了。

所以我开始在我的代码里面进行调试,

public void Start()
{
    _server = new NetTCPServer();
    _server.CreateTcpServer("127.0.0.1", 9999);  //这里开启Tcp服务器,在这里断点

     Console.WriteLine("启动聊天服务器");
}

列出创建服务器代码

// 开始监听
        public bool CreateTcpServer( string ip, int listenPort )
        {
            _port = listenPort;
            _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            foreach (IPAddress address in Dns.GetHostEntry(ip).AddressList)
            {
                    try
                    {
                        IPAddress hostIP = address;
                        IPEndPoint ipe = new IPEndPoint(address, _port);

                        _listener.Bind(ipe);
                        _listener.Listen(_maxConnections);
                        _listener.BeginAccept(new System.AsyncCallback(ListenTcpClient), _listener);

                        break;

                    }
                    catch (System.Exception)
                    {
                        return false;
                    }
                }
            return true;
        }
当我调试到 foreach ( IPAddress address in Dns. GetHostEntry( ip). AddressList) 时发现ip的

就会很神奇的发现,异常出现在了foreach语句中,并传入到adress中。但是异常捕获什么也没说就直接返回false,没有任何提示~所以服务器开启后一点反应都没有。

 

㈣查资料解决问题

找到问题所在后,我们要解决这个异常,看看什么原因。

其实 GetHostEntry( ip). AddressList返回的地址表里面不仅有本地的IPv4地址还有IPv6地址,socket是不支持IPv6的。这个方法只适用于系统没有IPv6地址(非Win7&Vista,win8,win10)的情况。知道这点后,我们对代码稍加改动,筛选一下就可以了。

 foreach (IPAddress address in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
   {
                if (address.AddressFamily.ToString() == "InterNetwork")
                {

不过这里还要注意一点,这样筛选出的IP不是127.0.0.1,所以在客户端和服务器的监听请求的IP要改为本机的实际IP

下面我们再次运行一下,在cmd里面netstat -an查看一下端口占用情况,这回就正常了。

当然例子里的客户端程序也有这个问题,修改一下就好了。

 

我们平时参考别人的例子或者书时,只硬搬是不可行的,很多人写的例子并没有考虑那么多,比如兼容问题,跨平台,API变更等。这也可能是历史问题,毕竟东西都是在变化的。所以,必须要学会调试,查资料,请教别人~

有关Unity的通信的范例,会在之后的博客里更新的。



你可能感兴趣的:(.net,服务器,Unity网络通信,C#socket,端口与IP)