Created
December 7, 2011 16:58
-
-
Save simonwh/1443584 to your computer and use it in GitHub Desktop.
Async sockets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Descent.Messaging.AsyncSockets | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
public delegate void MessageReceivedHandler(ClientInfo clientInfo, string message); | |
public delegate void ConnectionClosedHandler(ClientInfo clientInfo); | |
/// <summary> | |
/// Implementation of the asynchronous sockets client. | |
/// </summary> | |
public class AsyncSocketsClient | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="AsyncSocketsClient"/> class. | |
/// </summary> | |
/// <param name="address">Address of the server to connect to.</param> | |
/// <param name="port">Port of the server to connect to.</param> | |
public AsyncSocketsClient(string address, int port) | |
{ | |
IPAddress host = Dns.GetHostAddresses(address)[0]; | |
Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |
Socket.Connect(new IPEndPoint(host, port)); | |
} | |
public Socket Socket { get; private set; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Descent.Messaging.AsyncSockets | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
public delegate bool ClientConnectedHandler(ClientInfo clientInfo); | |
/// <summary> | |
/// Implementation of the asynchronous sockets server. | |
/// </summary> | |
public class AsyncSocketsServer | |
{ | |
private byte[] bytes = new byte[1024]; | |
private Socket socket; | |
private int port; | |
// Contains all the connected clients. | |
private Dictionary<int, ClientInfo> clients = new Dictionary<int, ClientInfo>(); | |
/// <summary> | |
/// Initializes a new instance of the <see cref="AsyncSocketsServer"/> class. | |
/// </summary> | |
/// <param name="port"> | |
/// Port that the server should use. | |
/// </param> | |
public AsyncSocketsServer(int port) | |
{ | |
this.port = port; | |
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |
} | |
/// <summary> | |
/// This event will be fired when a client has connected. | |
/// </summary> | |
public event ClientConnectedHandler ClientConnectedEvent; | |
public string Ip { get { return ((IPEndPoint)socket.LocalEndPoint).Address.ToString(); } } | |
/// <summary> | |
/// Start listening and accepting clients. | |
/// </summary> | |
public void Start() | |
{ | |
socket.Bind(new IPEndPoint(IPAddress.Any, port)); | |
socket.Listen(100); | |
socket.BeginAccept(new AsyncCallback(AcceptCallback), socket); | |
} | |
/// <summary> | |
/// Broadcast a string to all connected clients. | |
/// </summary> | |
/// <param name="message">The string to broadcast</param> | |
public void Broadcast(string message) | |
{ | |
foreach (ClientInfo clientInfo in clients.Values) | |
{ | |
clientInfo.Send(message); | |
} | |
} | |
/// <summary> | |
/// Broadcast a string to all connected clients except one. | |
/// </summary> | |
/// <param name="message">The string to broadcast</param> | |
/// <param name="clientId">The client which the server shouldn't send to.</param> | |
public void BroadcastExceptClient(string message, int clientId) | |
{ | |
foreach (ClientInfo clientInfo in clients.Values) | |
{ | |
if (clientInfo.Id != clientId) clientInfo.Send(message); | |
} | |
} | |
/// <summary> | |
/// Called when a client was connected. | |
/// </summary> | |
/// <param name="asyncResult">Asynchronous result</param> | |
private void AcceptCallback(IAsyncResult asyncResult) | |
{ | |
Socket server = (Socket)asyncResult.AsyncState; | |
Socket client = server.EndAccept(asyncResult); | |
ClientInfo clientInfo = new ClientInfo(client); | |
server.BeginAccept(new AsyncCallback(AcceptCallback), server); | |
// If we have a delegate and he returns false, we close the connection. | |
if (ClientConnectedEvent != null && !ClientConnectedEvent(clientInfo)) | |
{ | |
client.Close(); | |
return; | |
} | |
// Add client to collection of clients | |
clients.Add(clientInfo.Id, clientInfo); | |
clientInfo.ConnectionClosedEvent += new ConnectionClosedHandler(ClientConnectionDisconnected); | |
// Start receiving data. See ClientInfo class for details. | |
clientInfo.BeginReceive(); | |
} | |
/// <summary> | |
/// Called when a client disconnects. Removes the client from the collection. | |
/// </summary> | |
/// <param name="clientInfo">Disconnected client</param> | |
private void ClientConnectionDisconnected(ClientInfo clientInfo) | |
{ | |
clients.Remove(clientInfo.Id); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Descent.Messaging.AsyncSockets | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Text; | |
/// <summary> | |
/// Stores information about a client connection. | |
/// </summary> | |
public class ClientInfo | |
{ | |
private const int BUFSIZE = 1024; | |
private readonly Socket socket; | |
private static int nextId = 2; | |
private StringBuilder str = new StringBuilder(); | |
private byte[] buf = new byte[BUFSIZE]; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ClientInfo"/> class. | |
/// </summary> | |
/// <param name="clientSocket">Socket of the client.</param> | |
public ClientInfo(Socket clientSocket) | |
{ | |
socket = clientSocket; | |
Id = nextId++; | |
} | |
public event MessageReceivedHandler MessageReceivedEvent; | |
public event ConnectionClosedHandler ConnectionClosedEvent; | |
public int Id { get; set; } | |
public string Ip { get { return ((IPEndPoint)socket.LocalEndPoint).Address.ToString(); } } | |
/// <summary> | |
/// Send a string. | |
/// </summary> | |
/// <param name="message">String to send</param> | |
public void Send(string message) | |
{ | |
// Sends through the socket. Appends <EOF> to indicate end of message. | |
socket.Send(Encoding.UTF8.GetBytes(message + "<EOF>")); | |
} | |
/// <summary> | |
/// Begin receiving data asynchronously. | |
/// </summary> | |
public void BeginReceive() | |
{ | |
socket.BeginReceive(buf, 0, BUFSIZE, 0, new AsyncCallback(BeginReceiveCallback), this); | |
} | |
/// <summary> | |
/// Called when data was received. | |
/// </summary> | |
/// <param name="asyncResult">Async result</param> | |
private void BeginReceiveCallback(IAsyncResult asyncResult) | |
{ | |
int read = socket.EndReceive(asyncResult); | |
if (read > 0) | |
{ | |
// Append to string buffer | |
str.Append(Encoding.UTF8.GetString(buf, 0, read)); | |
string content = str.ToString(); | |
// Check for end of message | |
if (content.IndexOf("<EOF>") > -1) | |
{ | |
string message = content.Substring(0, content.Length - 5); | |
if (MessageReceivedEvent != null) | |
{ | |
MessageReceivedEvent(this, message); | |
} | |
str.Clear(); | |
} | |
BeginReceive(); | |
} | |
else | |
{ | |
// No data received - connection closed | |
ConnectionClosedEvent(this); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment