客户端服务器客户端之间的UDP连接

时间:2015-11-14 17:59:45

标签: c# sockets udp connection clients

我正在编写基于UDP打孔的应用程序。我在客户端之间建立连接时遇到问题。在每个客户端向服务器发送内容并使用其IP与服务器响应之后,客户端无法向对方发送任何内容。我错过了什么吗?或者我对UDP打孔的理解是错误的?是的,我是服务器所在的PC的外部IP。

服务器代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.IO;


namespace ConsoleApplication2
{
class Program
{

    static void Main(string[] args)
    {
        IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
        IPEndPoint localEP = new IPEndPoint(IP, 80);
        UdpClient server = new UdpClient();
        server.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        server.ExclusiveAddressUse = false;
        server.Client.Bind(localEP);
        IPEndPoint temp;

        IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 80);

        Console.WriteLine("Dane servera : " + localEP);
        byte[] buffer = server.Receive(ref remoteEP);

        Console.WriteLine("Otrzymano dane od : " + remoteEP + " o treści " + Encoding.ASCII.GetString(buffer));

        temp = remoteEP;

        remoteEP = new IPEndPoint(IPAddress.Any, 80);
        byte[] buffer2 = server.Receive(ref remoteEP);
        Console.WriteLine("Otrzymano dane od : " + remoteEP + " o treści " + Encoding.ASCII.GetString(buffer2));

        byte[] response = Encoding.ASCII.GetBytes(temp.ToString());
        server.Send(response, response.Length, remoteEP);
        byte[] response2 = Encoding.ASCII.GetBytes(remoteEP.ToString());
        server.Send(response2, response2.Length,temp );

    }
}
}

客户1:

namespace ConsoleApplication1
{
class Program
{
    public static IPEndPoint CreateIPEndPoint(string endPoint)
    {
        string[] ep = endPoint.Split(':');
        if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
        IPAddress ip;
        if (ep.Length > 2)
        {
            if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
            {
                throw new FormatException("Invalid ip-adress");
            }
        }
        else
        {
            if (!IPAddress.TryParse(ep[0], out ip))
            {
                throw new FormatException("Invalid ip-adress");
            }
        }
        int port;
        if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
        {
            throw new FormatException("Invalid port");
        }
        return new IPEndPoint(ip, port);
    }
    static void Main(string[] args)
    {
        IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
        IPEndPoint localpt = new IPEndPoint(IP, 80);
        UdpClient client = new UdpClient();
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        client.ExclusiveAddressUse = false;
        string powitanie = "ASUS";
        byte[] buffer = new byte[100];
        buffer = Encoding.ASCII.GetBytes(powitanie);
       // client.Connect(localpt);
        client.Send(buffer, buffer.Length,localpt);

        byte[] otrzymane = client.Receive(ref localpt);
        Console.WriteLine("Odpowiedz servera : " + Encoding.ASCII.GetString(otrzymane));
        Console.Read();
        IPEndPoint TV = CreateIPEndPoint(Encoding.ASCII.GetString(otrzymane));


        byte[] buffer2 = client.Receive(ref TV);
       Console.WriteLine("Odpowiedz klienta : " + Encoding.ASCII.GetString(buffer2));
    }
}
}

客户2:

namespace ConsoleApplication1
{
class Program
{
    public static IPEndPoint CreateIPEndPoint(string endPoint)
    {
        string[] ep = endPoint.Split(':');
        if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
        IPAddress ip;
        if (ep.Length > 2)
        {
            if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
            {
                throw new FormatException("Invalid ip-adress");
            }
        }
        else
        {
            if (!IPAddress.TryParse(ep[0], out ip))
            {
                throw new FormatException("Invalid ip-adress");
            }
        }
        int port;
        if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
        {
            throw new FormatException("Invalid port");
        }
        return new IPEndPoint(ip, port);
    }
    static void Main(string[] args)
    {
        IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
        IPEndPoint localpt = new IPEndPoint(IP, 80);
        UdpClient client = new UdpClient();
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        client.ExclusiveAddressUse = false;
        string powitanie = "Samsung";
        byte[] buffer = new byte[100];
        buffer = Encoding.ASCII.GetBytes(powitanie);
       // client.Connect(localpt);
        client.Send(buffer, buffer.Length,localpt);

        byte[] otrzymane = client.Receive(ref localpt);
        Console.WriteLine("Odpowiedz servera : " + Encoding.ASCII.GetString(otrzymane));
        Console.Read();
        IPEndPoint TV = CreateIPEndPoint(Encoding.ASCII.GetString(otrzymane));

        client.Send(buffer, buffer.Length, TV);

    }
}
}

1 个答案:

答案 0 :(得分:0)

如果无法访问您的确切环境和网络,我不相信能够有信心确保解决问题是可能的。但是这里有一些事情要记住:

  1. 首先,&#34;打孔&#34;不是具有技术规范或行业标准的明确定义的功能。虽然当然许多路由器以允许它的方式工作,但它永远不能保证它能够工作。如果您确定您的代码是正确的,但由于某种原因仍然无法正常工作,那么您可能总是使用一个或多个只是不会使用该技术的路由器。
  2. 根据路由器的行为,客户端可能会或可能不足以从特定端点发送数据报。在原始出站数据报的接收者已经回复之前,路由器可能不会将外部数据报路由到该端点,即实际上已经建立了双向通信。您当前的实现似乎包含允许用户在尝试发送给其他客户端之前测试代码等待两个服务器回复的代码,但请确保您正在利用它。即您不会尝试从一个客户端发送到另一个客户端,直到两个客户端都收到服务器响应。
  3. 除了上述内容之外,服务器可能还不足以将数据报发送到每个客户端。路由器仍可能丢弃从未知端点接收的数据报。因此,通常需要的技术变体是一个客户端尝试将数据报发送到另一个客户端,但也通过服务器通知该客户端它已经这样做了(即发送服务器报告此数据报,然后服务器将数据报发送到预期的收件人客户端以通知它。

    该数据报将被丢弃,但发送客户端的路由器不知道这样,当其他客户端直接回复发送客户端(已经被服务器通知它应该)时,现在原始发送客户端的路由器会将数据报传递给该客户端。它看到了以前用于其他客户端的数据报,因此它将来自该其他客户端的入站数据报视为有效。从那时起,两个客户都应该能够直接发送给对方。当然,实现这一点需要一个更复杂的应用程序协议,而不仅仅是转发IP地址,就像你的例子一样。
  4. 您的代码示例使用SocketOptionName.ReuseAddress,这几乎总是错误的。在我看来,只有你的服务器套接字绑定到一个显式地址,所以使用这个选项可能不会影响测试的结果(即你在任何给定的地址上仍然只有一个套接字,即使你正在测试一台机器)。但是,如果您的测试环境有更多,那么套接字地址确实被重用,这很容易干扰代码的正确操作。
  5. 最后,您在评论中提问(请在问题本身中提供相关信息和问题):&#34;我应该将udp.connect用于服务器,然后使用udp。在客户之间发送&#34; 。答案是&#34; no&#34;。首先,在UDP套接字上使用Connect()只是一种方便; UDP本身是无连接的,因此连接UDP套接字完全在框架内处理。第二,在.NET中,当你连接&#34;一个UDP套接字,该框架将过滤数据报,将它们限制为从&#34; connected&#34;端点。这与穿孔打孔完全相反;即您希望能够从服务器和其他客户端接收数据报。