संपादित करें: बहुत अधिक परीक्षण के बाद यह मेरे लिए बिल्कुल काम नहीं करता है जब तक कि मैं यूपीएनपी सक्षम नहीं करता। इसलिए मैंने यहां लिखी कई चीजें उपयोगी पा सकते हैं लेकिन कई लोगों के पास यूपीएनपी सक्षम नहीं है (क्योंकि यह एक सुरक्षा जोखिम है) इसलिए यह उनके लिए काम नहीं करेगा।
यहां कुछ कोड है जो PubNub को रिले सर्वर के रूप में उपयोग कर रहा है :)। मैं परीक्षण के बिना इस कोड का उपयोग करने की अनुशंसा नहीं करता क्योंकि यह सही नहीं है (मुझे यकीन नहीं है कि यह सुरक्षित है या चीजों को करने का सही तरीका है? Idk मैं नेटवर्किंग विशेषज्ञ नहीं हूं) लेकिन यह आपको एक विचार देना चाहिए क्या करना है यह कम से कम एक शौक परियोजना में मेरे लिए काम किया है। चीजें जो गायब हैं वे हैं:
- क्लाइंट आपके LAN पर परीक्षण कर रहा है या नहीं। मैं सिर्फ उन दोनों को भेजता हूं जो आपके LAN के लिए काम करते हैं और किसी अन्य नेटवर्क पर एक डिवाइस लेकिन यह बहुत अक्षम है।
- परीक्षण करते समय क्लाइंट सुनना बंद कर देता है, उदाहरण के लिए, उन्होंने प्रोग्राम बंद कर दिया।चूंकि यह यूडीपी है, यह स्टेटलेस है इसलिए इससे कोई फर्क नहीं पड़ता कि हम शून्य में संदेश भेज रहे हैं, लेकिन हमें शायद ऐसा नहीं करना चाहिए यदि कोई उन्हें
- प्रोग्रामिंग के लिए पोर्ट अग्रेषण करने के लिए Open.NAT का उपयोग करता है लेकिन यह काम नहीं कर सकता है कुछ उपकरणों पर। विशेष रूप से, यह यूपीएनपी का उपयोग करता है जो थोड़ा असुरक्षित है और मैन्युअल रूप से पोर्ट को अग्रेषित करने के लिए यूडीपी पोर्ट 1 9 00 की आवश्यकता होती है। एक बार ऐसा करने के बाद यह अधिकांश राउटर पर समर्थित है लेकिन कई ने अभी तक यह नहीं किया है।
तो सबसे पहले, आपको अपने बाहरी और स्थानीय आईपी प्राप्त करने का एक तरीका चाहिए। यहाँ अपने स्थानीय आईपी प्राप्त करने के लिए कोड है:
// 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 रिले सर्वर कोड के लिए
अब (यहाँ उदाहरण के लिए कुछ कर रहे हैं बाद में पी 2 पीपीर परिभाषित किया जाएगा)। एक बहुत तो मैं वास्तव में करने वाला समझा नहीं कर रहा हूँ, लेकिन उम्मीद है कि कोड काफी स्पष्ट आप समझते हैं कि
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;
मैं टिप्पणियों और प्रश्नों के लिए खुला रहा हूँ, अगर कुछ यहाँ बुरी प्रथा है या काम नहीं करता प्रतिक्रिया देने के लिए स्वतंत्र लग रहा है। मेरे कोड से अनुवाद में कुछ बग पेश किए गए थे जिन्हें मैं अंततः ठीक कर दूंगा लेकिन इससे कम से कम आपको यह विचार करना चाहिए कि क्या करना है।
आईपी या पोर्ट के बारे में पैकेट खो जाने पर स्थितियों से निपटने के लिए कैसे करें? – user1914692