Testing of socket communication program - c#

Am starting with socket programming with a simple UDPClient program to send some data. The large code snippet is below:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
class ShowIP
{
public static void Main(string[] args)
{
string name = Dns.GetHostName();
//name = "GSL1460";
name = "GSL1296";
try
{
IPAddress[] addrs = Dns.GetHostEntry(name).AddressList;
foreach (IPAddress addr in addrs)
Console.WriteLine("{0}/{1}", name, addr);
Console.WriteLine("Started listening");
Thread listenerThread = new Thread(new ThreadStart(StartListeningUDP));
listenerThread.Start();
Console.WriteLine("Started sending");
for (int counter = 0; counter <= 3; counter++)
{
Thread.Sleep(1000);
Console.WriteLine("Sending {0} time", counter.ToString());
StartSendingUDP(addrs[0]);
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static void StartListeningUDP()
{
UdpClient udpListener = null;
IPEndPoint nwPoint = new IPEndPoint(IPAddress.Any, 12345);
while (true)
{
try
{
udpListener = new UdpClient(12345);
Console.WriteLine("Waiting to receive");
Byte[] receivedBytes = udpListener.Receive(ref nwPoint);
string receivedData = Encoding.ASCII.GetString(receivedBytes);
Console.WriteLine("Data received : " + receivedData);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
udpListener.Close();
}
}
}
private static void StartSendingUDP(IPAddress clientAddress)
{
UdpClient udpSender = new UdpClient();
try
{
Byte[] sendBytes = Encoding.ASCII.GetBytes("Say HI to Papa...");
Console.WriteLine("Data Sent : Say HI to Papa...");
udpSender.Send(sendBytes, sendBytes.Length, new IPEndPoint(clientAddress, 12345));
}
finally
{
udpSender.Close();
}
}
}
The sample works fine on local machine, but am not able to send data to another machine on the intranet.
During testing
Am uncommenting the appropriate code to send data to his machine
Am running the Receiver bit on his machine
Have checked that the required port is open on his machine
Am I missing something? Please suggest.

udpSender.Flush?

I'm not a C# person, so I can't comment too much on your code, but it looks basically okay. Make sure that the IP address you're sending to is being resolved correctly to your receiving machine.
Also, see if Windows has firewalled your internet connection, and try disabling the firewall if so. And, I know that Microsoft has some ideas about "safe" code that have caused us some problems in the past. I don't have any specifics, but there might be settings in the project that keep it from being able to access the network.

The UDP-Listener might be listening on localhost only. You could try to replace
udpListener = new UdpClient(12345)
in StartListeningUDP() with
udpListener = new UdpClient(new IPEndPoint(IPAddress.Any,12345))

you can't really send UDP over the internet without doing few things before.
you will get too many udp filters on the way.
even if you will disable your firewall, your router/provider modem can be set to block it.
else - your provider servers will block it.
so in fact you will have to make sure that this port is open for UDP, just as on your localhost it won't work unless you will open this port in the firewall and/or install the loopback adapter

Related

Listen to message from an IP from another machine

I am trying to send a message to Unity through UDP. The machine that sends the message has IP as 192.16.14.1 and port as 3034. How do I enter these two inside of Unity application? I have found a code to listen for UDP messages but I cannot set the IP address here. Also the Unity application should be running at all times even if the message from another machine is sent or not.
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public class UDP_Listen : MonoBehaviour
{
UdpClient clientData;
int portData = 3034;
public int receiveBufferSize = 120000;
public bool showDebug = false;
IPEndPoint ipEndPointData;
private object obj = null;
private System.AsyncCallback AC;
byte[] receivedBytes;
void Start()
{
InitializeUDPListener();
}
public void InitializeUDPListener()
{
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
clientData = new UdpClient();
clientData.Client.ReceiveBufferSize = receiveBufferSize;
clientData.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue: true);
clientData.ExclusiveAddressUse = false;
clientData.EnableBroadcast = true;
clientData.Client.Bind(ipEndPointData);
clientData.DontFragment = true;
if (showDebug) Debug.Log("BufSize: " + clientData.Client.ReceiveBufferSize);
AC = new System.AsyncCallback(ReceivedUDPPacket);
clientData.BeginReceive(AC, obj);
Debug.Log("UDP - Start Receiving..");
}
void ReceivedUDPPacket(System.IAsyncResult result)
{
//stopwatch.Start();
receivedBytes = clientData.EndReceive(result, ref ipEndPointData);
ParsePacket();
clientData.BeginReceive(AC, obj);
//stopwatch.Stop();
//Debug.Log(stopwatch.ElapsedTicks);
//stopwatch.Reset();
} // ReceiveCallBack
void ParsePacket()
{
// work with receivedBytes
Debug.Log("receivedBytes len = " + receivedBytes.Length);
}
void OnDestroy()
{
if (clientData != null)
{
clientData.Close();
}
}
}
If the Unity application is to be receiving the messages constantly, it needs to be something like:
UdpClient listener = new UdpClient(11000);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Parse("192.16.14.1"), 3034);
while (true)
{
byte[] bytes = listener.Receive(ref groupEP);
}
This should read only calls from the specific IP, not sure which port you want the UDPClient to read out from (specified in the UDPClient constructor) but you can set this to whatever you need it to be.
So there are two different things:
You want to define the receiving local port you Bind your socket to
You want to define the expected sending remote ip + port you want to Receive from
Currently you are using the very same one
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
for both! (Fun fact: As a side effect by using always the same field you basically allow any sender but are then bond to that specific sender from this moment on)
Actually a lot of things you configure there are the default values anyway so here is more or less what I would do
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class UDP_Listen : MonoBehaviour
{
public ushort localReceiverPort = 3034;
public string senderIP = "192.168.111.1";
public ushort remoteSenderPort = 3034;
public bool showDebug = false;
// Thread-safe Queue to handle enqueued actions in the Unity main thread
private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
private Thread udpListenerThread;
private void Start()
{
// do your things completely asynchronous in a background thread
udpListenerThread = new Thread(UDPListenerThread);
udpListenerThread.Start();
}
private void Update()
{
// in the Unity main thread work off the actions
while (mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
private void UDPListenerThread()
{
UdpClient udpClient = null;
try
{
// local end point listens on any local IP
var localEndpoint = new IPEndPoint(IPAddress.Any, localReceiverPort);
udpClient = new UdpClient(localEndpoint);
if (showDebug)
{
Debug.Log("BufSize: " + clientData.Client.ReceiveBufferSize);
}
Debug.Log("UDP - Start Receiving..");
// endless loop -> ok since in a thread and containing blocking call(s)
while (true)
{
// remote sender endpoint -> listens only to specific IP
var expectedSenderEndpoint = new IPEndPoint(IPAddress.Parse(senderIP), remoteSenderPort);
// blocking call - but doesn't matter since this is a thread
var receivedBytes = udpClient.Receive(ref expectedSenderEndpoint);
// parse the bytes here
// do any expensive work while still on a background thread
mainThreadActions.Enqueue(() =>
{
// Put anything in here that is required to happen in the Unity main thread
// so basically anything using GameObject, Transform, etc
});
}
}
// thrown for "Abort"
catch (ThreadAbortException)
{
Debug.Log("UDP Listener terminated");
}
// Catch but Log any other exception
catch (Exception e)
{
Debug.LogException(e);
}
// This is run even if an exception happend
finally
{
// either way dispose the UDP client
udpClient?.Dispose();
}
}
private void OnDestroy()
{
udpListenerThread?.Abort();
}
}
I'm sure the same can be done also using the BeginReceive/EndReceive or task based alternatives but since it is going to run endless anyway I personally find a thread often easier to read and maintain.
I think you got it backwards. This code you shared is for, like you said, listen UDP protocol on desired port. This piece of code needs to be inside your "server". By server try to understand that as the receiving side.
on your shared method InitializeUDPListener(); we have this piece:
ipEndPointData = new IPEndPoint(IPAddress.Any, portData);
this means you are initializing your udp socket to listen for ANY ip adresses at the given port. That said, you have your server ready to go, what you need to do is setup the client side, the one who sends the message.
here some example:
public string serverIp = "127.0.0.1"; // your server ip, this one is sending to local host
public int serverPort = 28500; // your server port
public void ClientSendMessage()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress broadcast = IPAddress.Parse(serverIp);
byte[] sendbuf = Encoding.ASCII.GetBytes("THIS IS A MESSAGE FROM CLIENT!");
IPEndPoint ep = new IPEndPoint(broadcast, serverPort);
s.SendTo(sendbuf, ep);
}
I encourage you to read about UDP/TCP protocols before using them. MS has documentation with details.
here some links:
TCP
UDP
Sockets

C# - Can't receive UDP on second computer

I can send UDP messages from one computer to itself on a certain port but I can't send this messages from one computer to another in the same lan. I am pretty new to C# and I am also new to network programming so the solution is probably easy...
I have tried an example from Microsoft Docs and did only small changes. (names, etc.)
Both codes run as seperate programs.
//RECEIVE
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class UDPListener
{
private const int listenPort = 11000;
private static void Listen()
{
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
try
{
while (true)
{
Console.WriteLine("Waiting for broadcast");
byte[] receivebytes = listener.Receive(ref groupEP);
Console.WriteLine(Encoding.ASCII.GetString(receivebytes, 0, receivebytes.Length));
}
}
catch (SocketException e)
{
Console.WriteLine(e);
}
finally
{
listener.Close();
}
}
public static void Main()
{
Listen();
}
}
//SEND
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class Program
{
static void Main(string[] args)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//192.168.2.5 is the IP Address of the receiving computer
IPAddress broadcast = IPAddress.Parse("192.168.2.5");
byte[] bytestosend = Encoding.ASCII.GetBytes("Hi");
IPEndPoint ep = new IPEndPoint(broadcast, 11000);
socket.SendTo(bytestosend, ep);
Console.WriteLine("bytestosend were send");
}
}
The sending program tells me that the message was sent but the receiving program gives no output.
EDIT 1: The Ressource Manager said that on the receiving computer was no network activity while on the sending computer is actually sending something.
EDIT 2: I tried my applications in an other network and it worked fine. I did some research and I found that sometimes routers don't allow udp broadcasts through a subnet (or anything similar...). So I assume it might be my router. (unfortunately I can not access it's settings.) So I think the problem has resolved itself!

Winsock server/client application in c#

I'm working to make a Client/Server Application in C# using winsock Control. I done every thing in that but i stuck the place of sending data from client to server. In my program server always listen the client using the ip and port. I send the data from the client to server.
1)When click the Listen button on the server form it open the server where client is connect.
2)In Client form 1st i click the connect button for that the server is connected Gives an message (Connect Event: ip) for this message we easly know that the client is connected to the server.
3)Then we enter some data in the Send Data text Box then click Send Button to send the data to server and also save in client.
Code Below:
SERVER:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.Net.Sockets;
namespace Server
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
const string DEFAULT_SERVER = "ip";
const int DEFAULT_PORT = 120;
System.Net.Sockets.Socket serverSocket;
System.Net.Sockets.SocketInformation serverSocketInfo;
public string Startup()
{
IPHostEntry hostInfo = Dns.GetHostByName(DEFAULT_SERVER);
IPAddress serverAddr = hostInfo.AddressList[0];
var serverEndPoint = new IPEndPoint(serverAddr, DEFAULT_PORT);
serverSocket = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
serverSocket.Bind(serverEndPoint);
return serverSocket.LocalEndPoint.ToString();
}
public string Listen()
{
int backlog = 0;
try
{
serverSocket.Listen(backlog);
return "Server listening";
}
catch (Exception ex)
{
return "Failed to listen" + ex.ToString();
}
}
public string ReceiveData()
{
System.Net.Sockets.Socket receiveSocket;
byte[] buffer = new byte[256];
receiveSocket = serverSocket.Accept();
var bytesrecd = receiveSocket.Receive(buffer);
receiveSocket.Close();
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
return encoding.GetString(buffer);
}
private void Listen_Click(object sender, EventArgs e)
{
string serverInfo = Startup();
textBox1.Text = "Server started at:" + serverInfo;
serverInfo = Listen();
textBox1.Text = serverInfo;
//string datatosend = Console.ReadLine();
//SendData(datatosend);
serverInfo = ReceiveData();
textBox1.Text = serverInfo;
//Console.ReadLine();
}
private void winsock_DataArrival(object sender, AxMSWinsockLib.DMSWinsockControlEvents_DataArrivalEvent e)
{
ReceiveData();
Listen();
}
private void winsock_ConnectEvent(object sender, EventArgs e)
{
Listen();
}
}
}
This all are work perfectly But here my problem is that i get data form the client to server at only one time. When i send data again from the client to the server its not working and gives me some Message like
Additional information: Only one usage of each socket address
(protocol/network address/port) is normally permitted
In the server form
serverSocket.Bind(serverEndPoint);
Please someone help me to solve my problem.
Thank you.
Try this. It helps you
delegate void AddTextCallback(string text);
public Form1()
{
InitializeComponent();
}
private void ButtonConnected_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ServerHandler));
}
private void ServerHandler(object state)
{
TcpListener _listner = new TcpListener(IPAddress.Parse("12.2.54.658"), 145);
_listner.Start();
AddText("Server started - Listening on port 145");
Socket _sock = _listner.AcceptSocket();
//AddText("User from IP " + _sock.RemoteEndPoint);
while (_sock.Connected)
{
byte[] _Buffer = new byte[1024];
int _DataReceived = _sock.Receive(_Buffer);
if (_DataReceived == 0)
{
break;
}
AddText("Message Received...");
string _Message = Encoding.ASCII.GetString(_Buffer);
AddText(_Message);
}
_sock.Close();
AddText("Client Disconnected.");
_listner.Stop();
AddText("Server Stop.");
}
private void AddText(string text)
{
if (this.listBox1.InvokeRequired)
{
AddTextCallback d = new AddTextCallback(AddText);
this.Invoke(d, new object[] { text });
}
else
{
this.listBox1.Items.Add(text);
}
}
I'm also have the same problem like you on last month but i solve that using this Receive multiple different messages TcpListener C# from stackoverflow. This helps me lot hope it helps to solve your problem also.
I'm not 100% sure you understand TCP sockets so here goes.
When you use a TCP listener socket you first bind to a port so that clients have a fixed, known point to connect to. This reserves the port for your socket until you give it up by calling Close() on that socket.
Next you Listen in order to begin the process of accepting clients on the port you bound to. You can do both this and the first step in one but as you haven't I haven't here.
Next you call Accept(). This blocks (halts execution) until a client connects and then it returns a socket which is dedicated to communication with that client. If you want to allow another client to connect, you have to call Accept() again.
You can then communicate with your client using the socket that was returned by Accept() until you're done, at which point you call Close() on that socket.
When you're done listening for new connections you call Close() on your listener socket.
However when you press your listen button the following happens:
You bind correctly, you begin listening correctly and then your call to ReceiveData() blocks on the Accept call until a client is received. You then receive some data (though this is TCP so that might not be the whole data!) and then you instantly close the connection to your client.
I presume to get the error you're getting you must then press listen again on your server. This therefore restarts the whole listener socket and when you get to bind to the port the second time your previous listener is still bound to it and thus the call fails because something's already allocated on that port.
Solution wise you need to keep the socket returned from the Accept() call open until you're done with it. Have the client handle the close by calling the Shutdown() method on their socket or establish some convention for marking the end of communication.
You're also going to run into trouble when you try and have multiple users connected and so at some point you're either going to require threads or some asynchronous sockets but I feel that's out the scope of this question.
I suggest you do not use AxMSWinsockLib.. Have a look at socket example given here where it shows how to create a client socket and server socket - https://msdn.microsoft.com/en-us/library/kb5kfec7(v=vs.110).aspx AND this one - https://msdn.microsoft.com/en-us/library/6y0e13d3(v=vs.110).aspx

C# Winform to Connect to Device Using IP

I need to create an application that allows the user to connect to a device (a piece of hardware that will be sent arguments/commands) via wifi (wifi is end goal but I am settling at the moment for any connection) and then send commands to said device. I know some C# and some sockets/IP stuff, but not sockets/IP using C#. The visual, GUI side of the program is not what I am struggling with. I cannot seem to get the socket up and running and make any real connection. I keep getting the "An Invalid Argument was Supplied" exception.
Any tips on this particular issue or help with C# networking/sockets/etc. are welcome.
I declare a new socket by:
Socket sck;
sck = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
The exception is thrown when it tries to process the "sck = new Socket(...);"
Code in Question:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
namespace InternetConnect
{
public partial class Form1 : Form
{
// Global Variables
int portNumber = 0;
string ipAddress = "";
TcpClient client;
Socket sck;
EndPoint epLocal, epRemote;
// Needs to be defined at some point
// These are just holding a place for now
string extraIP = "127.0.0.1";
string extraPort = "135";
public Form1()
{
InitializeComponent();
try
{
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sck.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
private void connectButton_Click(object sender, EventArgs e)
{
try
{
epLocal = new IPEndPoint(IPAddress.Parse(ipAddressTextBox.Text), Convert.ToInt32(portNumberTextBox.Text));
MessageBox.Show("Before Bind");
sck.Bind(epLocal);
MessageBox.Show("After Bind");
epRemote = new IPEndPoint(IPAddress.Parse(extraIP), Convert.ToInt32(extraPort));
sck.Connect(epRemote);
MessageBox.Show("After Connect");
byte[] buffer = new byte[1500];
sck.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref epRemote, new AsyncCallback(MessageCallBack), buffer);
connectButton.Text = "Connected";
connectButton.Enabled = false;
sendButton.Enabled = true;
outgoingTextBox.Focus();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
ipAddress = ipAddressTextBox.Text;
// To make sure there are no letters in the IP Address
int errorCounter = Regex.Matches(ipAddress, #"[a-zA-Z]").Count;
if (errorCounter == 0)
{
if (ipAddress != "")
{
// To make sure the port number is entered without letters
if (int.TryParse(portNumberTextBox.Text, out portNumber))
{
WriteToStatusBar("IP Address and Port Number Valid");
}
else
{
MessageBox.Show("Please enter a valid Port Number.");
}
}
else
{
MessageBox.Show("Please enter a valid IP Address.");
}
}
else
{
MessageBox.Show("Please enter a valid IP Address.");
}
#endregion
try
{
client = new TcpClient();
WriteToStatusBar("Connecting...");
client.Connect(ipAddress, portNumber);
WriteToStatusBar("Connected");
outgoingTextBox.Text = "Enter message to be sent to the device...";
client.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As per request of the OP some information about using TcpClient and even TcpListener in case you need to create a server as well. Following link will help you get started with using TcpClient :https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
or this one in code project covers both client and server: http://www.codeproject.com/Articles/1415/Introduction-to-TCP-client-server-in-C
in that code (the first link) you'll find following statement : stream.Write(data, 0, data.Length);
You want to write multiple times to the socket then suppose you write the same data twice you can do simply :
stream.Write(data, 0, data.Length);
stream.Write(data, 0, data.Length);
At the receiver, you have to realise that because TCP is streaming, you might receive to 2 send 'messages' in one receive or scattered over multiple receives.
Further in the code you will find :
stream.Close();
client.Close();
Which ends the communication and closes the socket, sending after doing the close is not possible anymore.
Using sockets, but also tcpclient and tcplistener (because they are based on sockets), is something that I consider a bit advanced material. Without really understanding what streaming is, what IP addressing is, what TCP, UDP is, how to use sockets , some basic understanding of threads. It is easy to get lost. I am a professional programmer, and I did not touch this material after I read quite a lot of information about it.
The best thing to do is to do more investigation, based on examples on the internet & books. And ask very specific dedicated questions using this medium. Elaborate questions have a tendency to get closed without proper answer which will lead to frustration of course.

IPv4 mapped IPv6 addresses

I'm trying to make a IP version-agnostic client/server. I've been playing around with this in C++ and came up with something that works using the IN6ADDR_SETV4MAPPED macro (as Microsoft so kindly recommends). I followed the code sample here to accomplish this; my code that converts the address is no different from the example and everything works. I can connect to the server from the client by typing in both an IPv4 and IPv6 address (application does the mapping accordingly).
Now I'm looking for a solution in C# to upgrade a simple chat server I made and I've been unable to find any resources on how to use mapped addresses. I haven't found a function that provides the equivalent of IN6ADDR_SETV4MAPPED or any other facility in .net. My question is: how can I go about using a IPv4-mapped IPv6 address in C# (client-side)?
What I've tried:
Prepend string "::ffff:" to dotted IPv4 notation, call Socket.Connect using this address. Resulting address string looks like ::ffff:127.0.0.1.
Prepend string "::ffff:". Convert each octect from dotted format into hex and separate with colons, call Socket.Connect. Resulting address string looks like ::ffff:7f:0:0:1.
Neither of these approaches have worked so far.
Code snippet for server:
this.m_endPoint = new IPEndPoint(IPAddress.IPv6Any, 1337);
this.m_server.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
this.m_server.Bind(this.m_endPoint);
this.m_server.Listen(10);
Code snippet for client:
public ClientNet(string host, short port)
{
IPAddress ip;
if(IPAddress.TryParse(host, out ip))
{
string[] octs = host.Split(new char[] { '.' });
host = "::ffff:";
for(int i = 0; i < octs.Length; ++i)
{
host += string.Format("{0:x}", int.Parse(octs[i]));
if(i + 1 != octs.Length)
{
host += ":";
}
}
}
else
{
throw new ClientCreateException("[in ClientNet.Constructor] Unable to create client; use IPv4 address");
}
Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
client.Connect(host, port);
. . . //More initialization
}
Came back to this today thinking I might be able to figure it out. And I did! The answer is quite easy and I feel like a fool for not getting it for a year.
Two things about the code I posted:
Should have used IPAddress.MaptoIPv6 (see MSDN link) instead of that silly, contrived loop I wrote that's more prone to errors.
a. I realized later while working in .NET 4.0 that the convenience functions I used in my sample are not available until .NET 4.5. A quick code sample I threw together is at the bottom of this post, in case anyone else is stuck in an earlier version of .NET.
Real solution: Needed to call client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0); prior to calling client.Connect().
Here is a full code example of a sample application I wrote today to test it out. I was able to make a connection using both ::1 and 127.0.0.1 as addresses. Note that the server Socket is created for IPv6, and that the SocketOptionName.IPv6Only option is set to 0 on both the client and the server.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace sixsharp
{
class Program
{
static void Main(string[] args)
{
if(args.Length <= 0) //run as server
RunServer();
else
RunClient(args);
Console.WriteLine("Press enter to close.");
Console.ReadLine();
}
static void RunServer()
{
using(Socket serv = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
serv.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
serv.Bind(new IPEndPoint(IPAddress.IPv6Any, 1337));
serv.Listen(5);
Console.Write("Listening for client connection...");
using(Socket client = serv.Accept())
{
Console.WriteLine("Client connection accepted from {0}", client.RemoteEndPoint.ToString());
byte[] buf = new byte[128];
client.Receive(buf, 128, SocketFlags.None);
Console.WriteLine("Got \'{0}\' from client", Encoding.ASCII.GetString(buf));
Console.WriteLine("Echoing response");
client.Send(buf);
client.Shutdown(SocketShutdown.Both);
}
}
Console.WriteLine("Done.");
}
static void RunClient(string[] args)
{
using(Socket client = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp))
{
client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, 0);
Console.WriteLine("Setting up address, input is {0}", args[0]);
IPEndPoint ep;
try
{
ep = new IPEndPoint(IPAddress.Parse(args[0]), 1337);
}
catch(FormatException fe)
{
Console.WriteLine("IP address was improperly formatted and not parsed.");
Console.WriteLine("Detail: {0}", fe.Message);
return;
}
if(ep.AddressFamily == AddressFamily.InterNetwork)
{
ep = new IPEndPoint(ep.Address.MapToIPv6(), ep.Port);
if(!ep.Address.IsIPv4MappedToIPv6 || ep.Address.AddressFamily != AddressFamily.InterNetworkV6)
{
Console.WriteLine("Error mapping IPv4 address to IPv6");
return;
}
}
Console.WriteLine("Connecting to server {0} ...", ep.ToString());
try
{
client.Connect(ep);
}
catch(Exception ex)
{
Console.WriteLine("Unable to connect.\n Detail: {0}", ex.Message);
return;
}
client.Send(Encoding.ASCII.GetBytes("This is a test message. Hello!"));
byte[] buf = new byte[128];
client.Receive(buf);
Console.WriteLine("Got back from server: {0}", Encoding.ASCII.GetString(buf));
client.Shutdown(SocketShutdown.Both);
}
Console.WriteLine("Done.");
}
}
}
Client output:
Setting up address, input is 10.2.6.179
Connecting to server [::ffff:10.2.6.179]:1337 ...
Got back from server: This is a test message. Hello!
Done.
Press enter to close.
Server output:
Listening for client connection...Client connection accepted from [::ffff:10.2.6.179]:56275
Got 'This is a test message. Hello!
' from client
Echoing response
Done.
Press enter to close.
Sample extension methods providing the missing convenience functions in earlier versions of .NET:
static class IPAddressExtensions
{
public static IPAddress MapToIPv6(this IPAddress addr)
{
if(addr.AddressFamily != AddressFamily.InterNetwork)
throw new ArgumentException("Must pass an IPv4 address to MapToIPv6");
string ipv4str = addr.ToString();
return IPAddress.Parse("::ffff:" + ipv4str);
}
public static bool IsIPv4MappedToIPv6(this IPAddress addr)
{
bool pass1 = addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6, pass2;
try
{
pass2 = (addr.ToString().StartsWith("0000:0000:0000:0000:0000:ffff:") ||
addr.ToString().StartsWith("0:0:0:0:0:ffff:") ||
addr.ToString().StartsWith("::ffff:")) &&
IPAddress.Parse(addr.ToString().Substring(addr.ToString().LastIndexOf(":") + 1)).AddressFamily == AddressFamily.InterNetwork;
}
catch
{
return false;
}
return pass1 && pass2;
}
}

Categories