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;
}
Related
Im trying to build a webservice for clients on a local network. For the service i can target any version of the .NET Framework. The clients are mobile windows devices and i would like to use the universal windows platform (UWP) as the target framework.
The service will run on multiple machines with different network addresses. My goal is that a client can automatically detect the service as soon as he connects to that local network. I want to avoid any typing of ip-addresses by the user. But all samples i can find use a hard-coded service URL. Since i have no DNS-Server i have to enter (or hard-code) the service-ip-address into the clients.
Currently im running a WCF service with a UDPDiscoveryEndpoint which does exactly what i want. But unfortunately that part of WCF (the System.ServiceModel.Discovery namespace) is not available on WinRT and also not supported on the universal windows platform. I dont have to use WCF; any alternative library with service discovery functionality would be perfect.
So here is my question: Is there any way to do service discovery on a local network in a WinRT/ UWP App? I tried ASP.NET Web API and SignalR but it seems that this HTTP based services/frameworks dont support discovery at all.
Thank you!
I've managed to do webservice discovery in UWP using socket and broadcast messages.
Plese, check my answer for more details.
EDIT - As sugested by #naveen-vijay, I'm posting a more complete aswer instead of just a link to a solution.
Every WS will listen to a specific port, awaiting for some broadcasted message searching for WS running in the LAN.
The WS implementation is win32, and this is the code needed:
private byte[] dataStream = new byte[1024];
private Socket serverSocket;
private void InitializeSocketServer(string id)
{
// Sets the server ID
this._id = id;
// Initialise the socket
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// Initialise the IPEndPoint for the server and listen on port 30000
IPEndPoint server = new IPEndPoint(IPAddress.Any, 30000);
// Associate the socket with this IP address and port
serverSocket.Bind(server);
// Initialise the IPEndPoint for the clients
IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
// Initialise the EndPoint for the clients
EndPoint epSender = (EndPoint)clients;
// Start listening for incoming data
serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveData), epSender);
}
private void ReceiveData(IAsyncResult asyncResult)
{
// Initialise the IPEndPoint for the clients
IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
// Initialise the EndPoint for the clients
EndPoint epSender = (EndPoint)clients;
// Receive all data. Sets epSender to the address of the caller
serverSocket.EndReceiveFrom(asyncResult, ref epSender);
// Get the message received
string message = Encoding.UTF8.GetString(dataStream);
// Check if it is a search ws message
if (message.StartsWith("SEARCHWS", StringComparison.CurrentCultureIgnoreCase))
{
// Create a response messagem indicating the server ID and it's URL
byte[] data = Encoding.UTF8.GetBytes($"WSRESPONSE;{this._id};http://{GetIPAddress()}:5055/wsserver");
// Send the response message to the client who was searching
serverSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, epSender, new AsyncCallback(this.SendData), epSender);
}
// Listen for more connections again...
serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(this.ReceiveData), epSender);
}
private void SendData(IAsyncResult asyncResult)
{
serverSocket.EndSend(asyncResult);
}
The client implementation is UWP. I've created the following class to do the search:
public class WSDiscoveryClient
{
public class WSEndpoint
{
public string ID;
public string URL;
}
private List<WSEndpoint> _endPoints;
private int port = 30000;
private int timeOut = 5; // seconds
/// <summary>
/// Get available Webservices
/// </summary>
public async Task<List<WSEndpoint>> GetAvailableWSEndpoints()
{
_endPoints = new List<WSEndpoint>();
using (var socket = new DatagramSocket())
{
// Set the callback for servers' responses
socket.MessageReceived += SocketOnMessageReceived;
// Start listening for servers' responses
await socket.BindServiceNameAsync(port.ToString());
// Send a search message
await SendMessage(socket);
// Waits the timeout in order to receive all the servers' responses
await Task.Delay(TimeSpan.FromSeconds(timeOut));
}
return _endPoints;
}
/// <summary>
/// Sends a broadcast message searching for available Webservices
/// </summary>
private async Task SendMessage(DatagramSocket socket)
{
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), port.ToString()))
{
using (var writer = new DataWriter(stream))
{
var data = Encoding.UTF8.GetBytes("SEARCHWS");
writer.WriteBytes(data);
await writer.StoreAsync();
}
}
}
private async void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
// Creates a reader for the incoming message
var resultStream = args.GetDataStream().AsStreamForRead(1024);
using (var reader = new StreamReader(resultStream))
{
// Get the message received
string message = await reader.ReadToEndAsync();
// Cheks if the message is a response from a server
if (message.StartsWith("WSRESPONSE", StringComparison.CurrentCultureIgnoreCase))
{
// Spected format: WSRESPONSE;<ID>;<HTTP ADDRESS>
var splitedMessage = message.Split(';');
if (splitedMessage.Length == 3)
{
var id = splitedMessage[1];
var url = splitedMessage[2];
_endPoints.Add(new WSEndpoint() { ID = id, URL = url });
}
}
}
}
}
In UWP, you can use the PeerFinder class, to discover other instances of your applications in a LAN.
I know this is not exactly service discovery, it's just peer discovery, but it should be enough for your scenario. Just use one instance of the application as your "service" that communicates with the other instances.
You can use this to find your peers and create a socket connection:
PeerFinder.DisplayName = "Doru " + Guid.NewGuid().ToString();
PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
PeerFinder.Start();
private async void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)
{
PeerInformation peer = args.PeerInformation;
StreamSocket socket = await PeerFinder.ConnectAsync(peer);
}
For a deeper understanding on how Peer Discovery works, checkout this link [minute 6:30].
I have just released an app to the Windows Phone Store, after thoroughly debugging, and the functionality when testing in debug and release mode is not the same as when I use the app that was downloaded from the store. When testing in debug and release mode from my solution I have no issues (on device or on emulator) and everything works great. After downloading from the store I only return the SocketError.NetworkDown error?
I am creating a Socket connection to gather network interface information.
private async void UpdateCurrentInterface()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// To run this application you should specify the name of a server on your network that is running
// the required service.
string serverName = "www.bing.com";
// This identifies the port over which to communicate.
int portNumber = 80;
// Create DnsEndPoint.
DnsEndPoint hostEntry = new DnsEndPoint(serverName, portNumber);
// Create a SocketAsyncEventArgs object to be used in the connection request.
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = hostEntry;
socketEventArg.UserToken = socket;
socketEventArg.Completed += ShowNetworkInterfaceInformation1;
// // Make an asynchronous Connect request over the socket.
socket.ConnectAsync(socketEventArg);
}
/// <summary>
/// Display the network information using the GetCurrentNetworkInterface extension method on the socket.
/// </summary>
/// <remarks>This is the callback from the ConnectAsync method.</remarks>
private void ShowNetworkInterfaceInformation1(object s, SocketAsyncEventArgs e)
{
// When ConnectAsync was called it was passed the socket object in
// the UserToken field of the socketEventArg. This context is retrieved once
// the ConnectAsync has completed.
Socket socket = e.UserToken as Socket;
// Only call GetCurrentNetworkInterface if the connection was successful.
if (e.SocketError == SocketError.Success)
{
NetworkInterfaceInfo netInterfaceInfo = socket.GetCurrentNetworkInterface();
// We are making UI updates, so make sure these happen on the UI thread.
Dispatcher.BeginInvoke(() =>
{
currentTypeTextBlock.Text = GetInterfaceTypeString(netInterfaceInfo.InterfaceType);
currentNameTextBlock.Text = name = netInterfaceInfo.InterfaceName;
string change = "";
if (netInterfaceInfo.InterfaceState.ToString() == "Connected")
currentStateTextBlock.Text = "connected";
else
currentStateTextBlock.Text = "disconnected";
});
}
else if (e.SocketError == SocketError.NetworkDown)
{
DisplayMessage("Could not connect.", "Network Down Error", MessageBoxButton.OK);
}
// Close our socket since we no longer need it.
socket.Close();
}
Include ID_CAP_NETWORKING as a capability within WMAppManifest.
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.
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);
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?