2012-02-04 57 views
18

我正在尝试完成UDP打孔。我基于我的理论this article和这WIKI page,但我面临一些问题与它的C#编码。这是我的问题:UDP打孔实施

使用已发布的代码here我现在可以连接到远程计算机并侦听传入连接的同一端口(将2个UDP客户端绑定到同一端口)。

由于某种原因,两个绑定到同一个端口会阻止彼此接收任何数据。 我有一个UDP服务器响应我的连接,所以如果我连接到它之前绑定任何其他客户端的端口我得到它的回应。

如果我将另一个客户端绑定到端口,则在任一客户端上都不会收到任何数据。

以下是显示我的问题的2个代码段。第一个连接到远程服务器以在NAT设备上创建规则,然后在不同的线程上启动侦听器以捕获传入的数据包。然后代码将数据包发送到本地IP,以便侦听器可以获取它。第二个只发送数据包到本地IP,以确保它能正常工作。我知道这并不是实际的打孔,因为我根本没有生活NAT设备,而是将数据包发送给自己。我现在面临一个问题,如果我使用NAT设备外部的计算机进行连接,我不会想到这会有什么不同。我试过用我的网络上的另一台计算机和WireShark(数据包嗅探器)来测试侦听器。我看到从其他计算机传入但未被侦听器UDP客户端(udpServer)或发送者UDP客户端(客户端)接收到的数据包。

[编辑] 2/5/2010 现在我已经添加了一个函数调用,关闭第一个UDP客户端后,首次发送和接收数据包只生活第二个UDP客户端侦听端口。这可以工作,我可以从该端口的网络内部接收数据包。我现在将尝试发送和接收来自网络外部的数据包。我会尽快发现我的发现。

使用此代码我得到的监听客户端上的数据:

static void Main(string[] args) 
{ 
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
     UdpClient udpServer = new UdpClient(); 
     udpServer.ExclusiveAddressUse = false; 
     udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     udpServer.Client.Bind(localpt); 

     IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
     Console.WriteLine("Listening on " + localpt + "."); 
     byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever 
     Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    }); 

    Thread.Sleep(1000); 

    UdpClient udpServer2 = new UdpClient(6000); 

    // the following lines work and the data is received 
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 
    udpServer2.Send(new byte[] { 0x41 }, 1); 

    Console.Read(); 
} 

如果我使用下面的代码,我的客户端和服务器之间的连接和数据传输后,监听UDP客户端将不会收到任何东西:

static void Main(string[] args) 
{ 
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 

    //if the following lines up until serverConnect(); are removed all packets are received correctly 
    client = new UdpClient(); 
    client.ExclusiveAddressUse = false; 
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    client.Client.Bind(localpt); 
    remoteServerConnect(); //connection to remote server is done here 
          //response is received correctly and printed to the console 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
     UdpClient udpServer = new UdpClient(); 
     udpServer.ExclusiveAddressUse = false; 
     udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     udpServer.Client.Bind(localpt); 

     IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
     Console.WriteLine("Listening on " + localpt + "."); 
     byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever 
     Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    }); 

    Thread.Sleep(1000); 

    UdpClient udpServer2 = new UdpClient(6000); 

    // I expected the following line to work and to receive this as well 
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 
    udpServer2.Send(new byte[] { 0x41 }, 1); 

    Console.Read(); 
} 
+0

如何处理有关IP或端口的数据包丢失的情况? – user1914692 2014-07-07 16:36:23

回答

1

更新:

的UdpClients的哪种结合第一是将要发送的一个Windows传入的数据包。在您的示例中,尝试将设置侦听线程的代码块移动到顶部。

你确定问题不只是接收线程只写入处理单个接收?尝试用以下代替接收线程。

ThreadPool.QueueUserWorkItem(delegate 
{ 
    UdpClient udpServer = new UdpClient(); 
    udpServer.ExclusiveAddressUse = false; 
    udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    udpServer.Client.Bind(localpt); 

    IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
    Console.WriteLine("Listening on " + localpt + "."); 

    while (inEndPoint != null) 
    { 
     byte[] buffer = udpServer.Receive(ref inEndPoint); 
     Console.WriteLine("Bytes received from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    } 
}); 
+0

我没有收到第一个数据包。线程在接收时被阻塞,将接收放在while循环中将不起作用。此外,我确实看到数据包通过数据包嗅探器进入我的电脑,而不是客户端。你有一个UDP打孔工作的例子吗? – brooc 2012-02-05 06:31:26

+0

刚刚在打印线上尝试了一个断点,它从未达到它。 – brooc 2012-02-05 07:16:03

+0

理论上讲,在NAT中创建绑定需要做的是从私有套接字向公共套接字发送数据包。之后,NAT应该将所有数据包从该公共套接字传递到私有套接字(至少在绑定超时之前,这应该至少为X分钟)。听起来这就是你在做什么,所以它应该工作得很好。 – sipwiz 2012-02-05 09:56:35

4

您是否尝试过使用异步功能,这里是一个如何得到它的工作,它可能需要一些工作要实现100%的功能一个例子:

public void HolePunch(String ServerIp, Int32 Port) 
    { 
     IPEndPoint LocalPt = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], Port); 
     UdpClient Client = new UdpClient(); 
     Client.ExclusiveAddressUse = false; 
     Client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     Client.Client.Bind(LocalPt); 

     IPEndPoint RemotePt = new IPEndPoint(IPAddress.Parse(ServerIp), Port); 

     // This Part Sends your local endpoint to the server so if the two peers are on the same nat they can bypass it, you can omit this if you wish to just use the remote endpoint. 
     byte[] IPBuffer = System.Text.Encoding.UTF8.GetBytes(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString()); 
     byte[] LengthBuffer = BitConverter.GetBytes(IPBuffer.Length); 
     byte[] PortBuffer = BitConverter.GetBytes(Port); 
     byte[] Buffer = new byte[IPBuffer.Length + LengthBuffer.Length + PortBuffer.Length]; 
     LengthBuffer.CopyTo(Buffer,0); 
     IPBuffer.CopyTo(Buffer, LengthBuffer.Length); 
     PortBuffer.CopyTo(Buffer, IPBuffer.Length + LengthBuffer.Length); 
     Client.BeginSend(Buffer, Buffer.Length, RemotePt, new AsyncCallback(SendCallback), Client); 

     // Wait to receve something 
     BeginReceive(Client, Port); 

     // you may want to use a auto or manual ResetEvent here and have the server send back a confirmation, the server should have now stored your local (you sent it) and remote endpoint. 

     // you now need to work out who you need to connect to then ask the server for there remote and local end point then need to try to connect to the local first then the remote. 
     // if the server knows who you need to connect to you could just have it send you the endpoints as the confirmation. 

     // you may also need to keep this open with a keepalive packet untill it is time to connect to the peer or peers. 

     // once you have the endpoints of the peer you can close this connection unless you need to keep asking the server for other endpoints 

     Client.Close(); 
    } 

    public void ConnectToPeer(String PeerIp, Int32 Port) 
    { 
     IPEndPoint LocalPt = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], Port); 
     UdpClient Client = new UdpClient(); 
     Client.ExclusiveAddressUse = false; 
     Client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     Client.Client.Bind(LocalPt); 
     IPEndPoint RemotePt = new IPEndPoint(IPAddress.Parse(PeerIp), Port); 
     Client.Connect(RemotePt); 
     //you may want to keep the peer client connections in a list. 

     BeginReceive(Client, Port); 
    } 

    public void SendCallback(IAsyncResult ar) 
    { 
     UdpClient Client = (UdpClient)ar.AsyncState; 
     Client.EndSend(ar); 
    } 

    public void BeginReceive(UdpClient Client, Int32 Port) 
    { 
     IPEndPoint ListenPt = new IPEndPoint(IPAddress.Any, Port); 

     Object[] State = new Object[] { Client, ListenPt }; 

     Client.BeginReceive(new AsyncCallback(ReceiveCallback), State); 
    } 

    public void ReceiveCallback(IAsyncResult ar) 
    { 
     UdpClient Client = (UdpClient)((Object[])ar.AsyncState)[0]; 
     IPEndPoint ListenPt = (IPEndPoint)((Object[])ar.AsyncState)[0]; 

     Byte[] receiveBytes = Client.EndReceive(ar, ref ListenPt); 
    } 

希望这有助于。

+0

将外部IP配置为服务器的IP地址和连接服务器时的对等IP地址的位置? – 2016-05-14 16:31:38

13

如果我理解正确,您正试图在2个客户端之间进行点对点通信,每个客户端在不同的NAT后面,使用中介服务器进行打孔?

几年前,我的确在C#同样的事情,我还没有发现的代码还没有,但我给你一些指针,如果你喜欢:

首先,我不会使用Connect()函数在udpclient上,因为UDP是一个无连接协议,所有这个函数真正做的是隐藏UDP套接字的功能。

你应该会借此以下步骤:

  1. 打开服务器上的UDP套接字与它的端口没有被防火墙阻止,在一个特定的端口(例如绑定此插槽例如选择的端口23000)
  2. 在第一个客户端上创建一个UDP套接字,并在23000处向服务器发送一些东西。不要绑定这个套接字。当使用UDP发送数据包时,Windows会自由港自动分配到插座
  3. 从其他客户端
  4. 服务器现已获得2个包从2个客户端在2点不同不会忽略有2个不同的执行相同端口。测试服务器是否可以将数据包发送回相同的地址和端口。 (如果这不起作用,你做错了什么或你的NAT不工作,你知道它的工作,如果你可以不打开端口玩游戏:D)
  5. 服务器现在应该发送其他客户端的地址和端口到每个连接的客户端。
  6. 客户端现在应该能够使用UDP将数据包发送到从服务器接收的地址。

你应该注意,在NAT上使用的端口可能不是你的客户端PC上的端口!服务器应该将这个外部端口分配给客户端。 您必须使用外部地址和外部端口发送给!

另请注意,您的NAT可能不支持这种端口转发。一些NAT将分配端口上的所有传入流量转发给客户端,这正是您想要的。但是有些NAT会对传入的数据包地址进行过滤,因此可能会阻塞其他客户端数据包。尽管使用标准的个人用户路由器时,这是不太可能的。

+0

谢谢你的回答!我会试一试。我想我可能没有尝试在两个方向上发送数据包... – brooc 2012-07-08 05:43:31

+2

好吧,让我知道如果你得到它的工作:) 另一件事,我理解正确,你是绑定多个套接字到同一端口?在大多数情况下,每个端口只能使用一个套接字,但我不知道可以绑定多个套接字:D – MHGameWork 2012-07-10 08:33:37

2

编辑:经过很多更多的测试,除非启用UPnP,否则对我来说这似乎不起作用。所以我在这里写的很多东西可能会发现很有用,但很多人没有启用UPnP(因为它存在安全风险),所以它不适用于他们。

下面是一些使用PubNub作为中继服务器的代码:)。我不建议在没有测试的情况下使用此代码,因为它不完美(我不确定它是否安全或正确的方式做事?idk我不是网络专家),但它应该给你一个想法做什么。它至少在我的爱好项目中为我工作至今。它缺少的东西是:

  • 测试客户端是否在您的LAN上。我只发送给您的局域网和另一个网络上的设备,但效率非常低。
  • 测试客户端何时停止侦听,例如,如果他们关闭了程序。因为这是UDP,所以它是无状态的,因此无论我们是否将消息发送到无效,但我们可能不应该这样做,如果没有人得到它们
  • 我使用Open.NAT以编程方式执行端口转发,但这可能不起作用在某些设备上。具体来说,它使用的UPnP有点不安全,需要UDP端口1900手动端口转发。一旦他们这样做了,它在大多数路由器上都得到支持,但许多人还没有这样做。

因此,首先,您需要一种获取外部和本地IP的方法。这里是让你的本地IP代码:

// From http://stackoverflow.com/questions/6803073/get-local-ip-address 
public string GetLocalIp() 
{ 
    var host = Dns.GetHostEntry(Dns.GetHostName()); 
    foreach (var ip in host.AddressList) 
    { 
     if (ip.AddressFamily == AddressFamily.InterNetwork) 
     { 
      return ip.ToString(); 
     } 
    } 
    throw new Exception("Failed to get local IP"); 
} 

而且这里是通过努力,旨在回报您的外部IP一些网站让你的外部IP一些代码

public string GetExternalIp() 
{ 
    for (int i = 0; i < 2; i++) 
    { 
     string res = GetExternalIpWithTimeout(400); 
     if (res != "") 
     { 
      return res; 
     } 
    } 
    throw new Exception("Failed to get external IP"); 
} 
private static string GetExternalIpWithTimeout(int timeoutMillis) 
{ 
    string[] sites = new string[] { 
     "http://ipinfo.io/ip", 
     "http://icanhazip.com/", 
     "http://ipof.in/txt", 
     "http://ifconfig.me/ip", 
     "http://ipecho.net/plain" 
    }; 
    foreach (string site in sites) 
    { 
     try 
     { 
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(site); 
      request.Timeout = timeoutMillis; 
      using (var webResponse = (HttpWebResponse)request.GetResponse()) 
      { 
       using (Stream responseStream = webResponse.GetResponseStream()) 
       { 
        using (StreamReader responseReader = new System.IO.StreamReader(responseStream, Encoding.UTF8)) 
        { 
         return responseReader.ReadToEnd().Trim(); 
        } 
       } 
      } 
     } 
     catch 
     { 
      continue; 
     } 
    } 

    return ""; 

} 

现在,我们需要找到一个开放端口并将其转发到外部端口。如上所述,我使用了Open.NAT。首先,您在查看registered UDP ports之后,将您认为合理的端口列表放在一起。下面是例如几个:

public static int[] ports = new int[] 
{ 
    5283, 
    5284, 
    5285, 
    5286, 
    5287, 
    5288, 
    5289, 
    5290, 
    5291, 
    5292, 
    5293, 
    5294, 
    5295, 
    5296, 
    5297 
}; 

现在我们可以通过他们,并希望找到一个循环,是不是在使用使用的端口转发:

public UdpClient GetUDPClientFromPorts(out Socket portHolder, out string localIp, out int localPort, out string externalIp, out int externalPort) 
{ 
    localIp = GetLocalIp(); 
    externalIp = GetExternalIp(); 

    var discoverer = new Open.Nat.NatDiscoverer(); 
    var device = discoverer.DiscoverDeviceAsync().Result; 

    IPAddress localAddr = IPAddress.Parse(localIp); 
    int workingPort = -1; 
    for (int i = 0; i < ports.Length; i++) 
    { 
     try 
     { 
      // You can alternatively test tcp with nc -vz externalip 5293 in linux and 
      // udp with nc -vz -u externalip 5293 in linux 
      Socket tempServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
      tempServer.Bind(new IPEndPoint(localAddr, ports[i])); 
      tempServer.Close(); 
      workingPort = ports[i]; 
      break; 
     } 
     catch 
     { 
     // Binding failed, port is in use, try next one 
     } 
    } 


    if (workingPort == -1) 
    { 
     throw new Exception("Failed to connect to a port"); 
    } 


    int localPort = workingPort; 

    // You could try a different external port if the below code doesn't work 
    externalPort = workingPort; 

    // Mapping ports 
    device.CreatePortMapAsync(new Open.Nat.Mapping(Open.Nat.Protocol.Udp, localPort, externalPort)); 

    // Bind a socket to our port to "claim" it or cry if someone else is now using it 
    try 
    { 
     portHolder = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
     portHolder.Bind(new IPEndPoint(localAddr, localPort)); 
    } 
    catch 
    { 
     throw new Exception("Failed, someone is now using local port: " + localPort); 
    } 


    // Make a UDP Client that will use that port 
    UdpClient udpClient = new UdpClient(localPort); 
    return udpClient; 
} 

现在的PubNub中继服务器代码( P2PPeer将在后面定义)。有很多在这里,所以我不真的会解释,但有希望的代码是很清楚,以帮助您了解什么是对

public delegate void NewPeerCallback(P2PPeer newPeer); 
public event NewPeerCallback OnNewPeerConnection; 

public Pubnub pubnub; 
public string pubnubChannelName; 
public string localIp; 
public string externalIp; 
public int localPort; 
public int externalPort; 
public UdpClient udpClient; 
HashSet<string> uniqueIdsPubNubSeen; 
object peerLock = new object(); 
Dictionary<string, P2PPeer> connectedPeers; 
string myPeerDataString; 

public void InitPubnub(string pubnubPublishKey, string pubnubSubscribeKey, string pubnubChannelName) 
{ 
    uniqueIdsPubNubSeen = new HashSet<string>(); 
    connectedPeers = new Dictionary<string, P2PPeer>; 
    pubnub = new Pubnub(pubnubPublishKey, pubnubSubscribeKey); 
    myPeerDataString = localIp + " " + externalIp + " " + localPort + " " + externalPort + " " + pubnub.SessionUUID; 
    this.pubnubChannelName = pubnubChannelName; 
    pubnub.Subscribe<string>(
     pubnubChannelName, 
     OnPubNubMessage, 
     OnPubNubConnect, 
     OnPubNubError); 
    return pubnub; 
} 

//// Subscribe callbacks 
void OnPubNubConnect(string res) 
{ 
    pubnub.Publish<string>(pubnubChannelName, connectionDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
} 

void OnPubNubError(PubnubClientError clientError) 
{ 
    throw new Exception("PubNub error on subscribe: " + clientError.Message); 
} 

void OnPubNubMessage(string message) 
{ 
    // The message will be the string ["localIp externalIp localPort externalPort","messageId","channelName"] 
    string[] splitMessage = message.Trim().Substring(1, message.Length - 2).Split(new char[] { ',' }); 
    string peerDataString = splitMessage[0].Trim().Substring(1, splitMessage[0].Trim().Length - 2); 

    // If you want these, I don't need them 
    //string peerMessageId = splitMessage[1].Trim().Substring(1, splitMessage[1].Trim().Length - 2); 
    //string channelName = splitMessage[2].Trim().Substring(1, splitMessage[2].Trim().Length - 2); 


    string[] pieces = peerDataString.Split(new char[] { ' ', '\t' }); 
    string peerLocalIp = pieces[0].Trim(); 
    string peerExternalIp = pieces[1].Trim(); 
    string peerLocalPort = int.Parse(pieces[2].Trim()); 
    string peerExternalPort = int.Parse(pieces[3].Trim()); 
    string peerPubnubUniqueId = pieces[4].Trim(); 

    pubNubUniqueId = pieces[4].Trim(); 

    // If you are on the same device then you have to do this for it to work idk why 
    if (peerLocalIp == localIp && peerExternalIp == externalIp) 
    { 
     peerLocalIp = "127.0.0.1"; 
    } 


    // From me, ignore 
    if (peerPubnubUniqueId == pubnub.SessionUUID) 
    { 
     return; 
    } 

    // We haven't set up our connection yet, what are we doing 
    if (udpClient == null) 
    { 
     return; 
    } 


    // From someone else 


    IPEndPoint peerEndPoint = new IPEndPoint(IPAddress.Parse(peerExternalIp), peerExternalPort); 
    IPEndPoint peerEndPointLocal = new IPEndPoint(IPAddress.Parse(peerLocalIp), peerLocalPort); 

    // First time we have heard from them 
    if (!uniqueIdsPubNubSeen.Contains(peerPubnubUniqueId)) 
    { 
     uniqueIdsPubNubSeen.Add(peerPubnubUniqueId); 

     // Dummy messages to do UDP hole punching, these may or may not go through and that is fine 
     udpClient.Send(new byte[10], 10, peerEndPoint); 
     udpClient.Send(new byte[10], 10, peerEndPointLocal); // This is if they are on a LAN, we will try both 
     pubnub.Publish<string>(pubnubChannelName, myPeerDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
    } 
    // Second time we have heard from them, after then we don't care because we are connected 
    else if (!connectedPeers.ContainsKey(peerPubnubUniqueId)) 
    { 
     //bool isOnLan = IsOnLan(IPAddress.Parse(peerExternalIp)); TODO, this would be nice to test for 
     bool isOnLan = false; // For now we will just do things for both 
     P2PPeer peer = new P2PPeer(peerLocalIp, peerExternalIp, peerLocalPort, peerExternalPort, this, isOnLan); 
     lock (peerLock) 
     { 
      connectedPeers.Add(peerPubnubUniqueId, peer); 
     } 

     // More dummy messages because why not 
     udpClient.Send(new byte[10], 10, peerEndPoint); 
     udpClient.Send(new byte[10], 10, peerEndPointLocal); 


     pubnub.Publish<string>(pubnubChannelName, connectionDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
     if (OnNewPeerConnection != null) 
     { 
      OnNewPeerConnection(peer); 
     } 
    } 
} 

//// Publish callbacks 
void OnPubNubTheyGotMessage(object result) 
{ 

} 

void OnPubNubMessageFailed(PubnubClientError clientError) 
{ 
    throw new Exception("PubNub error on publish: " + clientError.Message); 
} 

去这里是一个P2PPeer

public class P2PPeer 
{ 
    public string localIp; 
    public string externalIp; 
    public int localPort; 
    public int externalPort; 
    public bool isOnLan; 

    P2PClient client; 

    public delegate void ReceivedBytesFromPeerCallback(byte[] bytes); 

    public event ReceivedBytesFromPeerCallback OnReceivedBytesFromPeer; 


    public P2PPeer(string localIp, string externalIp, int localPort, int externalPort, P2PClient client, bool isOnLan) 
    { 
     this.localIp = localIp; 
     this.externalIp = externalIp; 
     this.localPort = localPort; 
     this.externalPort = externalPort; 
     this.client = client; 
     this.isOnLan = isOnLan; 



     if (isOnLan) 
     { 
      IPEndPoint endPointLocal = new IPEndPoint(IPAddress.Parse(localIp), localPort); 
      Thread localListener = new Thread(() => ReceiveMessage(endPointLocal)); 
      localListener.IsBackground = true; 
      localListener.Start(); 
     } 

     else 
     { 
      IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(externalIp), externalPort); 
      Thread externalListener = new Thread(() => ReceiveMessage(endPoint)); 
      externalListener.IsBackground = true; 
      externalListener.Start(); 
     } 
    } 

    public void SendBytes(byte[] data) 
    { 
     if (client.udpClient == null) 
     { 
      throw new Exception("P2PClient doesn't have a udpSocket open anymore"); 
     } 
     //if (isOnLan) // This would work but I'm not sure how to test if they are on LAN so I'll just use both for now 
     { 
      client.udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse(localIp), localPort)); 
     } 
     //else 
     { 
      client.udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse(externalIp), externalPort)); 
     } 
    } 

    // Encoded in UTF8 
    public void SendString(string str) 
    { 
     SendBytes(System.Text.Encoding.UTF8.GetBytes(str)); 
    } 


    void ReceiveMessage(IPEndPoint endPoint) 
    { 
     while (client.udpClient != null) 
     { 
      byte[] message = client.udpClient.Receive(ref endPoint); 
      if (OnReceivedBytesFromPeer != null) 
      { 
       OnReceivedBytesFromPeer(message); 
      } 
      //string receiveString = Encoding.UTF8.GetString(message); 
      //Console.Log("got: " + receiveString); 
     } 
    } 
} 

最后,这里是我所有usings:

using PubNubMessaging.Core; // Get from PubNub GitHub for C#, I used the Unity3D library 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 

我打开的意见和问题,随时给反馈,如果这里的东西是不好的做法,或不工作。从我的代码翻译中引入了一些错误,我最终会在这里修复这些错误,但这至少应该让您知道该怎么做。