I wrote a C# game server that usually has several hundred people connected to it, and based it on this example from Microsoft. The players are always sending data back and forth, and the connections are rarely idle.
I have started wondering though: is every player connection launching its own Thread? Would it be better performance-wise if I recoded it to use 'Tasks/Await/etc' instead?
[EDIT]
Is the way I'm calling Thread.Sleep() an appropriate way to throttle someone's connection? Or, instead of using Thread.Sleep(), should I have it launch a task with Task.Delay() that contains SendBytes()
public class Server
{
void Listen()
{
IPAddress IpAddr = IPAddress.Parse(ipv4_local);
IPEndPoint ipEnd = new IPEndPoint(IpAddr, port);
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(ipEnd);
listener.Listen(5000);
while (true)
{
listener_allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptConnection), listener);
listener_allDone.WaitOne();
}
}
void AcceptConnection(IAsyncResult ar)
{
listener_allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
Player player = new Player(handler);
player.InitReceive();
}
}
And the player class, where Thread.Sleep() is used to throttle...
public class Player
{
public Socket socket;
public byte[] send_buffer = new byte[2048];
public byte[] receive_buffer = new byte[2048];
public Player(Socket socket)
{
this.socket = socket;
}
void SendBytes()
{
socket.BeginSend(send_buffer, 0, send_buffer.Length, SocketFlags.None, OnSendComplete, null);
}
void OnSendComplete(System.IAsyncResult iar)
{
int bytes_sent = socket.EndSend(iar);
if (MORE BYTES TO SEND)
{
Thread.Sleep(100); //THROTTLE
SendBytes();
}
}
public void InitReceive()
{
socket.BeginReceive(receive_buffer, 0, receive_buffer.Length, 0, new AsyncCallback(ReadCallback), this);
}
public void ReadCallback(IAsyncResult ar)
{
Player player = (Player)ar.AsyncState;
Socket socket = player.socket;
int bytesRead = socket.EndReceive(ar);
if (bytesRead != 0)
{
// READ BYTES...
// PROCESS DATA...
if (NEED TO SEND RESPONSE)
{
SendBytes();
}
socket.BeginReceive(player.receive_buffer, 0, player.receive_buffer.Length, 0, new AsyncCallback(ReadCallback), player);
}
}
}
Related
I want to send some images from server to client. I want when user connect to server and request for images, i sent some images to that. I searched but all of answers about send one image from client to server not server to client!
I wrote below code for send one image from server to client, but i get below error in runtime, because i shutdown socket and then called 'SendCallback' method. Where i shutdown and close socket?
public class ImageSocketServer
{
public class StateObject
{
// Client socket.
public Socket WorkSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] Buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public ManualResetEvent AllDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private Socket handler;
public void StartListening(string ip, int port)
{
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(serverEndPoint);
listener.Listen(port);
while (true)
{
AllDone.Reset();
listener.BeginAccept(AcceptCallback, listener);
AllDone.WaitOne();
}
}
private void AcceptCallback(IAsyncResult asyncResult)
{
AllDone.Set();
Socket listener = (Socket)asyncResult.AsyncState;
Socket handler = listener.EndAccept(asyncResult);
StateObject state = new StateObject { WorkSocket = handler };
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0,
ReadCallback, state);
}
private void ReadCallback(IAsyncResult asyncResult)
{
string content = string.Empty;
StateObject state = (StateObject)asyncResult.AsyncState;
handler = state.WorkSocket;
int bytesRead = handler.EndReceive(asyncResult);
if (bytesRead > 0)
{
state.sb.Append(Encoding.UTF8.GetString(state.Buffer, 0, bytesRead));
content = state.sb.ToString();
if (content.IndexOf(Cache.EndOfMessage, StringComparison.Ordinal) > -1)
{
//Send(handler, #"C:\Users\f.hashemian\Pictures\Camera Roll\test.jpg");
SendImage(handler, #"C:\Users\f.hashemian\Pictures\Camera Roll\test.jpg");
//
}
else
{
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, state);
}
}
}
private void SendImage(Socket client, string imagePath)
{
byte[] imgBuff = File.ReadAllBytes(imagePath);
var length = imgBuff.Length;
Send(client, BitConverter.GetBytes(length));
sendDone.WaitOne();
Send(client, imgBuff);
sendDone.WaitOne();
//Here close connection and i have error in SendCallback
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
private void Send(Socket client, byte[] byteData)
{
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult asyncResult)
{
try
{
Socket handler = (Socket)asyncResult.AsyncState;
int byteSent = handler.EndSend(asyncResult);
sendDone.Set();
//
}
catch (Exception ex)
{
throw ex;
}
}
}
This is about send one image with send size of image and then send image to client. If you have any tutorial about some images, please send link here
According to MSDN documentation https://learn.microsoft.com/en-us/dotnet/api/system.threading.manualresetevent.set?view=netframework-1.1#System_Threading_ManualResetEvent_Set, you should call Reset() method after calling WaitOne() on ManualResetEvent to set it in nonsignaled state.
private void SendImage(Socket client, string imagePath)
{
byte[] imgBuff = File.ReadAllBytes(imagePath);
var length = imgBuff.Length;
Send(client, BitConverter.GetBytes(length));
sendDone.WaitOne();
sendDone.Reset();
Send(client, imgBuff);
sendDone.WaitOne();
sendDone.Reset();
//Here close connection and i have error in SendCallback
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
Otherwise, the second WaitOne() will be skipped (since the event is in signaled state) and the Shutdown() method will be called. As a result, you will receive an exception in SendCallback, because the connection is already closed. In your case, it is better to use AutoResetEvent https://learn.microsoft.com/en-us/dotnet/api/system.threading.autoresetevent?view=netframework-4.8 which resets automatically after releasing a single waiting thread.
i am new at coding, especially with async, and have been trying to implement it for the first time on a udp server for a game im working on(1v1's).
In a synchronous udp server, the code stops when we do receivefrom, so how can i do the same in async? Currently the console goes through the method GetConnection() and simply closes, instead of waiting for a client. Help please.
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket clientSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
class UDPServer
{
Socket serverSocket;
IPEndPoint localIPEP;
IPEndPoint senderIPEP;
EndPoint sender;
IPEndPoint[,] playerList;
int playerListIndex;
bool waitingForSecondClient;
public UDPServer(IPEndPoint serverIpEndPoint)
{
localIPEP = serverIpEndPoint;
serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
serverSocket.Bind(localIPEP);
senderIPEP = new IPEndPoint(IPAddress.Any, 0);
sender = senderIPEP;
playerList = new IPEndPoint[5000, 2]; // 5000 possible player lobbies, each with 2 player ip addresses and ports
playerListIndex = 0; // we start filling up the lobbies from 0
Console.WriteLine("Server setup complete.");
}
public void GetConnection()
{
StateObject state = new StateObject();
Console.WriteLine("Waiting for new client.");
serverSocket.BeginReceiveFrom(state.buffer, 0, StateObject.BufferSize, SocketFlags.None,ref sender, ClientConnected, state);
}
public void ClientConnected(IAsyncResult asyncResult)
{
StateObject state = (StateObject)asyncResult;
EndPoint remote = state.clientSocket.LocalEndPoint;
StateObject tempState = new StateObject();
int bytesReceived = serverSocket.EndReceiveFrom(asyncResult, ref remote);
serverSocket.BeginReceiveFrom(tempState.buffer, 0, StateObject.BufferSize, SocketFlags.None, ref remote, ClientConnected, tempState);
Console.WriteLine("-------------");
Console.WriteLine("Received bytes of data: " + bytesReceived);
Console.WriteLine("-------------");
Console.WriteLine("Received string: " + state.sb.ToString());
Console.WriteLine("-------------");
if (state.sb.ToString().Equals("New client"))
{
Send(state.clientSocket, "Hello");
}
}
private void Send(Socket client,string message)
{
EndPoint remote = client.LocalEndPoint;
StateObject state = new StateObject();
// Begin sending the data to the remote device.
serverSocket.BeginSendTo(state.buffer, 0, StateObject.BufferSize, 0,remote,SendCallback,state);
}
private static void SendCallback(IAsyncResult asyncResult)
{
// Retrieve the socket from the state object.
Socket client = (Socket)asyncResult.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSendTo(asyncResult);
}
static void Main(string[] args)
{
UDPServer server = new UDPServer(new IPEndPoint(IPAddress.Any, 9050));
server.GetConnection();
}
}
This doesn't directly solve your problem, but have you looked at System.IO.Pipelines? It was designed precisely to help with the handling of asynchronous streams with the async/await paradigm.
https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/
im pretty to new to programming and i've just now wrote my first async server, but as soon as i start the program, it closes right after writing waiting for new client, can someone help me out? I dont know what stupid thing im doing.
I have a class UDPServer with some methods, of which
- UDPServer() receives an ipendpoint of the localserver ipaddress and port to initialize stuff (called in main to create a new UDP server object)
- right after that i call the get connection method, which supposedly should make the server wait for a new client, but instead it immediately closes.
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket clientSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
class UDPServer
{
Socket serverSocket;
IPEndPoint localIPEP;
IPEndPoint senderIPEP;
EndPoint sender;
IPEndPoint[,] playerList;
int playerListIndex;
bool waitingForSecondClient;
public UDPServer(IPEndPoint serverIpEndPoint)
{
localIPEP = serverIpEndPoint;
serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
serverSocket.Bind(localIPEP);
senderIPEP = new IPEndPoint(IPAddress.Any, 0);
sender = senderIPEP;
playerList = new IPEndPoint[5000, 2]; // 5000 possible player lobbies, each with 2 player ip addresses and ports
playerListIndex = 0; // we start filling up the lobbies from 0
Console.WriteLine("Server setup complete.");
}
public void GetConnection()
{
StateObject state = new StateObject();
Console.WriteLine("Waiting for new client.");
serverSocket.BeginReceiveFrom(state.buffer, 0, StateObject.BufferSize, SocketFlags.None,ref sender, ClientConnected, state);
}
public void ClientConnected(IAsyncResult asyncResult)
{
StateObject state = (StateObject)asyncResult;
EndPoint remote = state.clientSocket.LocalEndPoint;
StateObject tempState = new StateObject();
int bytesReceived = serverSocket.EndReceiveFrom(asyncResult, ref remote);
serverSocket.BeginReceiveFrom(tempState.buffer, 0, StateObject.BufferSize, SocketFlags.None, ref remote, ClientConnected, tempState);
Console.WriteLine("-------------");
Console.WriteLine("Received bytes of data: " + bytesReceived);
Console.WriteLine("-------------");
Console.WriteLine("Received string: " + state.sb.ToString());
Console.WriteLine("-------------");
if (state.sb.ToString().Equals("New client"))
{
Send(state.clientSocket, "Hello");
}
}
private void Send(Socket client,string message)
{
EndPoint remote = client.LocalEndPoint;
StateObject state = new StateObject();
// Begin sending the data to the remote device.
serverSocket.BeginSendTo(state.buffer, 0, StateObject.BufferSize, 0,remote,SendCallback,state);
}
private static void SendCallback(IAsyncResult asyncResult)
{
// Retrieve the socket from the state object.
Socket client = (Socket)asyncResult.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSendTo(asyncResult);
}
static void Main(string[] args)
{
UDPServer server = new UDPServer(new IPEndPoint(IPAddress.Any, 9050));
server.GetConnection();
}
}
BeginReceiveFrom Does not block, as such program execution reaches the end of your main method and finishes. I am not sure what you intend your exit condition to be, but you will need some sort of code making sure the server waits until that exit condition is met before finishing program execution.
This is my current setup (using UDP):
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packet
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
}
I was wondering what would happen if I immediately call socket.BeginReceive after calling EndReceive and then process the packet to obtain a continous packet flow like this:
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packets
}
If a packet is received as soon as I call BeginReceive, would this conflict with the current packet processing somehow?
Also if this would not conflict would changing to TCP make this disfunctional?
Looks like you are creating some sort of recursive handler there. I am unsure how that will work, probably not in a good way. I usually go for a separate reader thread that listens to incoming data and passes it on to an event. This has served me well in the past. I have not looked into using async for this though.
Here is some example code on how to use a separate thread to handle incoming UDP data. It is not complete but should give you an idea of how to set it up.
private Thread _udpReadThread;
private volatile bool _terminateThread;
public event DataEventHandler OnDataReceived;
public delegate void DataEventHandler(object sender, DataEventArgs e);
private void CreateUdpReadThread()
{
_udpReadThread = new Thread(UdpReadThread) { Name = "UDP Read thread" };
_udpReadThread.Start(new IPEndPoint(IPAddress.Any, 1234));
}
private void UdpReadThread(object endPoint)
{
var myEndPoint = (EndPoint)endPoint;
var udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// Important to specify a timeout value, otherwise the socket ReceiveFrom()
// will block indefinitely if no packets are received and the thread will never terminate
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
udpListener.Bind(myEndPoint);
try
{
while (!_terminateThread)
{
try
{
var buffer = new byte[1024];
var size = udpListener.ReceiveFrom(buffer, ref myEndPoint);
Array.Resize(ref buffer, size);
// Let any consumer(s) handle the data via an event
FireOnDataReceived(((IPEndPoint)(myEndPoint)).Address, buffer);
}
catch (SocketException socketException)
{
// Handle socket errors
}
}
}
finally
{
// Close Socket
udpListener.Shutdown(SocketShutdown.Both);
udpListener.Close();
}
}
public class DataEventArgs : EventArgs
{
public byte[] Data { get; private set; }
public IPAddress IpAddress { get; private set; }
public DataEventArgs(IPAddress ipaddress, byte[] data)
{
IpAddress = ipaddress;
Data = data;
}
}
The code shown below appears to almost work. If I create an instance of it and call "Connect" all works fine. When I call "Disconnect", sometimes everything is fine (mainly if I add a breakpoint and step through the function slowly). If I don't use a breakpoint the class(being hosted as a win forms app) seems to disappear (the form does) but visual studio still thinks it's running. In visual studio's output window I get "A first chance exception of type 'System.ObjectDisposedException' occurred in System.dll". Can anyone spot what I'm doing wrong?
// State object for reading client data asynchronously
public class StateObject
{
private Guid ID = Guid.NewGuid();
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
}
public class NetworkComms : IBasePanel
{
private static ILog _log = LogManager.GetCurrentClassLogger();
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
private static Socket _client = null;
private static IPEndPoint _endpoint = null;
public event ReceiveMessageEventHandler OnReceiveMessage;
public NetworkComms(string address, int port)
{
_endpoint = new IPEndPoint(GetIPAddress(address), port);
}
private IPAddress GetIPAddress(string address)
{
IPAddress ipAddress = null;
if (IPAddress.TryParse(address, out ipAddress))
{
return ipAddress;
}
else
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(address);
return ipHostInfo.AddressList[ipHostInfo.AddressList.Count() - 1];
}
}
private void ConnectCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
_log.DebugFormat("Socket connected to {0}", client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
private void Receive()
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = _client;
// Begin receiving the data from the remote device.
_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
private void ReceiveCallback(IAsyncResult ar)
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
ReceivedNewMessage(Encoding.Default.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
// Signal that all bytes have been received.
receiveDone.Set();
}
}
private static void SendCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
_log.DebugFormat("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
public void SendMessage(byte[] message)
{
_client.BeginSend(message, 0, message.Length, 0, new AsyncCallback(SendCallback), _client);
sendDone.WaitOne();
}
public void Connect()
{
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_client.BeginConnect(_endpoint, new AsyncCallback(ConnectCallback), _client);
connectDone.WaitOne();
Receive();
}
public void Disconnect()
{
try
{
_client.Shutdown(SocketShutdown.Both);
_client.Close();
}
finally
{
_client = null;
connectDone.Reset();
sendDone.Reset();
receiveDone.Reset();
}
}
private void ReceivedNewMessage(string message)
{
if (this.OnReceiveMessage != null)
{
this.OnReceiveMessage(message);
}
}
public bool IsConnected
{
get
{
if (_client == null) return false;
return _client.Connected;
}
}
}
All of your callbacks need to handle exceptions, which are relativly common in network programming.
In this case what is probably happening is that client.EndReceive(ar); is throwing an ObjectDisposedException because the socket is already closed when its called.