一度接続すると、ホストのパブリックエンドポイントを送信する、グローバルにアクセス可能なサーバーがあります。2 つのクライアントを使用して接続し、各クライアントで TcpListener と 2 つの TcpClient を使用して相互接続を試みます。接続が常に拒否されるかタイムアウトになるため、何か間違っている可能性があります。
2 つのクライアントのコードは次のとおりです。
static void Main(string[] args)
{
Console.Write("Main server hostname [" + defaultServerHost + "]: ");
string serverHost = Console.ReadLine();
TcpClient client = ConnectTcpClient(serverHost.Length > 0 ? serverHost : defaultServerHost, 9009);
TcpListener listener = CreateTcpListener(client.Client.LocalEndPoint as IPEndPoint);
listener.Start();
Task<TcpClient> incommingTraverse = TraverseIncoming(listener);
StreamReader reader = new StreamReader(client.GetStream());
string publicAddress = reader.ReadLine();
Console.WriteLine("Public endpoint: " + publicAddress);
Console.Write("Remote endpoint: ");
string[] remoteEndPointSplit = Console.ReadLine().Split(':');
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(remoteEndPointSplit[0]), int.Parse(remoteEndPointSplit[1]));
TcpClient publicClient = CreateTcpClient(client.Client.LocalEndPoint as IPEndPoint);
bool publicTraverse = TraverseOutgoing(publicClient, remoteEndPoint).Result;
if (publicTraverse)
{
Console.WriteLine("Connected via public connection");
}
else
{
Console.WriteLine("Connection failed");
}
Console.Read();
}
static TcpClient CreateTcpClient(IPEndPoint localEndPoint)
{
TcpClient client = new TcpClient();
client.Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.Client.Bind(localEndPoint);
return client;
}
static TcpListener CreateTcpListener(IPEndPoint localEndPoint)
{
TcpListener listener = new TcpListener(localEndPoint);
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
return listener;
}
static TcpClient ConnectTcpClient(string host, int port)
{
TcpClient client = new TcpClient();
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.Connect(host, port);
return client;
}
static async Task<TcpClient> TraverseIncoming(TcpListener listener)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
Console.WriteLine("Accepted client");
return client;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
static async Task<bool> TraverseOutgoing(TcpClient client, IPEndPoint remoteEndPoint)
{
try
{
await client.ConnectAsync(remoteEndPoint.Address.ToString(), remoteEndPoint.Port);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
どちらのクライアントも NAT の背後にあり、1 つはホーム ネットワークにあり、もう 1 つはビジネス ネットワークにあります。
Tcp NATトラバーサルの方法については、このドキュメントに従いました。