In my adaption of a few multicast tutorials, I changed my method of sending packets.
In my case, I create a queue of packets of size bytes[1024], after which it is sent via Socket.Send() over all online network adapters
Problem:
When sending, another method called receiveMessage() is running on a separate thread. Using Socket.Bind(), it captures packets, but only 1 packet is captured.
Am I doing too much processing thus delaying (and losing) the packets received?
UPDATE
I am considering adding a queue for incoming packets, thus 1 threads captures and stores, the second processes the packets.
SendMethod
Socket _listener_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
foreach (IPAddress localIP in Dns.GetHostAddresses(Dns.GetHostName()).Where(i => i.AddressFamily == AddressFamily.InterNetwork))
{
//handle image and files
_listener_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(_MultiIP, localIP));
_listener_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 1);
_listener_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_listener_socket.MulticastLoopback = true;
_listener_socket.Connect(new IPEndPoint(_MultiIP, _PORT));
int count = MSGS_TO_SEND.Count;
while (count > 0)
{
count--;
byte[] temp = (byte[])(MSGS_TO_SEND.Dequeue());
_listener_socket.Send(temp, _BYTE_BUFFER_SIZE, SocketFlags.None);
MSGS_TO_SEND.Enqueue(temp);
}
//----------------------------------------------
//-------------------SEND DATA------------------
//----------------------------------------------
}
_listener_socket.Close();
Receive method
//initialise multicast group and bind to interface
Socket _sender_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, _PORT);
_sender_socket.Bind(ipep);
IPAddress localip = _MultiIP;
_sender_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(localip, IPAddress.Any));
//{
while (_sender_socket.IsBound && !bStop)
{
byte[] b = new byte[_BYTE_BUFFER_SIZE];
_sender_socket.Receive(b);
char[] chars = new char[_BYTE_BUFFER_SIZE];
System.Buffer.BlockCopy(b, 0, chars, 0, b.Length);
string _message = new string(chars).Trim();
string ip = _message.Substring(0, _message.IndexOf("~"));
_message = _message.Remove(0, _message.IndexOf("~") + 1);
string _flag = _message.Substring(0, _message.IndexOf("~"));
_message = _message.Remove(0, _message.IndexOf("~") + 1);
_message = _message.Replace("\0", string.Empty);
ip = "1.0";
icount++;
handleData(ip, _flag, _message);
}
If you wan't to try, here is a fully working example: https://github.com/efaruk/playground/tree/master/Multicasting
You can use those re-usable components (MulticastSender, MulticastReceiver,) in your project if you whish...
The sender doesn't have to bind or add membership. Any datagram socket can send to a multicast address. The receiver needs to add membership to the multicast address. The sender and receiver also need to agree upon the port (receiver should be bound to the port.)
Your Multicast Options in each example have the arguments reversed.
The sockets already have buffering from the kernel, so you don't need to complicate your code with message queues.
Related
I am having a issue I have a UDP socket open on port 49473 while one ip address is being recognized another is not. I see in wireshark packets coming from 18.x.x.x and 24.x.x.x only the 18.x.x.x packets are being read. Any ideas what might be happing? (Side note 18.x.x.x is not traversed but 24.x.x.x is not sure if that makes a difference..) Also I have tried increasing the buffer from 8192 to 150k it did not help. The buffer is not dropping packets so that ones out unfortunately..
Image from wireshark click to enlarge
public UdpSocket(int port = 0)
{
sw.Start();
_buffer.Client = this;
if (port != 0)
_buffer.IsHost = true;
_discovery = new Discovery(_buffer);
_buffer.NatOp = new NatOperation(_buffer, _buffer.IsHost);
_endPoint = new IPEndPoint(IPAddress.Any, port);
_buffer.Stream = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_buffer.Stream.ReceiveBufferSize = 8192;
_buffer.Stream.SendBufferSize = 8192;
_buffer.Stream.EnableBroadcast = true;
//***********Magic Fairy Dust*************
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
_buffer.Stream.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
//*********** Fairy Dust End *************
_buffer.Stream.Bind(_endPoint);
Thread receive = new Thread(ReceiveUdp);
Thread send = new Thread(SendThread);
receive.IsBackground = true;
send.IsBackground = true;
receive.Start();
send.Start();
}
private void ReceiveUdp()
{
while (IsRunning)
{
try
{
Packet packet = _buffer.CreatePacket();
EndPoint _ep = new IPEndPoint(IPAddress.Any, 0);
count = _buffer.Stream.ReceiveFrom(data, 1024, SocketFlags.None, ref _ep);
if (((IPEndPoint)_ep).Address.ToString() != "18.x.x.x")
Console.WriteLine("Foreign Address Connection " + packet.RemoteEp.Address.ToString()); //never returns so 18.x.x.x only processing
packet.Data.MirrorData(count, data);
packet.RemoteEp = _ep as IPEndPoint;
packet.ReadHeader();
lock (_inProcess)
_inProcess.Enqueue(packet);
}
catch { }
}
}
For anyone reading this SendTo and ReceiveFrom creates ICMP responses the magic fairy dusts disables that, this solution only works on Windows / Linux.
Also I accepted the SocketOptions IP unrestricted as correct.. In my case Socket.Connect was the issue, when you do this it only allows that ip to make transfers.
This may be a NAT related problem. Do a try
_buffer.Stream.SetIPProtectionLevel(IPProtectionLevel.Unrestricted);
before or straight after Bind. UDPClient does exactly that by method AllowNatTraversal.
Or this may be a firewall/AV related issue. Turn them off temporarly and get a try.
When I start multiple instances of application listening on the same UDP port, only the last receives datagrams. Is there any way to send data to all instances other than sending broadcast (broadcasts are received in all instances)?
My application (commandline):
UdpClient client = new UdpClient();
client.Client.SetSocketOption(
SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.Client.Bind(new IPEndPoint(0, 7894));
client.EnableBroadcast = true;
Console.WriteLine("Press key to send datagram");
while (true)
{
if (client.Available > 0)
{
IPEndPoint ep = null;
client.Receive(ref ep);
Console.WriteLine("Received");
}
if (Console.KeyAvailable)
{
Console.ReadKey();
client.Send(new byte[] { 1 }, 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7894));
Console.WriteLine("Sent");
}
}
A port can only been owned by one application/process at a time, so a datagram sent to that port can only be received by one instance.
A clean way would be for each listener to use an ephemeral port and register it with the sender application.
Good day all
This is a piece of code which is part of a small lan chat (expermentation) program, however when sending messages to this, the server, it is recieved (as per debugging I can see the results) but the gui fails to update (gui meaning richedit and label)
When I click send, the receiveText(string IP) is called, which executes and awaits for a connection, when it recieves a connetion, it handles as it should but,
Problem :
Only after this method (and related) have been executed, the gui updates, so when this method finishes, and its calling method finishes (button_click) too, only then while the program is waiting for input, they richedit displays the messages and the label is updated
I tried w/ and w/o the while loops, both have the same result
private void recieveText(string _IPADDRESS)
{
//initialise multicast group and bind to interface
Socket _listener_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, _PORT);
_listener_socket.Bind(ipep);
IPAddress localip = IPAddress.Parse("224.5.6.7");
_listener_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(localip, IPAddress.Any));
//recieve data to multicast group
//while (true)
//{
//while (_listener_socket.Connected)
//{
label1.Text = "listening...";
byte[] b = new byte[1024];
_listener_socket.Receive(b);
label1.Text = "message recieved";
redBox.AppendText("\n---------------------------------\n New Message :\n");
char[] chars = new char[b.Length / sizeof(char)];
System.Buffer.BlockCopy(b, 0, chars, 0, b.Length);
string t = new string(chars).Trim();
redBox.AppendText(t);
redBox.AppendText("\n----------------------------------\n\n");
//}
//}
}
In order to see updates and don't block the main thread you must receive the data in a separated thread.
Here is an example:
private void recieveText(string _IPADDRESS)
{
//initialise multicast group and bind to interface
Socket _listener_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, _PORT);
_listener_socket.Bind(ipep);
IPAddress localip = IPAddress.Parse("224.5.6.7");
_listener_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(localip, IPAddress.Any));
ThreadPool.QueueUserWorkItem((o) =>
{
BeginInvoke((Action)(() => { label1.Text = "listening..."; }));
while (_listener_socket.Connected)
{
byte[] b = new byte[1024];
_listener_socket.Receive(b);
char[] chars = new char[b.Length / sizeof(char)];
System.Buffer.BlockCopy(b, 0, chars, 0, b.Length);
string t = new string(chars).Trim();
BeginInvoke((Action)(() =>
{
label1.Text = "message recieved";
redBox.AppendText("\n---------------------------------\n New Message :\n");
redBox.AppendText(t);
redBox.AppendText("\n----------------------------------\n\n");
}));
}
});
}
This is the example using different threads as you requested BUT this is not the best way to do it, as I said in my comment the right way is to use the asynchronous methods, the old ones (BeginXXX/EndXXX) or the new async ones (XXXAsync).
I try to send message to all members of the group. Sender:
// Create socket
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Multicast IP-address
IPAddress ip = IPAddress.Parse("224.168.55.25");
// Join multicast group
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ip));
// TTL
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
// Create an endpoint
IPEndPoint ipep = new IPEndPoint(ip, 4567);
// Connect to the endpoint
s.Connect(ipep);
// Scan message
while (true)
{
byte[] buffer = new byte[1024];
string msg = Console.ReadLine();
buffer = Encoding.ASCII.GetBytes(msg);
s.Send(buffer, buffer.Length, SocketFlags.None);
if (msg.Equals("Bye!", StringComparison.Ordinal))
break;
}
// Close socket
s.Close();
Receiver:
// Create new socket
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Create IP endpoint
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 4567);
// Bind endpoint to the socket
s.Bind(ipep);
// Multicast IP-address
IPAddress ip = IPAddress.Parse("224.168.55.25");
// Add socket to the multicast group
s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ip, IPAddress.Any));
// Receive messages
while (true)
{
byte[] data = new byte[1024];
s.Receive(data);
string str = System.Text.Encoding.ASCII.GetString(data, 0, data.Length);
Console.WriteLine(str.Trim());
if (str.Equals("Bye!", StringComparison.Ordinal))
{
break;
}
}
I don't uderstand why there is a lot of free space between messages when I print them to screen on the client side? Why loop in the Receiver program doesn't stop after receiveing message Bye!? How can I fix this problems?
Thank you very much!
You are creating a udp socket. Udp sockets are not connection oriented. So it just receives messages and has no idea about the state of the sender. Even if the sender socket closes, the receiver would keep on listening.
I hope I have understood your question correctly.
if (strData.Trim().Equals("Bye!", StringComparison.Ordinal))
{
Console.WriteLine("that's right");
break;
}
Socket.Receive() return the bytes received you should use the return value to generate the string, or you will get a string with the length of 1024 and Trim cannot handle it.
int len = s.Receive(data);
string str = System.Text.Encoding.ASCII.GetString(data, 0, len);
I'm working with sockets in C# and I'm getting the following error:
A request to send or receive data was
disallowed because the socket is not
connected and (when sending on a
datagram socket using a sendto call)
no address was supplied
Here is the code that I'm executing:
private void HostSubscriberService()
{
IPAddress ipV4 = PublisherService.ReturnMachineIP();
var localEP = new IPEndPoint(ipV4, _port);
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(localEP);
StartListening(server);
}
private void StartListening(Socket server)
{
EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
var data = new byte[1024];
int recv = server.ReceiveFrom(data, ref remoteEP);
string messageSendFromClient = Encoding.ASCII.GetString(data, 0, recv);
MessageBox.Show(messageSendFromClient);
}
}
The error happens # int recv = server.ReceiveFrom(data, ref remoteEP);
I just need to listen for new incoming connections and then print the message that was sent from the new client.
I need it to work on the TCP protocol, because some of the messages will be > 1500 bytes
Thanks!
You need to .BeginAccept() before you can receive.
Here's a link with a sinple Asynchronous Socket Server example
ReceiveFrom() is for UDP, use Accept() and Receive() for TCP