2012-02-04 289 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; 

我打開的意見和問題,隨時給反饋,如果這裏的東西是不好的做法,或不工作。從我的代碼翻譯中引入了一些錯誤,我最終會在這裏修復這些錯誤,但這至少應該讓您知道該怎麼做。