To test my server/client application, where each client is known by its IP address, I created several network adapters (see How to Create a Virtual Network Adapter in .NET?). Both 192.168.0.10 and 11 now correspond to local ethernet adaptors (10 being the "real" one, 11 being a loopback adapter).
The client can Connect
itself to the server as long as it doesn't Bind
its socket to a specific address. But if it does, the server doesn't notice anything and a timeout occurs in the client (I want to use Bind
as for security reasons the server automatically detects which client is connecting itself by looking at the IP address of the remote end point of the new connection: the server will drop the connection at once if it doesn't know the IP address - previously I was using several virtual machines, but it uses a lot more RAM and is less practical to use).
Here's the code in my server, listening eg on 192.168.0.10:1234
IPEndPoint myEP = new IPEndPoint(myAddress, myPort);
Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);
Socket acceptedSocket = listeningSocket.Accept();
Here's the code in my client, binding eg to 192.168.0.11 (any port) and connecting to 192.168.0.10:1234
Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(myAddress, 0)); // Bind to local address using automatic port
socket.Connect(new IPEndPoint(svrAddress, svrPort)); // Works fine without Bind, timeout with Bind
I've tried the same using the corresponding IPv6 addresses but I get the exact same result.
If I Bind
the client on the same address (using a different port than the server), it works fine.
Any idea what I'm doing wrong?
EDIT Here is my test projects (it might be useful to someone)
Server part:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Server
{
class Program
{
static void Main(string[] args)
{
IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
string line = string.Empty;
while (line != "q")
{
// Gets the IP address to listen on.
Console.WriteLine("IP to listen on:");
int count = 0;
foreach (IPAddress ip in ips)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
string numString = Console.ReadLine();
int pos = Convert.ToInt32(numString) - 1;
IPAddress myAddress = ips[pos]; // Removing or not the scope ID doesn't change anything as "localEndPoint" below will contain it no matter what
// Binds and starts listening.
IPEndPoint myEP = new IPEndPoint(myAddress, 12345);
Socket listeningSocket = new Socket(myAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listeningSocket.Bind(myEP);
listeningSocket.Listen(50);
IPEndPoint localEndPoint = (IPEndPoint)listeningSocket.LocalEndPoint;
Console.WriteLine("Listening on {0}:{1}", localEndPoint.Address, localEndPoint.Port);
Task.Factory.StartNew(() =>
{
try
{
// Accepts new connections and sends some dummy byte array, then closes the socket.
Socket acceptedSocket = listeningSocket.Accept();
IPEndPoint remoteEndPoint = (IPEndPoint)acceptedSocket.RemoteEndPoint;
Console.WriteLine("Accepted connection from {0}:{1}.", remoteEndPoint.Address, remoteEndPoint.Port);
acceptedSocket.Send(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
acceptedSocket.Close(5000);
Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue");
}
catch (Exception ex)
{ }
});
line = Console.ReadLine();
// Closes the listening socket.
listeningSocket.Close();
}
}
}
}
Client part
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Client
{
class Program
{
static void Main(string[] args)
{
IPAddress[] ips = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
string line = string.Empty;
while (line != "q")
{
// Gets the IP address to connect to (removes the "scope ID" if it's an IPv6).
Console.WriteLine("IP to connect to:");
int count = 0;
foreach (IPAddress ip in ips)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
string numString = Console.ReadLine();
int pos = Convert.ToInt32(numString) - 1;
IPAddress svrAddress = ips[pos].AddressFamily == AddressFamily.InterNetworkV6
? new IPAddress(ips[pos].GetAddressBytes())
: ips[pos];
Console.WriteLine("Connecting to " + svrAddress);
// Gets the IP address to bind on (can chose "none" - also removes the "scope ID" if it's an IPv6).
Console.WriteLine("IP to bind to:");
Console.WriteLine("0: none");
count = 0;
IPAddress[] filteredIps = ips.Where(i => i.AddressFamily == svrAddress.AddressFamily).ToArray();
foreach (IPAddress ip in filteredIps)
Console.WriteLine("{0}: {1}", ++count, ip.ToString());
numString = Console.ReadLine();
pos = Convert.ToInt32(numString) - 1;
IPEndPoint localEndPoint = (pos == -1)
? null
: new IPEndPoint(
filteredIps[pos].AddressFamily == AddressFamily.InterNetworkV6
? new IPAddress(filteredIps[pos].GetAddressBytes())
: filteredIps[pos]
, 0);
Console.WriteLine("Binding to " + (localEndPoint == null ? "none" : localEndPoint.Address.ToString()));
// Binds to an address if we chose to.
Socket socket = new Socket(svrAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
if (localEndPoint != null)
socket.Bind(localEndPoint);
Task.Factory.StartNew(() =>
{
try
{
// Connects to the server and receives the dummy byte array, then closes the socket.
socket.Connect(new IPEndPoint(svrAddress, 12345));
IPEndPoint remoteEndPoint = (IPEndPoint)socket.RemoteEndPoint;
Console.WriteLine("Connected to {0}:{1}", remoteEndPoint.Address, remoteEndPoint.Port);
byte[] buffer = new byte[10];
Console.WriteLine((socket.Receive(buffer) == buffer.Length) ? "Received message" : "Incorrect message");
socket.Close();
}
catch (Exception ex)
{
// An exception occured: should be a SocketException due to a timeout if we chose to bind to an address.
Console.WriteLine("ERROR: " + ex.ToString());
}
Console.WriteLine("-= FINISHED =- Type q to quit, anything else to continue");
});
line = Console.ReadLine();
}
}
}
}
See Question&Answers more detail:
os