I was looking for the best solution to receive and process messages via UdpClient class in C#. Does anyone have any solutions for this?
Try this code :
//Client uses as receive udp client
UdpClient Client = new UdpClient(Port);
try
{
Client.BeginReceive(new AsyncCallback(recv), null);
}
catch(Exception e)
{
MessageBox.Show(e.ToString());
}
//CallBack
private void recv(IAsyncResult res)
{
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 8000);
byte[] received = Client.EndReceive(res, ref RemoteIpEndPoint);
//Process codes
MessageBox.Show(Encoding.UTF8.GetString(received));
Client.BeginReceive(new AsyncCallback(recv), null);
}
For newer methods using TAP instead of Begin/End method you can use the following in .Net 4.5
Quite simple!
Asynchronous Method
private static void UDPListener()
{
Task.Run(async () =>
{
using (var udpClient = new UdpClient(11000))
{
string loggingEvent = "";
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var receivedResults = await udpClient.ReceiveAsync();
loggingEvent += Encoding.ASCII.GetString(receivedResults.Buffer);
}
}
});
}
Synchronous Method
As appose to the asynchronous method above, this can be also implemented in synchronous method in a very similar fashion:
private static void UDPListener()
{
Task.Run(() =>
{
using (var udpClient = new UdpClient(11000))
{
string loggingEvent = "";
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
var receivedResults = udpClient.Receive(ref remoteEndPoint);
loggingEvent += Encoding.ASCII.GetString(receivedResults);
}
}
});
}
I can recommend two links about this solution which were helpful for me.
PC Related
Stack Overflow
The first one is really easy solution but be careful at modifying because the continual receiving will work only if some UDP packet is sent as first to the "remote" device.
For continual listening add the code line "udp.BeginReceive(new AsyncCallback(UDP_IncomingData), udp_ep);" after each reading of incomming data to enable new receiving of UDP packets.
The second one is nice solution for using the Multicast IP-addresses (239.255.255.255 - 240.0.0.0)
Related
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
So I'm very interested in learning more sophisticated programming techniques and languages, and I've decided to further my study into C# by learning sockets and server programming. I stumbled upon a video that i bet a lot of you have already seen or heard of, and it's, in my opinion, pretty good at explaining most of what's going on.
So my problem is specifically with essentially taking this code and making it a chat system that takes info from a client, sends it to the server, then from the server to ALL clients. I've looked up any tutorials and other stack overflow pages to no avail, at least to me. I can't seem to figure it out, and when someone has an answer they've simply written "Found a solution" and more or less left it at that.
I am aware of this thread on SO, and I've tried what's been posted in there to no avail. I've also done my fair share of searching through google and whatnot, and with being new to sockets and all that, it's definitely not the easiest thing.
The Actual Problem
Here's my code for the server:
using static AppNameHere.ChatCommands;
namespace AppNameHere
{
public partial class Host : Form
{
public static Host host = null;
private static string response;
private static byte[] _buffer = new byte[1024];
private static List<Socket> _ClientSk = new List<Socket>();
private static Socket _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public Host()
{
InitializeComponent();
}
private void Host_Load(object sender, EventArgs e)
{
SetupServer();
}
private static void SetupServer()
{
_Socket.Bind(new IPEndPoint(IPAddress.Any, HostJoinSelect.portSelected));
_Socket.Listen(HostJoinSelect.playerTotal + 2);
_Socket.BeginAccept(new AsyncCallback(AccCallback), null);
}
private static void AccCallback(IAsyncResult iar)
{
Socket s = _Socket.EndAccept(iar);
_ClientSk.Add(s);
Console.WriteLine("Client Connected.");
s.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(RecCallback), s);
_Socket.BeginAccept(new AsyncCallback(AccCallback), null);
}
private static void RecCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
int received = s.EndReceive(iar);
byte[] dataBuf = new byte[received];
Buffer.BlockCopy(_buffer, 0, dataBuf, 0, received);
string t = Encoding.ASCII.GetString(dataBuf);
foreach (Socket socket in _ClientSk)
{
response = ChatCommandParser(t);
byte[] data = Encoding.ASCII.GetBytes(response);
socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(RecCallback), socket);
}
}
private static void SendCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
s.EndSend(iar);
}
}
}
The using at the top is just a class I made for converting text to something else, so it's no obstruction. What really rustles my jimmies is the foreach loop that's, in theory, supposed to make it work, and it makes sense as it should be going through all the clients and sending them all messages, but if a client sends a message, nothing happens on the other clients. Only the original client gets the message back.
So both debugging and troubleshooting have shown that it runs through BeginSend and such, but it still only sends a message to that same client that i typed into which is just weird.
Thanks in advance, and please, if you have an answer then explain it to me cause I'm still learning, so a fix and an explanation would be nice.
P.S. Yes I know I'm using TCP and not UDP.
EDIT: The code I used for my client is in this pastebin link: https://pastebin.com/4WicEu2x
EDIT 2: I have a feeling the main issue is that in my client code, I have the client listening for messages only AFTER it sends a message. If this is it, then i'm dumber than a rock and would like to thank anyone who posted here telling me how to improve my program.
EDIT 3: What I wrote in my second edit was correct. If you're having an issue and you've followed the same guide I did in that youtube video, then just know that you should have Asynchronous Receiving in your client-side program as well as your server. I didn't think too much of the client side, but always check your code! Here's my edited client-side code (yes i know it needs LOTS of work):
namespace AppNameHere
{
public partial class Join : Form
{
public static Join join = null;
private static Socket _ClientSk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static byte[] recBuf = new byte[1024];
public Join()
{
InitializeComponent();
}
private void Join_Load(object sender, EventArgs e)
{
}
private void Join_FormClosed(object sender, FormClosedEventArgs e)
{
if (HostJoinSelect.hjs != null)
{
HostJoinSelect.hjs.Show();
}
else
{
HostJoinSelect.hjs = new HostJoinSelect();
HostJoinSelect.hjs.Show();
}
}
private void Join_Shown(object sender, EventArgs e)
{
LoopConnect();
}
private static void Send()
{
string req = join.textBox1.Text;
byte[] buffer = Encoding.ASCII.GetBytes(req);
_ClientSk.Send(buffer);
}
private static void ConnectedCallback()
{
_ClientSk.BeginReceive(recBuf, 0, recBuf.Length, SocketFlags.None, new AsyncCallback(ReceivedCallback), _ClientSk);
}
private static void ReceivedCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
int rec = s.EndReceive(iar);
byte[] dataBuf = new byte[rec];
Buffer.BlockCopy(recBuf, 0, dataBuf, 0, rec);
string q = Encoding.ASCII.GetString(dataBuf);
join.Invoke(new MethodInvoker(delegate () {
join.listBox1.Items.Add(Form1.namesave + ": " + q);
join.listBox1.TopIndex = join.listBox1.Items.Count - 1;
}));
s.BeginReceive(recBuf, 0, recBuf.Length, SocketFlags.None, new AsyncCallback(ReceivedCallback), s);
}
private static void LoopConnect()
{
int attempts = 0;
while (!_ClientSk.Connected)
{
if (attempts < 4)
{
try
{
attempts++;
if (attempts <= 4)
{
_ClientSk.Connect(HostJoinSelect.IPSelectedJoin, HostJoinSelect.portSelectedJoin);
Console.WriteLine("Connected");
ConnectedCallback();
}
else
{
attempts = 0;
break;
}
}
catch (SocketException s)
{
if (attempts <= 4)
{
Console.WriteLine(s.Message + " | Connection attempts: " + attempts.ToString());
}
else
{
attempts = 0;
break;
}
}
}
else
{
MessageBox.Show("You failed to connect to " + HostJoinSelect.IPSelectedJoin + ":" + HostJoinSelect.portSelectedJoin + ", please ensure you have a means of connecting to this address.");
join.Close();
break;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Send();
}
}
}
I changed the code from accepting messages only after it sent one itself to having an asynchronous BeginReceive to take in data sent by the server. Always review your code and logic!
Besides the fact that your code requires some fixes it still works. I executed it in my test project and it worked as you want (almost, short of concurrency issues).
Required concurrency fixes:
As Emir Curtovic mentioned you shouldn't use a common buffer. Use local variable instead. If you want you can utilize the same buffers between connections using ThreadLocal or AsyncLocal class-level fields. It will be both thread-safe and reduce a heap fragmentation hazard.
You cannot just use List for sockets. It is not thread safe. There are at least two possible fixes: a) use a lock {} around the _ClientSk.Add(s) call or b) use concurrent collections.
If your client connections come simultaneously the last bug may cause the behavior that you described. However, there is also a possibility that your client code has some issues as well. If after introducing the fixes you still experience the issue please post your client code as well.
In general, there is more work to be done with your code. For example you should handle client disconnections. Currently it will just throw exceptions on int received = s.EndReceive(iar) call any time your client decides to close the connection. Besides handling the exception you will need to add to your protocol some way to perform a graceful connection closing. For example, by handling EOF symbol. Here you will find a nice code sample from MSFT.
I'm new with socket and trying to write a Client-Server application
My applicationhas those two main methods :
SERVER running on separate Thread :
public void socketListener()
{
byte[] StreamMessage = new byte[9632];
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndPoint =new IPEndPoint(IPAddress.Any , ControlLayer.GlobalParam.PEER2PEER_PORT);
listener.Bind(localEndPoint);
listener.Listen(10);
while (true)
{
Socket Handler = listener.Accept();
//int ByteRec = Handler.Receive(StreamMessage);
int MessageLength;
MessageLength = Handler.Receive(StreamMessage, 0, StreamMessage.Length, SocketFlags.None);
//return MessageLength;
// string message = System.Text.Encoding.Default.GetString(StreamMessage);
string message = System.Text.Encoding.UTF8.GetString(StreamMessage);
OnDataRecievedFromRemotePeer(this, message, "TcpServer");//send data to screen
Task.Run(() => { ParseMessage(message, Handler); });
}
}
once data arrives I prase it collect data and send it using Client
CLIENT :
public void Write(string message)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CreateClient), message);
}
private void CreateClient(object message)
{
try
{
peerClient = new TcpClient();
peerClient.Connect(remoteIP, 6001/*TODO remove this */);
netStream = peerClient.GetStream();//<- Exception
StreamWriter sw = new StreamWriter(netStream);
sw.Write((string)(message));
netStream.Close();
peerClient.Close();
}
catch(Exception ex)
{
//TODO :
}
}
Each station is symmetrical and have those two methods
I can tell that the server is working and accepting socket and data
but once I want to respond back I get exception in the Line marked in the CreateClient
stream was not writable and when looking on the netStream it is written that I have ObjectDisposed Exception .
What can be the cause of that ?
Also please inform me if more code is needed
You have a classical race here between the server closing the connection before the client has processed the response of the server.
TCP is a "polite" protocol, which means that you can not perform a fire and forget action on the server. The connection needs to be alive on both ends until both sides have processed all messages. Thus either the client needs to send an acknowledge/logout, so that the server can close the connection or at least the server has to wait x seconds until closing it.
I've decided to take a look at network messaging etc and my first port of call was UDP.
The problem i have is when i attempt to send a message. I'm trying to hit an IP on a specifc port, but the application errors with the error
"SocketException An existing connection was forcibly closed by the remote host".
Here is the code.
User ME = new User();
UdpClient MyUDPClient;
private void Form1_Load(object sender, EventArgs e)
{
ME.Username = Environment.UserName;
}
private void StartUDP_Click(object sender, EventArgs e)
{
CreateUDPClient();
}
private void CreateUDPClient()
{
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
int Port = int.Parse(txt_Port.Text);
ME.UserIP = new IPEndPoint(ip, Port);
break;
}
}
MyUDPClient = new UdpClient(ME.UserIP);
UDPListening();
}
public void UDPListening()
{
MyUDPClient.BeginReceive(ReceiveMessage, new object());
}
private void ReceiveMessage(IAsyncResult IAR)
{
byte[] B = MyUDPClient.EndReceive(IAR, ref ME.UserIP);
ProcessMSG(Encoding.ASCII.GetString(B));
UDPListening();
}
delegate void MessageDelegate(String MSG);
public void ProcessMSG(String M)
{
if (this.lbl_Messages.InvokeRequired)
{
MessageDelegate Del = new MessageDelegate(ProcessMSG);
this.Invoke(Del, M);
}
else
{
lbl_Messages.Text = M;
}
}
//Send Data to Another version of this program elsewhere.
private void btn_SendtoTarget_Click(object sender, EventArgs e)
{
IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse(txt_Send2IP.Text),int.Parse(txt_Send2Port.Text));
byte[] Message = Encoding.ASCII.GetBytes("TEST TEST TEST");
MyUDPClient.Send(Message, Message.Length, TargetIP);
}
}
thanks for the help.
Not sure if this helps but...
The Application is running on machine which is set to listen is on 192.168.0.25:5555
the Send is trying to send to Machine 192.168.0.50:10001
T
From further reading, I think my specific issue is the creation of the UDPclient object. It is created and is then listening on the ip 192.168.0.25:5555. When i attempt to send a message I'm attempting to use the same UDPClient but sending to a new IP. I'm getting the vibe that this is not the correct procedure and thus its trying to close the previous down??? I'm sure someone can comfirm this. So that would suggest to me that to have effective UDP networking (UP and Down) i'd need to have a UDPclient receiving and a second UDP to be able to send (which is dynamic to each target address i want to hit). Once again this is all guess work, and if i have this i hope someone could provide some pointers.
Several problems above.
Don't use ME in C#, it is this. Me is VB. The code above would not compile, as written, if using C#, since you left out your User() class you define as ME. I am not sure why you think you need that, since all it seems to hold is UserIP and UserName. The IP is part of a Socket, so you should just use that if you ever needed it:
IPAddress ip = IPAddress.Parse(socket.RemoteEndPoint);
And UserName could be made a global string variable - doesn't have to be part of a class.
Maybe I'm being nitpicky, but I didn't see where it was required to have either of those variables in the User() object like that and just mucked things up, for me, trying to decipher the code.
Always define your Socket, connect to it, then do your .Send(). You are missing the first 2 steps in your button click - you just do your .Send(). UdpClient is not a Socket, and I think it's better to do this as a Socket and define your connection type later. I think you could be right that you are trying to do your .Send() using the UdpClient you defined as your listener. Don't use the same object for your send and your listening! Normally you would, but you have 2 different addresses and ports for each event.
Don't use a foreach in your CreateUDPClient() function to get your IPAddress to assign to your IPEndPoint. You already knew your IP. Assign it directly. It's wasting processing time.
IPAddress ip = IPAddress.Parse("192.168.0.25");
IPEndPoint endPoint = new IPEndPoint(ip, port);
If you only had the host name, you would do:
string hostName = "MyComputerName";
int port = 5555; // or int.Parse(txt_Port.Text); ???
IPHostEntry hostEntry = Dns.GetHostAddresses(hostName);
IPEndPoint endPoint = new IPEndPoint(hostEntry[0], port);
Don't use Dns.GetHostEntry() -- if there's no reverse-lookup (PTR) record for that name, it will fail. Use Dns.GetHostAddresses(). And I have no idea why you think you needed the loop unless you have IPv6 addresses for the same host name you were providing. If not, just use [0] - it should be the first and only IP that will be returned that you'll be using if not using IPv6. But again, since you already have the IP, just plug that in - no need to do the lookup. It helps you eliminate Dns.GetHostName(), too - just use 192.168.0.25.
For your reference, MSDN has a procedure for your synchronous socket send/receive here:
https://msdn.microsoft.com/en-us/library/kb5kfec7(v=vs.110).aspx
and asynchronous socket send/receive here:
http://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx
Since I despise link only answers, and you seem to be mixing these 2 methods by using Send() and EndReceive(), respectively to the links above, I will endeavor to describe its contents succinctly and help fix your code:
Basically they say to use a StateObject class, instead of the global variable MyUDPClient you have:
public class StateObject
{
public byte[] buffer = new byte[1024];
public Socket workSocket;
public StringBuilder sb = new StringBuilder();
}
You would create a socket and add it to that. Buffer serves as a way to tell the Receive() or EndReceive() the size of the chunk you want to read back from the response at one time, and sb will serve as a placeholder for the response.
It looks like you have a lot going on here: a SendtoTarget_Click to do a one-off test from the form, and your listener. Your SendtoTarget button does a synchronous send, your StartUDP_Click() does an asynchronous receive.
You would change your SendtoTarget_Click() event to be this (which never defined your socket, before, and you must connect to it before sending):
private void btn_SendtoTarget_Click(object sender, EventArgs e)
{
IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse(txt_Send2IP.Text),int.Parse(txt_Send2Port.Text));
byte[] Message = Encoding.ASCII.GetBytes("TEST TEST TEST");
// Create a UDP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Udp);
try
{
// Connect to the remote endpoint.
sender.Connect(TargetIP);
// Send message -- already contains the endpoint so no need to
// specify again
sender.Send(Message, 0, Message.Length, SocketFlags.None);
sender.Close();
}
catch (Exception)
{
// do something here...
}
}
For your listener, you can do:
private void CreateUDPClient()
{
IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse("192.168.0.25"), 5555);
// Create a UDP socket.
Socket receiver = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Udp);
try {
// Create the state object.
StateObject state = new StateObject();
state.workSocket = receiver;
// Begin receiving the data from the remote device.
receiver.BeginReceive(state.buffer, 0, 256, 0,
new AsyncCallback(ReceiveMessage), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
and your function called ReceiveMessage(), does this:
private void ReceiveMessage(IAsyncResult IAR)
{
string response = String.Empty;
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject) IAR.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(IAR);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, 256, 0,
new AsyncCallback(ReceiveCallback), state);
} else {
// All the data has arrived; put it in response.
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
// Signal that all bytes have been received.
client.Close();
}
ProcessMSG(response);
CreateUDPClient(); // personally, I would re-create than calling BeginReceive directly
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Did my best to integrate your code with MSDN's in a way that should work. You might be able to get away with assigning that socket to the StateObject and calling BeginReceive() on it, again - not sure. But don't re-use the UdpClient object like that. Use the StateObject class like on MSDN and use it ONLY as your listener.
I want to preface this by saying my understanding of UDP Broadcasting and Multicasting is very limited. This is my first project working on this.
I have a C# desktop client running on a machine and a Windows phone 7 app.
The WP7 app is supposed to send a UDP broadcast over the network and the desktop client is supposed to listen for a UDP Multicast and respond accordingly. This is just meant for simple machine discovery over the network to find machines running the desktop client.
C# Desktop Client Code
public class ConnectionListener
{
private const int UDP_PORT = 54322;
private static readonly IPAddress MULTICAST_GROUP_ADDRESS = IPAddress.Parse("224.0.0.1");
private UdpClient _listener;
public ConnectionListener()
{
_listener = new UdpClient(UDP_PORT, AddressFamily.InterNetwork);
_listener.EnableBroadcast = true;
_listener.JoinMulticastGroup(MULTICAST_GROUP_ADDRESS);
_listener.BeginReceive(ReceiveCallback, null);
}
private void ReceiveCallback(IAsyncResult result)
{
IPEndPoint receiveEndpoint = new IPEndPoint(IPAddress.Any, UDP_PORT);
byte[] receivedBytes = _listener.EndReceive(result, ref receiveEndpoint);
byte[] response = Encoding.UTF8.GetBytes("WPF Response");
_listener.BeginSend(response, response.Length, receiveEndpoint, SendCallback, null);
}
private void SendCallback(IAsyncResult result)
{
int sendCount = _listener.EndSend(result);
Console.WriteLine("Sent Count is: " + sendCount);
}
}
The WP7 Server code:
public class MachineDetector
{
public const int UDP_PORT = 54322;
private const string MULTICAST_GROUP_ADDRESS = "224.0.0.1";
UdpAnySourceMulticastClient _client = null;
bool _joined = false;
private byte[] _receiveBuffer;
private const int MAX_MESSAGE_SIZE = 512;
public MachineDetector()
{
_client = new UdpAnySourceMulticastClient(IPAddress.Parse(MULTICAST_GROUP_ADDRESS), UDP_PORT);
_receiveBuffer = new byte[MAX_MESSAGE_SIZE];
_client.BeginJoinGroup(
result =>
{
_client.EndJoinGroup(result);
_client.MulticastLoopback = false;
SendRequest();
}, null);
}
private void SendRequest()
{
byte[] requestData = Encoding.UTF8.GetBytes("WP7 Multicast");
_client.BeginSendToGroup(requestData, 0, requestData.Length,
result =>
{
_client.EndSendToGroup(result);
Receive();
}, null);
}
private void Receive()
{
Array.Clear(_receiveBuffer, 0, _receiveBuffer.Length);
_client.BeginReceiveFromGroup(_receiveBuffer, 0, _receiveBuffer.Length,
result =>
{
IPEndPoint source;
_client.EndReceiveFromGroup(result, out source);
string dataReceived = Encoding.UTF8.GetString(_receiveBuffer, 0, _receiveBuffer.Length);
string message = String.Format("[{0}]: {1}", source.Address.ToString(), dataReceived);
Console.WriteLine(message);
Receive();
}, null);
}
}
I am able to receive data with the desktop client, but the WP7 app doesn't seem able to receive the response. I've been banging my head on this for a while now and don't know where else to look. Any help would be great.
So, any suggestions why the WP7 app isn't receiving a response?
I think the issue is with ConnectionListener:ReceiveCallback in C# Desktop Client.
_listener.BeginSend(response, response.Length,
receiveEndpoint, SendCallback, null);
Instead call
_listener.BeginSend(response, response.Length,
SendCallback, null);
This will cause the message to be sent to the multicast address. For further help with this refer to How to: Send and Receive Data in a Multicast Group for Windows Phone.
WP7 needs to multicast to the network in order to efficiently reach all desktop clients in one sweep. For the client response, the only intended destination is WP7. As such, a multicast has no real advantage here (as the desktop clients tuned into the multicast will effectively be ignoring it).
Instead, you could use receiveEndpoint populated by the call to EndReceive in ConnectionListener:ReceiveCallback to send a unicast response to the WP7 server. This is recommended consideration for creating multicast applications at MSDN.
This way WP7 no longer needs to join the multicast group for incoming multicast and the desktop client needn't send a multicast to respond.