.Net 3.5 Asynchronous Socket Server Performance Problem - c#

I'm developing an Asynchronous Game Server using .Net Socket Asynchronous Model( BeginAccept/EndAccept...etc.)
The problem I'm facing is described like that:
When I have only one client connected, the server response time is very fast but once a second client connects, the server response time increases too much.
I've measured the time from a client sends a message to the server until it gets the reply in both cases. I found that the average time in case of one client is about 17ms and in case of 2 clients about 280ms!!!
What I really see is that: When 2 clients are connected and only one of them is moving(i.e. requesting service from the server) it is equivalently equal to the case when only one client is connected(i.e. fast response). However, when the 2 clients move at the same time(i.e. requests service from the server at the same time) their motion becomes very slow (as if the server replies each one of them in order i.e. not simultaneously).
Basically, what I am doing is that:
When a client requests a permission for motion from the server and the server grants him the request, the server then broadcasts the new position of the client to all the players. So if two clients are moving in the same time, the server is eventually trying to broadcast to both clients the new position of each of them at the same time.
EX:
Client1 asks to go to position (2,2)
Client2 asks to go to position (5,5)
Server sends to each of Client1 & Client2 the same two messages:
message1: "Client1 at (2,2)"
message2: "Client2 at (5,5)"
I believe that the problem comes from the fact that Socket class is thread safe according MSDN documentation http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx. (NOT SURE THAT IT IS THE PROBLEM)
Below is the code for the server:
///
/// This class is responsible for handling packet receiving and sending
///
public class NetworkManager
{
///
/// An integer to hold the server port number to be used for the connections. Its default value is 5000.
///
private readonly int port = 5000;
///
/// hashtable contain all the clients connected to the server.
/// key: player Id
/// value: socket
///
private readonly Hashtable connectedClients = new Hashtable();
///
/// An event to hold the thread to wait for a new client
///
private readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
///
/// keeps track of the number of the connected clients
///
private int clientCount;
///
/// The socket of the server at which the clients connect
///
private readonly Socket mainSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
///
/// The socket exception that informs that a client is disconnected
///
private const int ClientDisconnectedErrorCode = 10054;
///
/// The only instance of this class.
///
private static readonly NetworkManager networkManagerInstance = new NetworkManager();
///
/// A delegate for the new client connected event.
///
/// the sender object
/// the event args
public delegate void NewClientConnected(Object sender, SystemEventArgs e);
///
/// A delegate for the position update message reception.
///
/// the sender object
/// the event args
public delegate void PositionUpdateMessageRecieved(Object sender, PositionUpdateEventArgs e);
///
/// The event which fires when a client sends a position message
///
public PositionUpdateMessageRecieved PositionUpdateMessageEvent
{
get;
set;
}
///
/// keeps track of the number of the connected clients
///
public int ClientCount
{
get
{
return clientCount;
}
}
///
/// A getter for this class instance.
///
/// only instance.
public static NetworkManager NetworkManagerInstance
{
get
{
return networkManagerInstance;
}
}
private NetworkManager()
{}
/// Starts the game server and holds this thread alive
///
public void StartServer()
{
//Bind the mainSocket to the server IP address and port
mainSocket.Bind(new IPEndPoint(IPAddress.Any, port));
//The server starts to listen on the binded socket with max connection queue //1024
mainSocket.Listen(1024);
//Start accepting clients asynchronously
mainSocket.BeginAccept(OnClientConnected, null);
//Wait until there is a client wants to connect
resetEvent.WaitOne();
}
///
/// Receives connections of new clients and fire the NewClientConnected event
///
private void OnClientConnected(IAsyncResult asyncResult)
{
Interlocked.Increment(ref clientCount);
ClientInfo newClient = new ClientInfo
{
WorkerSocket = mainSocket.EndAccept(asyncResult),
PlayerId = clientCount
};
//Add the new client to the hashtable and increment the number of clients
connectedClients.Add(newClient.PlayerId, newClient);
//fire the new client event informing that a new client is connected to the server
if (NewClientEvent != null)
{
NewClientEvent(this, System.EventArgs.Empty);
}
newClient.WorkerSocket.BeginReceive(newClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), newClient);
//Start accepting clients asynchronously again
mainSocket.BeginAccept(OnClientConnected, null);
}
/// Waits for the upcoming messages from different clients and fires the proper event according to the packet type.
///
///
private void WaitForData(IAsyncResult asyncResult)
{
ClientInfo sendingClient = null;
try
{
//Take the client information from the asynchronous result resulting from the BeginReceive
sendingClient = asyncResult.AsyncState as ClientInfo;
// If client is disconnected, then throw a socket exception
// with the correct error code.
if (!IsConnected(sendingClient.WorkerSocket))
{
throw new SocketException(ClientDisconnectedErrorCode);
}
//End the pending receive request
sendingClient.WorkerSocket.EndReceive(asyncResult);
//Fire the appropriate event
FireMessageTypeEvent(sendingClient.ConvertBytesToPacket() as BasePacket);
// Begin receiving data from this client
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
catch (SocketException e)
{
if (e.ErrorCode == ClientDisconnectedErrorCode)
{
// Close the socket.
if (sendingClient.WorkerSocket != null)
{
sendingClient.WorkerSocket.Close();
sendingClient.WorkerSocket = null;
}
// Remove it from the hash table.
connectedClients.Remove(sendingClient.PlayerId);
if (ClientDisconnectedEvent != null)
{
ClientDisconnectedEvent(this, new ClientDisconnectedEventArgs(sendingClient.PlayerId));
}
}
}
catch (Exception e)
{
// Begin receiving data from this client
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
}
///
/// Broadcasts the input message to all the connected clients
///
///
public void BroadcastMessage(BasePacket message)
{
byte[] bytes = message.ConvertToBytes();
foreach (ClientInfo client in connectedClients.Values)
{
client.WorkerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendAsync, client);
}
}
///
/// Sends the input message to the client specified by his ID.
///
///
/// The message to be sent.
/// The id of the client to receive the message.
public void SendToClient(BasePacket message, int id)
{
byte[] bytes = message.ConvertToBytes();
(connectedClients[id] as ClientInfo).WorkerSocket.BeginSend(bytes, 0, bytes.Length,
SocketFlags.None, SendAsync, connectedClients[id]);
}
private void SendAsync(IAsyncResult asyncResult)
{
ClientInfo currentClient = (ClientInfo)asyncResult.AsyncState;
currentClient.WorkerSocket.EndSend(asyncResult);
}
/// Fires the event depending on the type of received packet
///
/// The received packet.
void FireMessageTypeEvent(BasePacket packet)
{
switch (packet.MessageType)
{
case MessageType.PositionUpdateMessage:
if (PositionUpdateMessageEvent != null)
{
PositionUpdateMessageEvent(this, new PositionUpdateEventArgs(packet as PositionUpdatePacket));
}
break;
}
}
}
The events fired are handled in a different class, here are the event handling code for the PositionUpdateMessage (Other handlers are irrelevant):
private readonly Hashtable onlinePlayers = new Hashtable();
///
/// Constructor that creates a new instance of the GameController class.
///
private GameController()
{
//Start the server
server = new Thread(networkManager.StartServer);
server.Start();
//Create an event handler for the NewClientEvent of networkManager
networkManager.PositionUpdateMessageEvent += OnPositionUpdateMessageReceived;
}
///
/// this event handler is called when a client asks for movement.
///
private void OnPositionUpdateMessageReceived(object sender, PositionUpdateEventArgs e)
{
Point currentLocation = ((PlayerData)onlinePlayers[e.PositionUpdatePacket.PlayerId]).Position;
Point locationRequested = e.PositionUpdatePacket.Position;
((PlayerData)onlinePlayers[e.PositionUpdatePacket.PlayerId]).Position = locationRequested;
// Broadcast the new position
networkManager.BroadcastMessage(new PositionUpdatePacket
{
Position = locationRequested,
PlayerId = e.PositionUpdatePacket.PlayerId
});
}

You should move
//Start accepting clients asynchronously again
mainSocket.BeginAccept(OnClientConnected, null);
To right after the line
ClientInfo newClient = new ClientInfo
So that you don't block on accepting new connections longer than necessary.
believe that the problem comes from
the fact that Socket class is thread
safe
That shouldn't be relevant for your scenario.
Did you remember to set the NoDelay property on your socket to disable Nagling?

Related

Reusing connections in Azure Relay Bus

we're looking into connecting Azure websites with on-premises WCF services. We want to use the Azure Relay Bus for this.
There is a cost involved in setting up the TCP channel for the first time, about 3 seconds in our case. This is understood. We do cache the channel, so a next call goes faster. ( 250ms ) The channel is cached in ThreadLocal storage.
When multiple users are using the website, for every new thread used, a new instance of a channel is created, and we have to pay the price again for setting up the TCP channel.
My question are:
How do we prevent that a real user is confronted with the delay of setting up the channel?
Is it normal practice to have a TCP channel for each client thread?
Note: On-premises we use IIS auto-start or NT services to 'warm up' our WCF services, but is this the way to go for a website running on Azure? The fact that each thread has its own channel doesn't make it easier.
Code is shown below.
public static class NetTcpRelayClient<TChannel>
{
/// <summary>
/// A ThreadLocal instance of the channel, so that each thread can have an open TCP channel
/// </summary>
private static ThreadLocal<TChannel> staticChannel = new ThreadLocal<TChannel>();
/// <summary>
/// A shared instance of the ChannelFactory
/// </summary>
private static ChannelFactory<TChannel> cf = null;
/// <summary>
/// Creates the channel.
/// </summary>
/// <returns>The created channel</returns>
private static TChannel CreateChannel()
{
// get the url and access parameters from the configuration service
var address = ServiceSecurityInspector.GetNetTcpRelayBindingAddress(typeof(TContract));
string issuerName = ConfigurationManager.AppSettings["Shared.appsettings.AzureServiceBus.IssuerName"];
string issuerSecret = ConfigurationManager.AppSettings["Shared.appsettings.AzureServiceBus.IssuerSecret"];
// create a NetTcpRelayBinding,
if (cf == null)
{
cf = new ChannelFactory<TChannel>(new NetTcpRelayBinding(), new EndpointAddress(address));
cf.Endpoint.Behaviors.Add(new TransportClientEndpointBehavior
{
TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret)
});
}
TChannel channel = cf.CreateChannel();
// open the channel
IClientChannel clientChannel = channel as IClientChannel;
if (clientChannel != null)
{
clientChannel.Open();
}
return channel;
}
/// <summary>
/// Gets the channel for making a call over the relay bus.
/// Note that the channel is cached.
/// And that each thread has it's own channnel.
/// </summary>
/// <returns>The channel</returns>
public static TChannel GetChannel()
{
// check if we have a channel instance already
if (!staticChannel.IsValueCreated)
{
// no, create one
staticChannel.Value = CreateChannel();
}
else
{
// if the channel exists already
IClientChannel clientChannel = staticChannel as IClientChannel;
// check if it is open, if not, make a new one
if (clientChannel != null)
{
CommunicationState state = clientChannel.State;
// check its state
if (state == CommunicationState.Faulted)
{
// channel is in faulted state, close and recreate it
CloseChannel();
staticChannel.Value = CreateChannel();
}
else if ((state == CommunicationState.Closed) || (state == CommunicationState.Closing))
{
// channel is closed or closing, recreate it
staticChannel.Value = CreateChannel();
}
}
}
return staticChannel.Value;
}
/// <summary>
/// Closes the channel in a proper way
/// </summary>
private static void CloseChannel()
{
// always check if we still have a valid channel
if (staticChannel != null)
{
IClientChannel clientChannel = staticChannel as IClientChannel;
if (clientChannel != null)
{
// if the channel is open, we close it
if (clientChannel.State != CommunicationState.Closed)
{
clientChannel.Abort();
}
}
// and we set the static variable back to it's default ( = null )
staticChannel = null;
}
}
}

async NamedPipeClientStream implementation feedback

Presently, I have implemented named pipes using demo code from http://msdn.microsoft.com/en-us/library/bb546085.aspx. Instead of the synchronous client; however, I want to make it asynchronous. Here is my implementation, where the main program makes a call to StartClientNamedPipeListening():
/// <summary>
/// Buffer where received bytes or bytes received are stored
/// </summary>
private byte[] _byteBuffer = null;
/// <summary>
/// Callback result for reading data from the named pipe
/// </summary>
private IAsyncResult _pipeResult;
/// <summary>
/// Named object to send and receive data to and from watchdog
/// </summary>
NamedPipeClientStream _pipeClient;
/// <summary>
/// Notifies waiting threads that an event has occurred
/// </summary>
protected ManualResetEvent _pipeReadDone = new ManualResetEvent(false);
private object _pipeState = new object();
private void StartClientNamedPipeListening()
{
// open and then close the gate as soon as after one thread passed,
// i.e., put the event into a non-signaled, or closed, state:
_pipeReadDone.Reset();
// Reads the data coming in from the pipe and call the
// thread safe delegate to get the data received.
_byteBuffer = new Byte[50];
_pipeResult = _pipeClient.BeginRead(_byteBuffer, 0,
_byteBuffer.Length, PipeReadCallback, _pipeState);
// worker thread block in here (waiting for...
// _pipeReadDone.Set()), i.e., wait for the door to be opened
_pipeReadDone.WaitOne();
}
private void PipeReadCallback(IAsyncResult ar)
{
int bytesRead = 0;
// if port serial is open and..
if (_pipeClient.IsConnected)
{
// the stream can read then..
if (_pipeClient.CanRead)
{
// wait for asynchronous read to be completed
bytesRead = _pipeClient.EndRead(ar);
}
}
if (bytesRead > 0)
{
StreamString ss = new StreamString(_pipeClient);
// Validate the server's signature string
if (ss.ReadString() == "I am the one true server!")
{
// The client security token is sent with the first write.
// Send the name of the file whose contents are returned
// by the server.
ss.WriteString(#"C:\Temp\namedpipestring.txt");
// Print the file to the screen.
Console.WriteLine(ss.ReadString(), false);
}
else
{
Console.WriteLine("Server could not be verified.");
}
// put the event into a signaled, or open, state:
// open gate for next data
_pipeReadDone.Set();
// Start waiting for the next watchdog message
StartClientNamedPipeListening();
}
}
This implementation works according to my tests; however, I was wondering, am I doing some obvious no-no's? Does anyone have any suggestions on how it could possibly be implemented better? TIA.
Here is how I modified the code to get it to work asynchronously. I'm not sure why I thought I needed the ManualResetEvent:
/// <summary>
/// Buffer where received bytes or bytes received are stored
/// </summary>
private byte[] _byteBuffer = null;
/// <summary>
/// Callback result for reading data from the named pipe
/// </summary>
private IAsyncResult _pipeResult;
/// <summary>
/// Named object to send and receive data to and from watchdog
/// </summary>
NamedPipeClientStream _pipeClient;
private object _pipeState = new object();
private void StartClientNamedPipeListening()
{
_pipeClient = new NamedPipeClientStream(".", "testpipe",
PipeDirection.InOut, PipeOptions.Asynchronous,
TokenImpersonationLevel.Impersonation);
_pipeClient.Connect();
// Reads the data coming in from the pipe and call the
// thread safe delegate to get the data received.
_byteBuffer = new Byte[50];
_pipeResult = _pipeClient.BeginRead(_byteBuffer, 0,
_byteBuffer.Length, PipeReadCallback, _pipeState);
}
private void PipeReadCallback(IAsyncResult ar)
{
int bytesRead = 0;
// if port serial is open and..
if (_pipeClient.IsConnected)
{
// the stream can read then..
if (_pipeClient.CanRead)
{
// wait for asynchronous read to be completed
bytesRead = _pipeClient.EndRead(ar);
}
}
if (bytesRead > 0)
{
StreamString ss = new StreamString(_pipeClient);
// Validate the server's signature string
if (ss.ReadString() == "I am the one true server!")
{
// The client security token is sent with the first write.
// Send the name of the file whose contents are returned
// by the server.
ss.WriteString(#"C:\Temp\namedpipestring.txt");
// Print the file to the screen.
Console.WriteLine(ss.ReadString(), false);
}
else
{
Console.WriteLine("Server could not be verified.");
}
// Start waiting for the next watchdog message
StartClientNamedPipeListening();
}
}
Thanks, Hans.

Received data buffer always empty after calling socket's ReceiveAsync() call?

I have a Windows Phone 8 app that talks to server over a socket. The server is quite simple. It accepts a string, returns a string, and immediately closes the connection. I have talked to the server using Windows Forms apps and never had a problem.
I'm using the code below which I adapted from this MSDN page that shows how to use a socket in a Windows Phone 8 app:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202858(v=vs.105).aspx
I modified the code to be non-blocking using async/await. All of the methods are awaitable now and the WaitOne() call after each socket async operation is spun off to a new task. I am not getting any socket errors or Exceptions. However, when the anonymous Completed event handler for the ReceiveAsync() call fires, the bytes transferred value is always 0.
Strange note. If I set a breakpoint on certain lines in completed event handler for ReceiveAsync() the call times out. This doesn't happen if I don't set a breakpoint and it only happens on certain lines. I have no idea why. No time-outs occur if I don't set breakpoints
What am I doing wrong? Here's the code I'm using. The calling code (not shown) simply creates an instance of the SocketDetails class, and then calls in order, await ConnectAsync(), await SendAsync("somestring"), and finally await ReceiveAsync() on the SocketDetails instance.:
/// <summary>
/// Details object that holds a socket.
/// </summary>
public class SocketDetails
{
/// <summary>
/// Creates a new socket details object.
/// </summary>
/// <param name="hostName">The host name for the connection.</param>
/// <param name="portNumber">The port name for the connection.</param>
/// <param name="timeOutMS">The maximum number of milliseconds to wait for a connection before <br />
/// timing out.</param>
/// <param name="defaultBufferSize">The maximum number of bytes for the buffer that receives the <br />
/// connection result string.</param>
public SocketDetails(
string hostName,
int portNumber,
int timeOutMS,
int defaultBufferSize)
{
if (String.IsNullOrWhiteSpace(hostName))
throw new ArgumentNullException("The host name is empty.");
if (portNumber <= 0)
throw new ArgumentOutOfRangeException("The port number is less than or equal to 0.");
if (timeOutMS < 0)
throw new ArgumentOutOfRangeException("The time-out value is negative.");
this.HostName = hostName;
this.PortNumber = portNumber;
this.TimeOutMS = timeOutMS;
// Create DnsEndPoint. The hostName and port are passed in to this method.
this.HostEntry = new DnsEndPoint(this.HostName, this.PortNumber);
// Create a stream-based, TCP socket using the InterNetwork Address Family.
this.Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Create the manual reset event.
this.ClientDone = new ManualResetEvent(false);
}
/// <summary>
/// The string returned by the last connection attempt.
/// </summary>
public string ConnectionResult { get; private set; }
public DnsEndPoint HostEntry
{ get; private set; }
/// <summary>
/// The host name to open the socket on.
/// </summary>
public string HostName { get; private set; }
/// <summary>
/// The port number to use when opening the socket.
/// </summary>
public int PortNumber { get; private set; }
/// <summary>
/// Cached Socket object that will be used by each call for the lifetime of this class
/// </summary>
public Socket Socket { get; private set; }
/// <summary>
/// Signaling object used to notify when an asynchronous operation is completed. Exposing it <br />
/// so other threads/code can reset it if necessary, unblocking any threads waiting on this socket.
/// </summary>
public ManualResetEvent ClientDone { get; private set; }
/// <summary>
/// Define a timeout in milliseconds for each asynchronous call. If a response is not received within this <br />
// timeout period, the call is aborted.
/// </summary>
public int TimeOutMS { get; set; }
// The maximum size of the data buffer to use with the asynchronous socket methods
public int BufferSize { get; set; }
/// <summary>
/// Waits until a socket operation completes or the time-out period is reached.
/// </summary>
/// <returns>TRUE if the semaphore wait did not TIME-OUT, FALSE if a time-out did occur.</returns>
private bool BlockUntilSocketOperationCompletes(string caller)
{
// Sets the state of the event to nonsignaled, causing this task's thread to block.
// The completed handler of the socket method that called this method should unblock it.
this.ClientDone.Reset();
bool bRet = this.ClientDone.WaitOne(this.TimeOutMS);
if (bRet)
Debug.WriteLine("WaitOne() completed successfully for caller: " + caller);
else
Debug.WriteLine("WaitOne() timed-out for caller: " + caller);
return bRet;
}
/// <summary>
/// (awaitable) Connects to the socket using the details given in the constructor.
/// </summary>
/// <returns>Returns the banner or error message returned from the sockete during the <br />
/// connection attempt.</returns>
/// <remarks>This call BLOCKS until the connection succeeds or the time-out limit is reached!</remarks>
async public Task<string> ConnectAsync()
{
// Create a SocketAsyncEventArgs object to be used in the connection request
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = this.HostEntry;
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
// Retrieve the result of this request
this.ConnectionResult = e.SocketError.ToString();
Debug.WriteLine("CONNECT completed, Connection result string received: " + this.ConnectionResult);
// Signal that the request is complete, unblocking the UI thread
this.ClientDone.Set();
});
// Make an asynchronous Connect request over the socket
this.Socket.ConnectAsync(socketEventArg);
// Wait for the return operation to complete or until it times out.
bool bIsTimeOut = !(await Task.Run(() => BlockUntilSocketOperationCompletes("ConnectAsync")));
return this.ConnectionResult;
}
/// <summary>
/// (awaitable) Send the given data to the server using the established connection
/// </summary>
/// <param name="data">The data to send to the server</param>
/// <returns>The result of the Send request</returns>
/// <remarks>This call BLOCKS until the data is received or the attempt times out!</remarks>
async public Task<string> SendAsync(string data)
{
string response = "Operation Timeout";
// We are re-using the _socket object initialized in the Connect method
if (this.Socket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
// Set properties on context object
socketEventArg.RemoteEndPoint = this.Socket.RemoteEndPoint;
socketEventArg.UserToken = null;
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order
// to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
response = e.SocketError.ToString();
Debug.WriteLine("SEND completed, Response received: " + response);
// Unblock the UI thread
this.ClientDone.Set();
});
// Add the data to be sent into the buffer
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
// Make an asynchronous Send request over the socket
this.Socket.SendAsync(socketEventArg);
// Wait for the return operation to complete or until it times out.
bool bIsTimeOut = !(await Task.Run(() => BlockUntilSocketOperationCompletes("SendAsync")));
}
else
{
response = "Socket is not initialized";
}
return response;
}
/// <summary>
/// (awaitable) Receive data from the server using the established socket connection
/// </summary>
/// <returns>The data received from the server</returns>
async public Task<string> ReceiveAsync()
{
string response = "Operation Timeout";
// We are receiving over an established socket connection
if (this.Socket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = this.Socket.RemoteEndPoint;
// Setup the buffer to receive the data
socketEventArg.SetBuffer(new Byte[this.BufferSize], 0, this.BufferSize);
// Inline event handler for the Completed event.
// Note: This even handler was implemented inline in order to make
// this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
Debug.WriteLine("RECEIVE completed.");
if (e.SocketError == SocketError.Success)
{
// Retrieve the data from the buffer
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
Debug.WriteLine("RECEIVE completed, response received: " + response);
}
else
{
response = e.SocketError.ToString();
Debug.WriteLine("RECEIVE failed: socket error: " + response);
}
this.ClientDone.Set();
});
// Make an asynchronous Receive request over the socket
this.Socket.ReceiveAsync(socketEventArg);
bool bIsTimeOut = await Task.Run(() => BlockUntilSocketOperationCompletes("ReceiveAsync"));
}
else
{
response = "Socket is not initialized";
}
return response;
}
/// <summary>
/// Closes the Socket connection and releases all associated resources
/// </summary>
public void Close()
{
if (this.Socket != null)
{
this.Socket.Close();
}
}
} // public class SocketDetails
First off, I strongly recommend you not use TCP/IP. If at all possible, use WebAPI + HttpClient instead. This is particularly true if you are still learning async.
That said, my comments follow.
Receiving an empty array is a normal situation for a socket. It indicates that the other side has closed down its sending channel.
I don't recommend using the SocketAsyncEventArgs-based API. It's possible, but awkward to make it work with async. Instead, write TAP-over-APM wrappers (I prefer to write these as extension methods) use the TAP methods (the ones defined on SocketTaskExtensions).
The *Async wrappers in your code sample all follow a pattern where they start an asynchronous operation and then queue up a thread pool operation to wait for it to complete. This seems quite unnecessary to me; Task<T>.Factory.FromAsync or TaskCompletionSource<T> would be more efficient and less convoluted.
Finally, you should ensure you are not using a read-then-write loop, a common mistake with TCP/IP applications. There are a couple of problems with a read-then-write loop; one of them is that it cannot recover from the half-open problem, which I describe on my blog.

Windows Phone 8: Send data to a remoteserver

I am trying to implement a gps tracking for WP8. but I have no idea which code would allow me to access the socket to send data to server.
I have tried this in web form
udpClient.Connect(IP, Port)
' Sends a message to the host to which you have connected.
Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(message)
udpClient.Send(sendBytes, sendBytes.Length)
udpClient.Close()
but for windows phone. its a bit of complicated.
Please Provide me a solution
I give you a small sample how do you can send data with socket connection :
But i advice you , to go see the complete a really good official documentation from microsoft .
Use TCP - socket
How to create and use a TCP socket client app for Windows Phone
Use UDP - socket
How to create and use a UDP socket client app for Windows Phone
Easy to implement in your code
// Cached Socket object that will be used by each call for the lifetime of this class
Socket _socket = null;
// Signaling object used to notify when an asynchronous operation is completed
static ManualResetEvent _clientDone = new ManualResetEvent(false);
// Define a timeout in milliseconds for each asynchronous call. If a response is not received within this
// timeout period, the call is aborted.
const int TIMEOUT_MILLISECONDS = 5000;
// The maximum size of the data buffer to use with the asynchronous socket methods
const int MAX_BUFFER_SIZE = 2048;
/// <summary>
/// SocketClient Constructor
/// </summary>
public SocketClient()
{
// The following creates a socket with the following properties:
// AddressFamily.InterNetwork - the socket will use the IP version 4 addressing scheme to resolve an address
// SocketType.Dgram - a socket that supports datagram (message) packets
// PrototcolType.Udp - the User Datagram Protocol (UDP)
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
/// <summary>
/// Send the given data to the server using the established connection
/// </summary>
/// <param name="serverName">The name of the server</param>
/// <param name="portNumber">The number of the port over which to send the data</param>
/// <param name="data">The data to send to the server</param>
/// <returns>The result of the Send request</returns>
public string Send(string serverName, int portNumber, string data)
{
string response = "Operation Timeout";
// We are re-using the _socket object that was initialized in the Connect method
if (_socket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
// Set properties on context object
socketEventArg.RemoteEndPoint = new DnsEndPoint(serverName, portNumber);
// Inline event handler for the Completed event.
// Note: This event handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
response = e.SocketError.ToString();
// Unblock the UI thread
_clientDone.Set();
});
// Add the data to be sent into the buffer
byte[] payload = Encoding.UTF8.GetBytes(data);
socketEventArg.SetBuffer(payload, 0, payload.Length);
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
// Make an asynchronous Send request over the socket
_socket.SendToAsync(socketEventArg);
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
response = "Socket is not initialized";
}
return response;
}

C# TCP Server buffer

I have been trying to get this to work for a while and I even searched through most of the forum but I've got nothing so far.
My Issue:
Process_send works perfectly in that my client can receive everything that server sends. However, when the client says something, the buffer in the process_receive function is returned as null, even though the TransferredBytes property says "46", which is what it is supposed to be.
Please let me know how I can fix this issue. Thanks!
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.Net.Sockets;
using System.Threading;
using System.Net;
namespace MonkeySyncServer
{
public partial class ServerForm : Form
{
public ServerForm()
{
InitializeComponent();
}
private void btn_startServer_Click(object sender, EventArgs e)
{
Server s = new Server(100, 1024);
s.Init();
s.Start(new IPEndPoint(IPAddress.Any, 1000));
}
}
/// <summary>
/// Implements the connection logic for the socket server.
/// </summary>
class Server
{
private int m_numConnections; // the maximum number of connections the sample is designed to handle simultaneously
private int m_receiveBufferSize;// buffer size to use for each socket I/O operation
BufferManager m_bufferManager; // represents a large reusable set of buffers for all socket operations
const int opsToPreAlloc = 2; // read, write (don't alloc buffer space for accepts)
Socket listenSocket; // the socket used to listen for incoming connection requests
// pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations
SocketAsyncEventArgsPool m_readWritePool;
int m_totalBytesRead; // counter of the total # bytes received by the server
int m_numConnectedSockets; // the total number of clients connected to the server
Semaphore m_maxNumberAcceptedClients;
/// <summary>
/// Create an uninitialized server instance. To start the server listening for connection requests
/// call the Init method followed by Start method
/// </summary>
/// <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param>
/// <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param>
public Server(int numConnections, int receiveBufferSize)
{
m_totalBytesRead = 0;
m_numConnectedSockets = 0;
m_numConnections = numConnections;
m_receiveBufferSize = receiveBufferSize;
// allocate buffers such that the maximum number of sockets can have one outstanding read and
//write posted to the socket simultaneously
m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc,
receiveBufferSize);
m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
}
/// <summary>
/// Initializes the server by preallocating reusable buffers and context objects. These objects do not
/// need to be preallocated or reused, by is done this way to illustrate how the API can easily be used
/// to create reusable objects to increase server performance.
/// </summary>
public void Init()
{
// Allocates one large byte buffer which all I/O operations use a piece of. This gaurds
// against memory fragmentation
m_bufferManager.InitBuffer();
// preallocate pool of SocketAsyncEventArgs objects
SocketAsyncEventArgs readWriteEventArg;
for (int i = 0; i < m_numConnections; i++)
{
//Pre-allocate a set of reusable SocketAsyncEventArgs
readWriteEventArg = new SocketAsyncEventArgs();
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
readWriteEventArg.UserToken = new AsyncUserToken();
// assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
m_bufferManager.SetBuffer(readWriteEventArg);
// add SocketAsyncEventArg to the pool
m_readWritePool.Push(readWriteEventArg);
}
}
/// <summary>
/// Starts the server such that it is listening for incoming connection requests.
/// </summary>
/// <param name="localEndPoint">The endpoint which the server will listening for conenction requests on</param>
public void Start(IPEndPoint localEndPoint)
{
// create the socket which listens for incoming connections
listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(localEndPoint);
// start the server with a listen backlog of 100 connections
listenSocket.Listen(100);
// post accepts on the listening socket
StartAccept(null);
}
/// <summary>
/// Begins an operation to accept a connection request from the client
/// </summary>
/// <param name="acceptEventArg">The context object to use when issuing the accept operation on the
/// server's listening socket</param>
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
}
else
{
// socket must be cleared since the context object is being reused
acceptEventArg.AcceptSocket = null;
}
m_maxNumberAcceptedClients.WaitOne();
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
ProcessAccept(acceptEventArg);
}
}
/// <summary>
/// This method is the callback method associated with Socket.AcceptAsync operations and is invoked
/// when an accept operation is complete
/// </summary>
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
private void ProcessAccept(SocketAsyncEventArgs e)
{
Interlocked.Increment(ref m_numConnectedSockets);
Console.WriteLine("Client connection accepted. There are {0} clients connected to the server",
m_numConnectedSockets);
// Get the socket for the accepted client connection and put it into the
//ReadEventArg object user token
SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop();
((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
byte[] d = e.Buffer;
// As soon as the client is connected, post a receive to the connection
bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
d = d;
if (!willRaiseEvent)
{
ProcessReceive(readEventArgs);
}
// Accept the next connection request
StartAccept(e);
}
/// <summary>
/// This method is called whenever a receive or send opreation is completed on a socket
/// </summary>
/// <param name="e">SocketAsyncEventArg associated with the completed receive operation</param>
void IO_Completed(object sender, SocketAsyncEventArgs e)
{
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
private bool sendToClient(AsyncUserToken token, SocketAsyncEventArgs e, string s)
{
// Send 'Hello World' to the server
byte[] buffer = Encoding.UTF8.GetBytes(s);
Console.WriteLine("Sending: " + buffer.Length + " bytes of data");
e.SetBuffer(buffer, 0, buffer.Length);
bool willRaiseEvent = token.Socket.SendAsync(e);
return willRaiseEvent;
}
/// <summary>
/// This method is invoked when an asycnhronous receive operation completes. If the
/// remote host closed the connection, then the socket is closed. If data was received then
/// the data is echoed back to the client.
/// </summary>
private void ProcessReceive(SocketAsyncEventArgs e)
{
m_bufferManager.SetBuffer(e);
// check if the remote host closed the connection
AsyncUserToken token = (AsyncUserToken)e.UserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
//increment the count of the total bytes receive by the server
Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
// Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead);
Console.WriteLine("Received from client: {0}", Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred));
byte[] data = new byte[1024];
data = e.Buffer;
String dataString = Encoding.UTF8.GetString(data);
if (dataString.StartsWith("hello"))
{
token.encryptionKey = Encoding.UTF8.GetString(data).Substring(5, dataString.IndexOf("world") - 5);
Console.WriteLine(token.encryptionKey);
}
/* bool willRaiseEvent = sendToClient(token, e, "hello world");
if (!willRaiseEvent)
{
ProcessSend(e);
}*/
}
else
{
CloseClientSocket(e);
}
}
/// <summary>
/// This method is invoked when an asynchronous send operation completes. The method issues another receive
/// on the socket to read any additional data sent from the client
/// </summary>
/// <param name="e"></param>
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
// done echoing data back to the client
AsyncUserToken token = (AsyncUserToken)e.UserToken;
// read the next block of data send from the client
bool willRaiseEvent = token.Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
// e.SetBuffer(e.Offset, listenSocket.ReceiveBufferSize);
// ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
private void CloseClientSocket(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken;
// close the socket associated with the client
try
{
token.Socket.Shutdown(SocketShutdown.Send);
}
// throws if client process has already closed
catch (Exception) { }
token.Socket.Close();
// decrement the counter keeping track of the total number of clients connected to the server
Interlocked.Decrement(ref m_numConnectedSockets);
m_maxNumberAcceptedClients.Release();
Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
// Free the SocketAsyncEventArg so they can be reused by another client
m_readWritePool.Push(e);
}
}
}
Just a wild-guess based on the code. I suggest you step through this code with a debugger, the answer should be pretty clear when you do.
These lines are suspicious:
byte[] data = new byte[1024];
data = e.Buffer; // reassigns data, so that byte[1024] gets ignored.
String dataString = Encoding.UTF8.GetString(data); // Ignores the e.Offset value.
My guess is that the buffer gets zero-initialized, but for reasons unknown to me the socket data is not read into the start of the buffer, but rather somewhere else. The UTF8 encoder sees a 0 as the first character (0 being the end-of-string signifier in UTF8) and returns an empty string, which is different from null.
Replace the three lines with:
String dataString = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);

Categories

Resources