Skip to content

Instantly share code, notes, and snippets.

@simonwh
Created December 7, 2011 16:58
Show Gist options
  • Save simonwh/1443584 to your computer and use it in GitHub Desktop.
Save simonwh/1443584 to your computer and use it in GitHub Desktop.
Async sockets
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; }
}
}
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);
}
}
}
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