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.
Related
I'm creating a chat application in which multiple users can enter their messages and send them to the server over tcp (networkstream).
I have the following code for receiving messages:
private void ReceiveData()
{
Task.Run(() =>
{
int bufferSize = 1024;
string message = "";
byte[] buffer = new byte[bufferSize];
_NetworkStream = _TcpClient.GetStream();
AddMessage("Connected!");
while (true)
{
int readBytes = _NetworkStream.Read(buffer, 0, bufferSize);
message = Encoding.ASCII.GetString(buffer, 0, readBytes);
Console.WriteLine("I received a message: " + message);
if (message.Equals("bye"))
{
break;
}
AddMessage(message);
}
buffer = Encoding.ASCII.GetBytes("bye");
_NetworkStream.Write(buffer, 0, buffer.Length);
_NetworkStream.Close();
_TcpClient.Close();
AddMessage("Connection closed!");
});
}
Now when I call AddMessage (which I call from a different Thread, thus a different context) My application crashes. Which is quite logical given my AddMessage code:
private void AddMessage(string message)
{
this.ListBox_Messages.Items.Add(message);
}
My question is, is the addmessage function responsible for executing this on the ui thread or the function caller, in this case ReceiveData() and what would be the best and most modern way to implement it?
Thank you in advance!
Did your teacher actually TEACH you any of these "modern methods"?!
Tell him or her there is nothing wrong with going back to basics:
private void AddMessage(string message)
{
if (this.ListBox_Messages.InvokeRequired)
{
this.ListBox_Messages.Invoke((MethodInvoker)delegate {
this.ListBox_Messages.Items.Add(message);
});
}
else
{
this.ListBox_Messages.Items.Add(message);
}
}
It's simple, and it just works.
As a computer science teacher myself, I hate seeing these types of questions. Makes me wonder what the teachers are doing.
Teacher: "You must use this magical stuff that I haven't taught you yet!"
Giving your teacher the benefit of the doubt, are there any requirements for this application that you haven't shared with us?
I have a special socket listener, that works in it's thread. It's job to get commands from external program that updates database. When command comes on socket i am calling special method, that updates my application cache from database.
I have a problem, that is delay between sending command from external program and processing that command in my app (ASP .NET Application). Every day my app restarting at 4 a.m. and by the end of the day i have delay about 1-2 hours.
How i can reduce this delay?
You can find code of my listener below.
Thanks.
public delegate void OnECIGetCommand( string command );
public class ECIMain
{
protected Socket socket;
protected string ip;
protected int port;
private static ECIMain INSTANCE = null;
const int receivedDataSize = 250;
protected static byte[] buffer = new byte[ receivedDataSize ];
protected static StringBuilder sb;
protected static DoWorkEventHandler onCommand;
private ECIMain()
{
socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
sb = new StringBuilder();
}
private void StartSocket()
{
sb.Clear();
socket.Listen(1);
socket.BeginAccept(null, receivedDataSize,
new AsyncCallback(AcceptReceiveDataCallback), socket);
}
private static void AcceptReceiveDataCallback(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
// End the operation and display the received data on the console.
byte[] Buffer;
int bytesTransferred;
Socket handler = listener.EndAccept(out Buffer,
out bytesTransferred, ar);
HandleBuff(bytesTransferred, Buffer);
// Create the state object for the asynchronous receive.
handler.BeginReceive(buffer, 0, receivedDataSize,
SocketFlags.None, new AsyncCallback(ReadCallback), handler);
}
private static void HandleBuff(int size, byte[] buff )
{
if (size > 0)
{
// There might be more data, so store the data received so far.
sb.Append(Encoding.ASCII.GetString(buff, 0, size));
// Check for end-of-file tag. If it is not there, read more data.
var content = sb.ToString();
int pos = -1;
if ((pos = content.IndexOf("</cmd>")) > -1)
{
// All the data has been read from the
// client.
pos += 6;
if( pos < content.Length )
content = content.Remove(pos);
var startPos = content.LastIndexOf("<cmd>");
if( startPos > -1 )
{
if (startPos > 0)
content = content.Remove(0, startPos);
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += onCommand;
worker.RunWorkerAsync(content);
}
sb.Remove(0, pos);
}
}
}
private static void ReadCallback(IAsyncResult ar)
{
// Retrieve the state object and the handler socket
// from the asynchronous state object.
Socket handler = (Socket)ar.AsyncState;
SocketError error;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar, out error );
if (error == SocketError.Success)
{
if (bytesRead > 0)
{
HandleBuff(bytesRead, buffer);
handler.BeginReceive(buffer, 0, receivedDataSize,
SocketFlags.None, new AsyncCallback(ReadCallback), handler);
}
else
{
handler.Disconnect(true);
INSTANCE.StartSocket();
}
}
else if (error == SocketError.Shutdown || error == SocketError.ConnectionReset)
{
INSTANCE.StartSocket();
}
}
public static string InitECI(int port, DoWorkEventHandler commandHandler)
{
if (INSTANCE == null)
{
INSTANCE = new ECIMain();
INSTANCE.port = port;
onCommand += commandHandler;
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList
.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
INSTANCE.ip = ipAddress.ToString();
try
{
INSTANCE.socket.Bind(localEndPoint);
}
catch (System.Net.Sockets.SocketException e)
{
if (e.SocketErrorCode == System.Net.Sockets
.SocketError.AddressAlreadyInUse)
{
//INSTANCE.socket.Bind(localEndPoint);
}
throw e;
}
INSTANCE.StartSocket();
}
return INSTANCE.ip;
}
public static void ShutDownECI()
{
if( INSTANCE.socket.Connected )
INSTANCE.socket.Disconnect(false);
INSTANCE.socket.Close();
}
}
When using the TCP Stack (either sending or receiving) one must look at the stack as it's own system, which is bullet proof and works very well... Most of these types of issues deal with the fact that the application layer is easily able to fire off as many async actions as it wants, but that doesn't mean the TCP Stack is going to be faster, especially if it's overwhelmed. What it means instead is that it will queue up and process tasks as it's able.
One of the symptoms of the stack being overwhelmed is the presence of many half session states visualized via the Netstat command. Anything that has the word "Wait" is an indicator of a half state. This occurs when one side posts a packet but the other doesn't respond right away. From there it's all downhill because TCP kicks in and attempts to keep the sessions alive by re-sending the same packet. Multiply this by the number of active sessions and the fact that TCP retries up to 10 times for each packet before timing out, you can see this is the last thing an overwhelmed stack needs.
Your situation could be that the network traffic is exceeding the capacity of the single Network adapter. This is usually solved by adding more network cards and using other means to do load balancing. One way is what they call DNS round robin and it's the least expensive. The other way is an F5 device.
Bottom line, it sounds like your network adapter is being overwhelmed.
Here's few things to check at both side of the wire.
Are all of the sockets being fully closed when the gig is up for each session?
Are you able to run a network monitor to review the overall loads... You typically want 60% or less utilization on any single network adapter.
If your loads are too high then you can talk to DNS folks about using round robin, and put in another card into the same server (it will have different address)
Sometimes it's due to lack of compression of the data being sent on the wire. You can investigate compression techniques too.
Sometimes switches go bad which give MAC-To-MAC connect-ability.
Improper router configuration can allow for re-tranmissions on the wire from different access points.
Incorrectly configured servers can also broadcast too much noise (total junk)
Wireless Access Points are notorious for going flaky they could be the source of noise too.
There's a lot to getting to the root of this type of issue, but hope that some of these ideas will help you.
I've been trying to make a simple udp chat application in c#. it was working an hour ago, but I don't exactly realize what happened to it or what exactly I've changed in it. when I try to listen for any incoming messages, I just get an exception saying "Invalid argument was supplied", pointing to the variable 'rcv'. Here's the code:
public partial class Form1 : Form
{
Socket sock = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"),
1234);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ThreadStart(send));
thread1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
Thread thread2 = new Thread(new ThreadStart(receive));
thread2.Start();
}
private void receive()
{
while (true)
{
byte[] rcv = new byte[2048];
int size = sock.Receive(rcv); // this is where the exception is, pointing at rcv.
char[] chars = new char[size];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int length = d.GetChars(rcv, 0, size, chars, 0);
System.String recv = new System.String(chars);
textBox1.Text += recv.ToString();
}
}
private void send()
{
byte[] msg = Encoding.UTF8.GetBytes(textBox1.Text);
sock.SendTo(msg, localEndPoint);
}
private void button3_Click(object sender, EventArgs e)
{
sock.Close();
}
}
I've honestly never seen this exception while working with sockets. I thought maybe the socket was open and in use so I tried closing it but with no success.
Any tips will be greatly appreciated.
From MSDN:
If you are using a connection-oriented protocol, you must either call Connect to establish a remote host connection, or Accept to accept an incoming connection prior to calling Receive. The Receive method will only read data that arrives from the remote host established in the Connect or Accept method. If you are using a connectionless protocol, you can also use the ReceiveFrom method. ReceiveFrom will allow you to receive data arriving from any host.
It appears you are not calling Connect, Accept, or ReceiveFrom in any case. Since a chat application follows (logically) a connection-oriented protocol, you should probably call Connect or Accept somewhere prior to Receive.
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)
I want to do this
for (int i = 0; i < 100; i++ )
{
Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
But instead of using UdpClient.Receive, I have to use UdpClient.BeginReceive. The problem is, how do I do that? There aren't a lot of samples using BeginReceive, and the MSDN example is not helping at all. Should I use BeginReceive, or just create it under a separate thread?
I consistently get ObjectDisposedException exception. I only get the first data sent. The next data will throw exception.
public class UdpReceiver
{
private UdpClient _client;
public System.Net.Sockets.UdpClient Client
{
get { return _client; }
set { _client = value; }
}
private IPEndPoint _endPoint;
public System.Net.IPEndPoint EndPoint
{
get { return _endPoint; }
set { _endPoint = value; }
}
private int _packetCount;
public int PacketCount
{
get { return _packetCount; }
set { _packetCount = value; }
}
private string _buffers;
public string Buffers
{
get { return _buffers; }
set { _buffers = value; }
}
private Int32 _counter;
public System.Int32 Counter
{
get { return _counter; }
set { _counter = value; }
}
private Int32 _maxTransmission;
public System.Int32 MaxTransmission
{
get { return _maxTransmission; }
set { _maxTransmission = value; }
}
public UdpReceiver(UdpClient udpClient, IPEndPoint ipEndPoint, string buffers, Int32 counter, Int32 maxTransmission)
{
_client = udpClient;
_endPoint = ipEndPoint;
_buffers = buffers;
_counter = counter;
_maxTransmission = maxTransmission;
}
public void StartReceive()
{
_packetCount = 0;
_client.BeginReceive(new AsyncCallback(Callback), null);
}
private void Callback(IAsyncResult result)
{
try
{
byte[] buffer = _client.EndReceive(result, ref _endPoint);
// Process buffer
MainWindow.Log(Encoding.ASCII.GetString(buffer));
_packetCount += 1;
if (_packetCount < _maxTransmission)
{
_client.BeginReceive(new AsyncCallback(Callback), null);
}
}
catch (ObjectDisposedException ex)
{
MainWindow.Log(ex.ToString());
}
catch (SocketException ex)
{
MainWindow.Log(ex.ToString());
}
catch (System.Exception ex)
{
MainWindow.Log(ex.ToString());
}
}
}
What gives?
By the way, the general idea is:
Create tcpclient manager.
Start sending/receiving data using udpclient.
When all data has been sent, tcpclient manager will signal receiver that all data has been sent, and udpclient connection should be closed.
It would seem that UdpClient.BeginReceive() and UdpClient.EndReceive() are not well implemented/understood. And certainly compared to how the TcpListener is implemented, are a lot harder to use.
There are several things that you can do to make using the UdpClient.Receive() work better for you. Firstly, setting timeouts on the underlying socket Client will enable control to fall through (to an exception), allowing the flow of control to continue or be looped as you like. Secondly, by creating the UDP listener on a new thread (the creation of which I haven't shown), you can avoid the semi-blocking effect of the UdpClient.Receive() function and you can effectively abort that thread later if you do it correctly.
The code below is in three parts. The first and last parts should be in your main loop at the entry and exit points respectively. The second part should be in the new thread that you created.
A simple example:
// Define this globally, on your main thread
UdpClient listener = null;
// ...
// ...
// Create a new thread and run this code:
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999);
byte[] data = new byte[0];
string message = "";
listener.Client.SendTimeout = 5000;
listener.Client.ReceiveTimeout = 5000;
listener = new UdpClient(endPoint);
while(true)
{
try
{
data = listener.Receive(ref endPoint);
message = Encoding.ASCII.GetString(data);
}
catch(System.Net.Socket.SocketException ex)
{
if (ex.ErrorCode != 10060)
{
// Handle the error. 10060 is a timeout error, which is expected.
}
}
// Do something else here.
// ...
//
// If your process is eating CPU, you may want to sleep briefly
// System.Threading.Thread.Sleep(10);
}
// ...
// ...
// Back on your main thread, when it's exiting, run this code
// in order to completely kill off the UDP thread you created above:
listener.Close();
thread.Close();
thread.Abort();
thread.Join(5000);
thread = null;
In addition to all this, you can also check UdpClient.Available > 0 in order to determine if any UDP requests are queued prior to executing UdpClient.Receive() - this completely removes the blocking aspect. I do suggest that you try this with caution as this behaviour does not appear in the Microsoft documentation, but does seem to work.
Note:
The MSDN exmaple code you may have found while researching this problem requires an additional user defined class - UdpState. This is not a .NET library class. This seems to confuse a lot of people when they are researching this problem.
The timeouts do not strictly have to be set to enable your app to exit completely, but they will allow you to do other things in that loop instead of blocking forever.
The listener.Close() command is important because it forces the UdpClient to throw an exception and exit the loop, allowing Thread.Abort() to get handled. Without this you may not be able to kill off the listener thread properly until it times out or a UDP packet is received causing the code to continue past the UdpClient.Receive() block.
Just to add to this priceless answer, here's a working and tested code fragment. (Here in a Unity3D context but of course for any c#.)
// minmal flawless UDP listener per PretorianNZ
using System.Collections;
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
void Start()
{
listenThread = new Thread (new ThreadStart (SimplestReceiver));
listenThread.Start();
}
private Thread listenThread;
private UdpClient listenClient;
private void SimplestReceiver()
{
Debug.Log(",,,,,,,,,,,, Overall listener thread started.");
IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, 1260);
listenClient = new UdpClient(listenEndPoint);
Debug.Log(",,,,,,,,,,,, listen client started.");
while(true)
{
Debug.Log(",,,,, listen client listening");
try
{
Byte[] data = listenClient.Receive(ref listenEndPoint);
string message = Encoding.ASCII.GetString(data);
Debug.Log("Listener heard: " +message);
}
catch( SocketException ex)
{
if (ex.ErrorCode != 10060)
Debug.Log("a more serious error " +ex.ErrorCode);
else
Debug.Log("expected timeout error");
}
Thread.Sleep(10); // tune for your situation, can usually be omitted
}
}
void OnDestroy() { CleanUp(); }
void OnDisable() { CleanUp(); }
// be certain to catch ALL possibilities of exit in your environment,
// or else the thread will typically live on beyond the app quitting.
void CleanUp()
{
Debug.Log ("Cleanup for listener...");
// note, consider carefully that it may not be running
listenClient.Close();
Debug.Log(",,,,, listen client correctly stopped");
listenThread.Abort();
listenThread.Join(5000);
listenThread = null;
Debug.Log(",,,,, listener thread correctly stopped");
}
I think you should not use it in a loop but instead whenever the BeginReceive callback is called you call BeginReceive once more and you keep a public variable for count if you want to limit the number to 100.
have look at MSDN first. They provide good example.
http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive.aspx
I would do network communication on a background thread, so that it doesn't block anything else in your application.
The issue with BeginReceive is that you must call EndReceive at some point (otherwise you have wait handles just sitting around) - and calling EndReceive will block until the receive is finished. This is why it is easier to just put the communication on another thread.
You have to do network operations, file manipulations and such things that are dependent to other things rather than your own program on another thread (or task) because they may freeze your program. The reason for that is that your code executes sequentially.
You have used it in a loop which is not fine. Whenever BeginRecieve callback is invoked you should call it again. Take a look at the following code:
public static bool messageReceived = false;
public static void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
Byte[] receiveBytes = u.EndReceive(ar, ref e);
string receiveString = Encoding.ASCII.GetString(receiveBytes);
Console.WriteLine("Received: {0}", receiveString);
messageReceived = true;
}
public static void ReceiveMessages()
{
// Receive a message and write it to the console.
IPEndPoint e = new IPEndPoint(IPAddress.Any, listenPort);
UdpClient u = new UdpClient(e);
UdpState s = new UdpState();
s.e = e;
s.u = u;
Console.WriteLine("listening for messages");
u.BeginReceive(new AsyncCallback(ReceiveCallback), s);
// Do some work while we wait for a message. For this example,
// we'll just sleep
while (!messageReceived)
{
Thread.Sleep(100);
}
}