I'm trying to create a simple game using the XNA Framework. I'm starting to implement multiplayer over LAN. Everything has been going well but now I'm running into a problem with UDPClient.Receive.
When the server is running, it is supposed to have two listeners(using UDPClient.Receive), on different ports. One listener is waiting for any incoming connection requests for people who want to join the game and respond accordingly. The other listens for constant updates on the player's positions so it can keep all players' views synchronised. The problem is that I don't know how to code in the first listener.
When the listener for connection request starts, all the code freezes, and it doesn't start until that listener will receive something. How would I code it so it is just running in the background?
Here is my code for the connection listener:
public class Connect
{
public static void WaitForConnections()
{
UdpClient udpc2 = new UdpClient(2054);
IPEndPoint ep = null;
Random rnd = new Random();
Console.WriteLine("Waiting for connections...");
byte[] joinrequest = udpc2.Receive(ref ep);
if (Encoding.ASCII.GetString(joinrequest) == "join")
{
Console.WriteLine("Attempting to join");
if (Server.ConnectedPlayers.Count < Server.MaxPlayers)
{
byte[] newShooter = DataControls.ClassToByteArray(new ShooterObject(Server.ConnectedPlayers.Count + 1, new Vector2(((Server.ConnectedPlayers.Count + 1) * 100) + 22, 70), new byte[3] { (byte)rnd.Next(255), (byte)rnd.Next(255), (byte)rnd.Next(255) }));
udpc2.Send(newShooter, newShooter.Length, ep);
Console.WriteLine("Joined successfully");
}
else
{
byte[] error = Encoding.ASCII.GetBytes("full");
udpc2.Send(error, error.Length, ep);
Console.WriteLine("Too many players");
}
}
}
}
You need to use a background worker thread or equivalent (look at Task and threads generally) but to help you get going in basic full example:
using System;
using System.ComponentModel;
using System.Threading;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main()
{
// Create our listener and start listening...
var sammpleListener = new SampleListener();
sammpleListener.StartListening();
// Run forever...
Console.WriteLine("Waiting for players to join...");
Console.WriteLine("Press Ctrl+C to stop!");
for (var counter = 1;; counter++)
{
Console.WriteLine("Main thread working: {0}...", counter);
Thread.Sleep(200);
}
}
internal class SampleListener
{
private readonly BackgroundWorker _udpWaitForPlayer;
public SampleListener()
{
_udpWaitForPlayer = new BackgroundWorker();
_udpWaitForPlayer.DoWork += _udpWaitForPlayer_DoWork;
}
public void StartListening()
{
_udpWaitForPlayer.RunWorkerAsync();
}
private void _udpWaitForPlayer_DoWork(object sender, DoWorkEventArgs e)
{
const int junkSample = 10;
for (var i = 1; i <= junkSample; i++)
{
// Notice how this doesn't make the main thread sleep / hang...
Console.WriteLine("Background worker working: {0} of {1}...", i, junkSample);
Thread.Sleep(1000);
}
Console.WriteLine("Background worker: Finished!");
}
}
}
}
Related
I'm using a winform to try to gather online/offline status of every IP on the network, doing so I'm using a ping request, if it replies it marks the IP as online then moves on. Only issue is waiting for up to 255 replies, after it gets all 255 responses I am wanting it to fill a data grid view.
I've managed to get it all to work but only downside is, gui hangs while doing this process, I figured just use a separate thread with the following expression
Thread T1 = new Thread(PingHost)
T1.run();
PingHost does as the name implies, pings all the hosts and decides if online or offline. Problem is I tried to update the dgv table from the thread, naturally dgv is locked to main thread.
So I tried switching to a Task and just grab the return value and update after everything is finished. Sadly I can't quite get how to get the return value from the task AND have it run on a separate thread.
I've tried googling different methods, but just running in circles at this point, so I humbly come to you guys for assistance
Code of main thread using tasks
List<string> everything = new List<string>();
int i = 0;
Task<List<string>> task1 = Task<List<string>>.Factory.StartNew(PingHost);
everything = task1.Result;
foreach(var item in everything)
{
var index = dataGridView1.Rows.Add();
dataGridView1.Rows[i].Cells["IP"].Value = item;
i++;
}
And this is the thread of my PingHost method
bool pingable = false;
Ping pinger = null;
int i = 0;
string ip;
while (i < 255)
{
ip = "192.168.0." + i;
try
{
pinger = new Ping();
PingReply reply = pinger.Send(ip, 8);
pingable = reply.Status == IPStatus.Success;
}
catch (PingException)
{
MessageBox.Show("ERROR");
// Discard PingExceptions and return false;
}
finally
{
if (pinger != null)
{
pinger.Dispose();
}
}
if (pingable)
{
checkedIP.Add(ip + ": ONLINE");
}
else
{
checkedIP.Add(ip + ": OFFLINE");
}
i++;
}
return checkedIP;
This might be a bit overkill, but I just drafted a solution. Basically I created a new Class for pinging with an event that triggers after each Ping returned, this event uses custom EventArgs to return the IP that was just pinged and if it is online or not. I then subscribed to that Event in my GUI Thread and just update the GUI. Here's some code:
This is my Pinger class responsible for pinging the actual Computers:
class Pinger
{
public event EventHandler<PingReturnedEventArgs> OnPingReturned;
public void PingNetwork()
{
for (int i = 1; i <= 255; i++)
{
string ip = $"192.168.0.{i}";
Ping ping = new Ping();
try
{
PingReply reply = ping.Send(IPAddress.Parse(ip));
TriggerEvent(reply?.Address.ToString(), true);
}
catch (Exception)
{
TriggerEvent(ip, false);
}
}
}
private void TriggerEvent(string ip, bool online)
{
if (OnPingReturned == null) return;
PingReturnedEventArgs args = new PingReturnedEventArgs(ip, online);
OnPingReturned(this, args);
}
}
My custom EventArgs:
class PingReturnedEventArgs : EventArgs
{
public string Ip { get; private set; }
public bool Online { get; private set; }
private PingReturnedEventArgs() { }
public PingReturnedEventArgs(string ip, bool online)
{
Ip = ip;
Online = online;
}
}
And finally here is how I'm actually using all of this:
Pinger pinger = new Pinger();
pinger.OnPingReturned += Your event handler to update the GUI
// I recommend doing it like this so you can stop the Thread at a later time
// Maybe with like a cancel button
Thread pingThread = new Thread(pinger.PingNetwork);
pingThread.Start();
The event handler looks like this private void PingReturnedHandler(object sender, PingReturnedEventArgs args)
The two main benefits of this are that 1. the GUI Thread remains unblocked, meaning the GUI will still respond to user input and 2. this procedurally (on every ping completion) triggers the event, meaning that if it takes a long time to ping all the PCs you don't have to wait for the entirety to finish before the user sees something
I have the following code that is working and pings all of the Runescape game servers and returns a list in the console of the ip addresses and the respective 'roundtrip' time taken.
EDIT 'IGNORE SOME OF THE OLD COMMENTS'
I am having trouble with the following:
A) How can I return the lowest ping out of all of the servers and write it to the console?
B) How can i return the original hostname or 'number' of the current 'server.ToString()' across methods?
public void buttonClick_Click(object sender, EventArgs e)
{
Console.WriteLine();
Ping();
}
public static void Ping()
{
for (int server = 1; server <= 110; server++)
{
string hostname = "oldschool" + server.ToString() + ".runescape.com";
// Get an object that will block the main thread.
AutoResetEvent waiter = new AutoResetEvent(false);
// Ping's the local machine.
Ping pingSender = new Ping();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
//Console.WriteLine("Send Before Async.");
// Send the ping asynchronously.
// Use the waiter as the user token.
// When the callback completes, it can wake up this thread.
pingSender.SendAsync(hostname, 1000, waiter);
//Console.WriteLine("Ping example completed.");
}
}
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
// If the operation was canceled, display a message to the user.
if (e.Cancelled)
{
Console.WriteLine("Ping canceled.");
// Let the main thread resume.
// UserToken is the AutoResetEvent object that the main thread
// is waiting for.
((AutoResetEvent)e.UserState).Set();
}
// If an error occurred, display the exception to the user.
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
Console.WriteLine(e.Error.ToString());
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
PingReply reply = e.Reply;
DisplayReply(reply);
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
public static void DisplayReply(PingReply reply)
{
List<long> lag = new List<long>();
if (reply == null)
return;
Console.WriteLine("Status: {0}", reply.Status);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("\r\n");
Console.WriteLine("Address: {0}", reply.Address);
Console.WriteLine("Ping: {0}", reply.RoundtripTime);
}
return;
}
I restructured your code so it should work. I also made pings run in parallel which should speed up results.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
namespace WindowsFormsApplication13
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
public class State
{
public string ip { get; set;}
public decimal roundTripTime { get; set; }
public IPStatus status { get;set;}
}
public class myPing
{
static const int NUMBER_SERVERS = 110;
public static List<State> states { get; set; }
public static int count = 0;
public myPing()
{
AutoResetEvent waiter = new AutoResetEvent(false);
for (int server = 1; server <= NUMBER_SERVERS; server++)
{
string hostname = "oldschool" + server.ToString() + ".runescape.com";
// Get an object that will block the main thread.
// Ping's the local machine.
Ping pingSender = new Ping();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
//Console.WriteLine("Send Before Async.");
// Send the ping asynchronously.
// Use the waiter as the user token.
// When the callback completes, it can wake up this thread.
pingSender.SendAsync(hostname, 1000, waiter);
//Console.WriteLine("Ping example completed.");
}
waiter.WaitOne();
DisplayReply(states);
Console.ReadLine();
}
private static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
// If the operation was canceled, display a message to the user.
if (e.Cancelled)
{
Console.WriteLine("Ping canceled.");
// Let the main thread resume.
// UserToken is the AutoResetEvent object that the main thread
// is waiting for.
((AutoResetEvent)e.UserState).Set();
}
// If an error occurred, display the exception to the user.
if (e.Error != null)
{
Console.WriteLine("Ping failed:");
Console.WriteLine(e.Error.ToString());
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
PingReply reply = e.Reply;
State state = new State();
Object thisLock = new Object();
lock (thisLock)
{
states.Add(state);
}
state.ip = reply.Address.ToString();
state.roundTripTime = reply.RoundtripTime;
state.status = reply.Status;
count++;
// Let the main thread resume.
if (count == NUMBER_SERVERS)
{
((AutoResetEvent)e.UserState).Set();
}
}
public static void DisplayReply(List<State> replies)
{
foreach (State reply in replies)
{
Console.WriteLine("Status: {0}", reply.status);
if (reply.status == IPStatus.Success)
{
Console.WriteLine("\r\n");
Console.WriteLine("Address: {0}", reply.ip);
Console.WriteLine("Ping: {0}", reply.roundTripTime);
}
}
State shortest = replies.OrderBy(x => x.roundTripTime).FirstOrDefault();
Console.WriteLine("Shortest Route IP : '{0}' Time : '{1}'", shortest.ip.ToString(), shortest.roundTripTime.ToString() );
}
}
}
I have the following C# code:
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace CTCServer
{
class Server
{
//Stores the IP Adress the server listens on
private IPAddress ip;
//Stores the port the server listens on
private int port;
//Stores the counter of connected clients. *Note* The counter only gets increased, it acts as "id"
private int clientCount = 0;
//Defines if the server is running. When chaning to false the server will stop and disconnect all clients.
private bool running = true;
//Stores all connected clients.
public List<Client> clients = new List<Client>();
//Event to pass recived data to the main class
public delegate void GotDataFromCTCHandler(object sender, string msg);
public event GotDataFromCTCHandler GotDataFromCTC;
//Constructor for Server. If autoStart is true, the server will automaticly start listening.
public Server(IPAddress ip, int port, bool autoStart = false)
{
this.ip = ip;
this.port = port;
if (autoStart)
this.Run();
}
//Starts the server.
public void Run()
{
//Run in new thread. Otherwise the whole application would be blocked
new Thread(() =>
{
//Init TcpListener
TcpListener listener = new TcpListener(this.ip, this.port);
//Start listener
listener.Start();
//While the server should run
while (running)
{
//Check if someone wants to connect
if (listener.Pending())
{
//Client connection incoming. Accept, setup data incoming event and add to client list
Client client = new Client(listener.AcceptTcpClient(), this.clientCount);
//Declare event
client.internalGotDataFromCTC += GotDataFromClient;
//Add to list
clients.Add(client);
//Increase client count
this.clientCount++;
}
else
{
//No new connections. Sleep a little to prevent CPU from going to 100%
Thread.Sleep(100);
}
}
//When we land here running were set to false or another problem occured. Stop server and disconnect all.
Stop();
}).Start(); //Start thread. Lambda \(o.o)/
}
//Fires event for the user
private void GotDataFromClient(object sender, string data)
{
//Data gets passed to parent class
GotDataFromCTC(sender, data);
}
//Send string "data" to all clients in list "clients"
public void SendToAll(string data)
{
//Call send method on every client. Lambda \(o.o)/
this.clients.ForEach(client => client.Send(data));
}
//Stop server
public void Stop()
{
//Exit listening loop
this.running = false;
//Disconnect every client in list "client". Lambda \(o.o)/
this.clients.ForEach(client => client.Close());
//Clear clients.
this.clients.Clear();
}
}
}
Should run not be in a loop to create new threads?
If the first question is not true, and the lambda expression already creates new thread, at which point is new thread created? and where is the logic to decide it?
new Thread( will create the new thread. The lambda is executed on the thread. Run should not be in a loop. Because it will create many threads.
and the lambda expression already creates new thread, no it will be used as the thread method.
The only problem is, that you don't have a reference to the thread, so you cannot wait until it is terminated.
Also you're using a bool running for the while loop. You'd better use a ManualResetEvent for it.
I use this as standard thread setup:
// signal for terminating the thread.
private ManualResetEvent _terminating = new ManualResetEvent(false);
private Thread _thread;
public void Start()
{
ManualResetEvent threadStarted = new ManualResetEvent(false);
_thread = new Thread(() =>
{
threadStarted.Set();
while(!_terminating.WaitOne(0))
{
// do your thing here.
}
});
_thread.Start();
threadStarted.WaitOne();
}
public void Dispose()
{
_terminating.Set();
_thread.Join();
}
One remark here is: Should you use Threaded clients or async sockets.
Threaded clients: client count <= 10
async sockets: client count > 10
The problem with a server is, that your not in charge of how many clients are connecting.
Some pseudo code how to setup your tcp-server and running threads for each client.
public class Server
{
// signal for terminating the thread.
private ManualResetEvent _terminating = new ManualResetEvent(false);
private List<ClientHandler> _clients = new List<ClientHandler>();
public void Start()
{
ManualResetEvent threadStarted = new ManualResetEvent(false);
_thread = new Thread(() =>
{
threadStarted.Set();
// create listener.....
while(!_terminating.WaitOne(0))
{
// do your thing here.
// accept socket
var socket = _listenerSocket.Accept();
ClientHandler handler = new ClientHandler(socket);
_clients.Add(handler);
}
});
_thread.Start();
threadStarted.WaitOne();
}
public void Dispose()
{
_terminating.Set();
_thread.Join();
}
}
public class ClientHandler
{
// signal for terminating the thread.
private ManualResetEvent _terminating = new ManualResetEvent(false);
public ClientHandler(Socket socket)
{
ManualResetEvent threadStarted = new ManualResetEvent(false);
_thread = new Thread(() =>
{
threadStarted.Set();
while(!_terminating.WaitOne(0))
{
// do your thing here.
// accept socket
var bytesReaded = socket.Read(.....);
// handle data....
}
});
_thread.Start();
threadStarted.WaitOne();
}
public void Dispose()
{
_terminating.Set();
_thread.Join();
}
}
I'm trying to create two client threads which are connected to a server. When there are two connections (Two entries in the threadsArray), I want the start to be announced. The code is never hitting the threadRequest.annouceStart() call however.
Through debugging I have determined that the first thread that is created is being stopped while the server is listening for another connection in the form of the second client. Is it this "freeze" as the server hangs waiting for another connection that is stopping the first thread?
static void Main(string[] args)
{
runServer();
}
static void runServer()
{
TcpListener listener;
Socket connection;
Handler threadRequest;
string defaultName = "";
int defaultScore = 0;
int i = 0;
Thread[] threadsArray = new Thread[2];
try
{
listener = new TcpListener(IPAddress.Any, 43);
listener.Start();
Console.WriteLine("Quiz Server launched");
Console.WriteLine("A default user has been created for testing purposes");
while(true) //main game loop
{
connection = listener.AcceptSocket();
threadRequest = new Handler();
threadsArray[i] = new Thread(() => threadRequest.clientInteraction(connection, teamInformation));
threadsArray[i].Name = "Team" + (i + 1);
threadsArray[i].Start();
i++;
if (threadsArray[1] != null)
{
if (threadsArray[1].ThreadState == ThreadState.Running
&& threadsArray[0].ThreadState == ThreadState.Running)
{
foreach (Thread thread in threadsArray)
{
threadRequest.announceStart(connection, teamInformation);
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.ToString());
Console.ReadKey();
}
}
edit: added Handler class definition.
class Handler {
public static string interactionType;
public static string pTeamName;
public static string pAnswer;
public void announceStart(Socket connection, ConcurrentDictionary<string, int> teamInformation)...
public void clientInteraction(Socket connection, ConcurrentDictionary<string, int> teamInformation)...
public static void parseStrings(StreamReader sr, string recievedLine, out string pTeamName,
out string pAnswer)...
static void findInteractionType(out string interactionType, string clientString)...
}
Credit to Andrey Polyakov in comments.
"Thread stops if executing method returns. You should run infinite loop in such method for avoid thread termination" in regards to the clientInteraction() method.
I have an issue about the server-client communication.
I googled around but I did not find a solution to this.
Right now I am using 32feet in order to get in touch 2 or more (till 7) BT clients to 1 BT server.
I need to broadcast a message from the server to every device in the same time, but I don't know how to do it.
The only way I figured out was to use the list of connection in order to send the message one per time, but it means a delay between each message sent (around 100 ms per device). Unfortunately it means to have a large delay on the last one.
Can someone please give me an advice on how to solve this problem?
Is there a way to broadcast the message to all devices in the same time?
If it can be helpfull, here there is the handle of connection and reading from devices.
Thanks for your help
private void btnStartServer_Click(object sender, EventArgs e)
{
btnStartClient.Enabled = false;
ConnectAsServer();
}
private void ConnectAsServer()
{
connessioniServer = new List<BluetoothClient>();
// thread handshake
Thread bluetoothConnectionControlThread = new Thread(new ThreadStart(ServerControlThread));
bluetoothConnectionControlThread.IsBackground = true;
bluetoothConnectionControlThread.Start();
// thread connessione
Thread bluetoothServerThread = new Thread(new ThreadStart(ServerConnectThread));
bluetoothServerThread.IsBackground = true;
bluetoothServerThread.Start();
}
private void ServerControlThread()
{
while (true)
{
foreach (BluetoothClient cc in connessioniServer)
{
if (!cc.Connected)
{
connessioniServer.Remove(cc);
break;
}
}
updateConnList();
Thread.Sleep(0);
}
}
Guid mUUID = new Guid("fc5ffc49-00e3-4c8b-9cf1-6b72aad1001a");
private void ServerConnectThread()
{
updateUI("server started");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
while (true)
{
BluetoothClient conn = blueListener.AcceptBluetoothClient();
connessioniServer.Add(conn);
Thread appoggio = new Thread(new ParameterizedThreadStart(ThreadAscoltoClient));
appoggio.IsBackground = true;
appoggio.Start(conn);
updateUI(conn.RemoteMachineName+" has connected");
}
}
private void ThreadAscoltoClient(object obj)
{
BluetoothClient clientServer = (BluetoothClient)obj;
Stream streamServer = clientServer.GetStream();
streamServer.ReadTimeout=1000;
while (clientServer.Connected)
{
try
{
int bytesDaLeggere = clientServer.Available;
if (bytesDaLeggere > 0)
{
byte[] bytesLetti = new byte[bytesDaLeggere];
int byteLetti = 0;
while (bytesDaLeggere > 0)
{
int bytesDavveroLetti = streamServer.Read(bytesLetti, byteLetti, bytesDaLeggere);
bytesDaLeggere -= bytesDavveroLetti;
byteLetti += bytesDavveroLetti;
}
updateUI("message sent from "+clientServer.RemoteMachineName+": " + System.Text.Encoding.Default.GetString(bytesLetti));
}
}
catch { }
Thread.Sleep(0);
}
updateUI(clientServer.RemoteMachineName + " has gone");
}
private void updateUI(string message)
{
Func<int> del = delegate()
{
textBox1.AppendText(message + System.Environment.NewLine);
return 0;
};
Invoke(del);
}
private void updateConnList()
{
Func<int> del = delegate()
{
listaSensori.Items.Clear();
foreach (BluetoothClient d in connessioniServer)
{
listaSensori.Items.Add(d.RemoteMachineName);
}
return 0;
};
try
{
Invoke(del);
}
catch { }
}
I don't exactly understand how you do it right now (the italian names are not helping...) but maybe my solution can help you.
first of all, bluetooth classic does not support broadcast. so you have to deliver at one at a time.
i do connect to 7 serial port devices at a time, using 7 threads. then i tell every thread to send data. this is very close to same time, but of course not exactly.
let me know if that helps or if you need a code example.