How to join source specific multicast group in C# (IGMPv3) - c#

We have a C# application that can join and receives data from a multicast group. This works well. We now want to support IGMPv3 and be able to specify the IP of the source when joining a multicast group. From the MSDN documentation, I don't see how to do this. I have found the following link that seems to answer my question.
http://social.msdn.microsoft.com/Forums/en/netfxnetcom/thread/e8063f6d-22f5-445e-a00c-bf46b46c1561
And here is how I implemented this:
byte[] membershipAddresses = new byte[12]; // 3 IPs * 4 bytes (IPv4)
Buffer.BlockCopy(multicastIp.GetAddressBytes(), 0, membershipAddresses, 0, 4);
Buffer.BlockCopy(sourceIp.GetAddressBytes(), 0, membershipAddresses, 4, 4);
Buffer.BlockCopy(localIp.GetAddressBytes(), 0, membershipAddresses, 8, 4);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, membershipAddresses);
But I get a SocketException when calling SetSocketOption() with this error: The requested address is not valid in its context.
Can someone points me what I am doing wrong here? Thanks!

The link states SocketOptionName.AddSourceMembership, you are using AddMembership.

For anyone struggling with source multicast
static void StartListner(IPAddress sourceIp, IPAddress multicastGroupIp, IPAddress localIp, int port)
{
Task.Run(() =>
{
try
{
Console.WriteLine("Starting: " + sourceIp + " - " + multicastGroupIp + " - " + localIp + " / " + port);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint localEndpoint = new IPEndPoint(localIp, port);
socket.Bind(localEndpoint);
byte[] membershipAddresses = new byte[12]; // 3 IPs * 4 bytes (IPv4)
Buffer.BlockCopy(multicastGroupIp.GetAddressBytes(), 0, membershipAddresses, 0, 4);
Buffer.BlockCopy(sourceIp.GetAddressBytes(), 0, membershipAddresses, 4, 4);
Buffer.BlockCopy(localIp.GetAddressBytes(), 0, membershipAddresses, 8, 4);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddSourceMembership, membershipAddresses);
while (true)
{
byte[] b = new byte[1024];
int length = socket.Receive(b);
Console.WriteLine("PORT: " + port + " : " + Encoding.ASCII.GetString(b, 0, length));
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
});
}

Related

TCP/IP CLIENT RECEIVE MULTIPLE MESSAGE

I need a help about my server. I have a program that sends a message over a socket with a TCP/IP connection and gets acknowledgment. My current problem is: I'm sending data to the machine I'm using for it to print. The machine confirms that it has received the data each time. But now it has to give feedback that it has printed the data after confirming that it has received the data. In the program I wrote, I cannot receive 2 messages in a row from the machine. What do I need to do to listen to multiple messages over Socket?
The code I use is below. Thanks for helps...
private void clientBaglanti(string ipAdres, int portNumarasi)
{
try
{
IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddr = IPAddress.Parse(ipAdres); //ipHost.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddr, portNumarasi);
soket = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
catch (Exception rt)
{
MessageBox.Show(rt.Message);
}
string sonMesaj;
sonMesaj = gonderilenText.ToCharArray().Aggregate("", (sonuc, c) => sonuc += ((!string.IsNullOrEmpty(sonuc) && (sonuc.Length + 1) % 3 == 0) ? " " : "") + c.ToString());
byte[] gonderilenVeri = sonMesaj.Split().Select(s => Convert.ToByte(s, 16)).ToArray();
byte b = checksumHesap(gonderilenVeri);
string txtChecksum = b.ToString("X2");
string toplamMesaj = sonMesaj + (" ") + txtChecksum;
byte[] gonderilenSonVeri = toplamMesaj.Split().Select(s => Convert.ToByte(s, 16)).ToArray();
int byteGonderilen = soket.Send(gonderilenSonVeri);
string stringGidenVeri = BitConverter.ToString(gonderilenSonVeri);
lstVtMakineHaberlesme.Items.Add("Gönderilen: " + stringGidenVeri);
byte[] cevap = new byte[1024];
int ht = soket.Receive(cevap);
string stringGelenVeri = BitConverter.ToString(cevap, 0, ht);
lstVtMakineHaberlesme.Items.Add("Gelen: " + stringGelenVeri);

Send broadcast with unknown port

Is it possible, to send a broadcast for searching a server application, if i don't know the port on which the server is running? Or do i have to check every port?
To send a simple broadcast, i found the following code at the internet:
Server
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
Console.Write("Running server..." + Environment.NewLine);
server.Bind(new IPEndPoint(IPAddress.Any, 48000));
while (true)
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
byte[] buffer = new byte[1000];
server.ReceiveFrom(buffer, ref tempRemoteEP);
Console.Write("Server got '" + buffer[0] + "' from " + tempRemoteEP.ToString() + Environment.NewLine);
Console.Write("Sending '2' to " + tempRemoteEP.ToString() +
Environment.NewLine);
server.SendTo(new byte[] { 2 },
tempRemoteEP);
}
Client
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint AllEndPoint = new IPEndPoint(IPAddress.Broadcast, 48000);
//Allow sending broadcast messages
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Broadcast, 1);
//Send message to everyone
client.SendTo(new byte[] { 1 }, AllEndPoint);
Console.Write("Client send '1' to " + AllEndPoint.ToString() +
Environment.NewLine);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
byte[] buffer = new byte[1000];
string serverIp;
try
{
//Recieve from server. Don't wait more than 3000 milliseconds.
client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 3000);
client.ReceiveFrom(buffer, ref tempRemoteEP);
Console.Write("Client got '" + buffer[0] + "' from " +
tempRemoteEP.ToString() + Environment.NewLine);
//Get server IP (ugly)
serverIp = tempRemoteEP.ToString().Split(":".ToCharArray(), 2)[0];
}
catch
{
//Timout. No server answered.
serverIp = "?";
}
Console.Write("ServerIp: " + serverIp + Environment.NewLine);
Console.ReadLine();
But I don't know, wehter my server use port 48000.
To send a broadcast:
var port = 123;
var udp = new UdpClient();
var data = new byte[] { 1, 2, 3 };
udp.Send(data, data.Length, new IPEndPoint(IPAddress.Any, port));
If you don't know the port, you have to try all ports, which I do not recommend, since you're spamming the network.
What are you trying to do?

Accept websocket use tcp .net very slow?

I created a .NET class socket using a websocket server. When my browser tries to connect to socket on my program, I see that the method 'accept socket' is called very slow or seconds after my browser connects. I tried creating many connections but the socket accepts a websocket every second. I tried alchemy websocket too, results like my code.
After I used Miscrosoft websocket on IIS8, I noticed that the speed is pretty good.
I am not experimental.
My code (express)
class Connection
{
byte[] buffer = new byte[1024];
Socket socket;
bool IsAuthencation;
public Connection(Socket socket)
{
this.socket = socket;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
}
void OnReceive(IAsyncResult result)
{
try
{
int count = socket.EndReceive(result);
if (count != 0)
{
if (!IsAuthencation)
{
byte[] tmp = new byte[count];
Array.Copy(buffer, 0, tmp, 0, count);
string Request = Encoding.UTF8.GetString(tmp);
if (Request.Contains("GET"))
{
if (!IsAuthencation)
{
int indexStart = Request.IndexOf("Sec-WebSocket-Key: ");
int indexEnd = Request.IndexOf("Sec-WebSocket-Version:");
string key = Request.Substring(indexStart + 19, indexEnd - indexStart - 21);
string magic = string.Concat(key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
string base64 = "";
using (SHA1 sha1 = SHA1.Create())
{
byte[] bufferMagic = sha1.ComputeHash(Encoding.UTF8.GetBytes(magic));
base64 = Convert.ToBase64String(bufferMagic);
}
Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+ "Connection: Upgrade" + Environment.NewLine
+ "Upgrade: websocket" + Environment.NewLine
+ "Sec-WebSocket-Accept: " + base64
+ Environment.NewLine
+ Environment.NewLine);
socket.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
IsAuthencation = true;
}
}
}
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
}
else
{
// disconnect
}
}
catch (Exception)
{
}
}
void OnSend(IAsyncResult result)
{
try
{
socket.EndSend(result);
}
catch (Exception)
{
}
}
}
class Program
{
static List<Connection> clients;
static Socket socket;
static void Main(string[] args)
{
clients = new List<Connection>();
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 30000);
socket.Bind(endPoint);
socket.Listen(0);
socket.BeginAccept(new AsyncCallback(AcceptAsync), null);
Console.ReadLine();
}
static void AcceptAsync(IAsyncResult result)
{
Console.WriteLine("a new connection");
Socket s = socket.EndAccept(result);
clients.Add(new Connection(s));
socket.BeginAccept(new AsyncCallback(AcceptAsync), null);
}
}
you can try command javascript var socket= new WebSocket("ws://localhost:30000"), i not see a problem in my code.
Thanks
You have to accept the socket, and handle it asynchronously, and then back to accepting more sockets straightaway.
UPDATE
After seeing your code, it seems you are accepting connections asynchronously. How are you creating those test connections? If you are creating too many you are maybe saturating the app.
I think I see the problem, why are you setting the socket backlog to 0? Read this answer from SO.
Try to put a bigger value: socket.Listen(100);

TCP IP Server with acknowledgement and receive data again

I have developed TCP/IP Server in C# listening on port and a GPS device is sending the data to server.
Initially, the device sends the IMEI number to server and server acknowledges with 01. After receiving the acknowledgement by device, a new packet of data is sent to server.
I am able to get IMEI number by TCP Server and after sending acknowledgement, i am not able to receive new packet data from client. I am including my code below also. Please let me know where i am wrong.
I have tested using hercules tcp server tool and next data packet is receiving successfully but from my application it is not working.
try
{
IPAddress ipAdress = IPAddress.Parse(ConfigurationManager.AppSettings["Server"].ToString());
// You can use local IP as far as you use the
//same in client
// Initializes the Listener
TcpListener myList = new TcpListener(ipAdress, int.Parse(ConfigurationManager.AppSettings["Port"].ToString()));
for (; ; )
{ // Run forever, accepting and servicing connections
//Start Listeneting at the specified port
myList.Start();
Console.WriteLine("Server running - Port: 8000");
Console.WriteLine("Local end point:" + myList.LocalEndpoint);
Console.WriteLine("Waiting for connections...");
Socket socket = myList.AcceptSocket();
// When accepted
Console.WriteLine("Connection accepted from " + socket.RemoteEndPoint);
byte[] b = new byte[1000];
int k = socket.Receive(b);
Console.WriteLine("echoed {0} bytes.", k);
Console.WriteLine("Reading IMEI....");
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
File.WriteAllText(#"C:\ClientFiles\testIMEI", hexStr);
ASCIIEncoding asen = new ASCIIEncoding();
string response = "01";
Console.WriteLine("Ackowledgement Data - " + response);
//
int sentBytes = socket.Send(asen.GetBytes(response));
Console.WriteLine("Acknowledgement data sent!\n");
int count = 0;
while (socket.Poll(-1, SelectMode.SelectRead))
{
b = new byte[1000];
k = socket.Receive(b);
if (k > 0)
{
count++;
Console.WriteLine("Reading Client Data - Count - " + count.ToString() + " and lenght " + k.ToString());
hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
File.WriteAllText(#"C:\ClientFiles\testData" + count.ToString(), hexStr);
}
}
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
myList.Stop();
}
catch (Exception ex)
{
File.WriteAllText(#"C:\ClientFiles\Error.txt", ex.Message + "****" + ex.InnerException);
}
I don't see any error in the program, except these comments:
// Don't use the b.Length in this command:
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, b.Length));
// use k instead, the numbers actually read, not the length of the buffer.
string hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, k));
while (socket.Poll(-1, SelectMode.SelectRead))
{
// b has been allocated, don't need this
// b = new byte[1000];
k = socket.Receive(b);
if (k > 0)
{
count++;
Console.WriteLine("Reading Client Data - Count - " + count.ToString() + " and lenght " + k.ToString();
// use k not Length
hexStr = ConvertAsciiToHex(Encoding.ASCII.GetString(b, 0, k /*b.Length*/));
File.WriteAllText(#"C:\ClientFiles\testData" + count.ToString(), hexStr);
}
}
Do you know if the Poll command ever returns? May be it raises an error and you are missing that one. Check it out.

Troubleshooting a simple proxy server

I've written a simple proxy server in C# using synchronous methods and classes of System.Net and System.Net.Sockets. Most of the things are done, except one minor glitch.
When I browse a url from the proxy client (say www.google.com), it opens fine. When I do a keyword search that too goes fine. However, when I click on a search-result, the client again sees the google.com home-page on the screen!! However, the client browser url bar is still showing the search-result url which is clicked.
Perhaps this is happenning because google result page doesn't provide a direct link to the result url, but a link back to google itself, and I think the relay logic in my code is not able to handle that. Could you help me with this? My code is as follows:
(The ThreadHandleClient() method is the important one that handles all the client requests and relays requests and responses)
public void Start(IPAddress ip, int port)
{
try
{
TcpListener listener = new TcpListener(ip, port);
listener.Start(100);
while (!stopFlag)
{
Socket client = listener.AcceptSocket();
IPEndPoint rep = (IPEndPoint)client.RemoteEndPoint;
Thread th = new Thread(ThreadHandleClient);
th.Start(client);
}
listener.Stop();
}
catch (Exception ex)
{
Debug.Print("START: " + ex.Message);
}
}
public void Stop()
{
stopFlag = true;
}
public void ThreadHandleClient(object o)
{
try
{
Socket client = (Socket)o;
Debug.Print("lingerstate=" + client.LingerState.Enabled.ToString() + " timeout=" + client.LingerState.LingerTime.ToString());
NetworkStream ns = new NetworkStream(client);
//RECEIVE CLIENT DATA
byte[] buffer = new byte[2048];
int rec = 0, sent = 0, transferred = 0, rport = 0;
string data = "";
do
{
rec = ns.Read(buffer, 0, buffer.Length);
data += Encoding.ASCII.GetString(buffer, 0, rec);
} while (rec == buffer.Length);
//PARSE DESTINATION AND SEND REQUEST
string line = data.Replace("\r\n", "\n").Split(new string[] { "\n" }, StringSplitOptions.None)[0];
Uri uri = new Uri(line.Split(new string[] { " " }, StringSplitOptions.None)[1]);
Debug.Print("CLIENT REQUEST RECEIVED: " + uri.OriginalString);
if (uri.Scheme == "https")
{
rport = 443;
Debug.Print("HTTPS - 443");
//rq = HttpVersion + " 200 Connection established\r\nProxy-Agent: Prahlad`s Proxy Server\r\n\r\n";
//ClientSocket.BeginSend(Encoding.ASCII.GetBytes(rq), 0, rq.Length, SocketFlags.None, new AsyncCallback(this.OnOkSent), ClientSocket);
}
else
{
rport = 80;
Debug.Print("HTTP - 443");
}
IPHostEntry rh = Dns.GetHostEntry(uri.Host);
Socket webserver = new Socket(rh.AddressList[0].AddressFamily, SocketType.Stream, ProtocolType.IP);
webserver.Connect(new IPEndPoint(rh.AddressList[0], rport));
byte[] databytes = Encoding.ASCII.GetBytes(data);
webserver.Send(databytes, databytes.Length, SocketFlags.None);
Debug.Print("SENT TO SERVER. WILL NOW RELAY: " + data);
//START RELAY
buffer = new byte[2048];
bool firstTime = true;
rec = 0;
data = "";
do
{
transferred = 0;
do
{
if (webserver.Poll((firstTime ? 9000 : 2000) * 1000, SelectMode.SelectRead))
{
rec = webserver.Receive(buffer, buffer.Length, SocketFlags.None);
Debug.Print("RECEIVED FROM WEBSERVER[" + rec.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
firstTime = false;
sent = client.Send(buffer, rec, SocketFlags.None);
Debug.Print("SENT TO CLIENT[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
transferred += rec;
}
else
{
Debug.Print("No data polled from webserver");
}
} while (rec == buffer.Length);
Debug.Print("loop-1 finished");
//if (transferred == 0)
// break;
//transferred = 0;
rec = 0;
do
{
if (client.Poll(1000 * 1000, SelectMode.SelectRead))
{
rec = client.Receive(buffer, buffer.Length, SocketFlags.None);
Debug.Print("RECEIVED FROM CLIENT: " + Encoding.ASCII.GetString(buffer, 0, rec));
sent = webserver.Send(buffer, rec, SocketFlags.None);
Debug.Print("SENT TO WEBSERVER[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
transferred += rec;
}
else
{
Debug.Print("No data polled from client");
}
} while (rec == buffer.Length);
Debug.Print("loop-2 finished");
} while (transferred > 0);
Debug.Print("LOOP ENDS. EXITING THREAD");
client.Close();
webserver.Close();
}
catch (Exception ex)
{
Debug.Print("Error occured: " + ex.Message);
}
finally {
Debug.Print("Client thread closed");
}
}
After some trial and error, I found the solution, hence I'm answering my own question. The issue with my relay logic was that it used to relay both ways i.e. from webserver->client and client->webserver.
I realized that relaying the second part could lead to trouble in redirects, because in redirects the url part of the header ("GET http://debian.org HTTP/1.1") would suddenly change, so that it will be different from the web-server being relayed!! For example, the google redirect pointed to debian.org, whereas the proxy will still relay traffic from google.com and not debian.org.
To avoid this situation, I removed the second relay part (i.e. client->webserver). If a clients sends a new request, it better be handled in a separate thread, so that it would be directed to the proper url host. So, all I had to do was comment out the second inner loop in my relay logic:
//rec = 0;
//do
//{
// if (client.Poll(3000 * 1000, SelectMode.SelectRead))
// {
// rec = client.Receive(buffer, buffer.Length, SocketFlags.None);
// Debug.Print("RECEIVED FROM CLIENT: " + Encoding.ASCII.GetString(buffer, 0, rec));
// sent = webserver.Send(buffer, rec, SocketFlags.None);
// Debug.Print("SENT TO WEBSERVER[" + sent.ToString() + "]: " + Encoding.ASCII.GetString(buffer, 0, rec));
// transferred += rec;
// }
// else
// {
// Debug.Print("No data polled from client");
// }
//} while (rec == buffer.Length);
//Debug.Print("loop-2 finished");

Categories

Resources