Listener (Server Socket)
This is the way that I design my sockets. Note that my chosen collection is ConcurrentQueue and it contains a data type of byte array. I like ConcurrentQueue because I don't
need lock() statements and a lock object to ensure thread safety when I add to the collection in the listener loop.
The reason that I use a byte array data type in that collection is for the sake of speed. The listener loop simply fills the byte array buffer with the
incoming bytes and stores the buffer straight into the collection.
The Producer (listener loop) is also avoiding the task of ASCII conversion. I leave the task of converting to characters to the Consumer.
The result of these strategies combined results in a listener loop that is fast enough that there is no requirement of a termination character (at least up to 1024 per message) or character sequence and the
messages never get combined together in error.
Logger logs errors to a text file.
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace Server
{
class Listener
{
private int _port;
private int sent;
private int received;
private int backLog;
private bool connected;
private bool running;
private bool debug;
private string _address;
private List<string> logEntries;
private Socket socket;
private Socket handlerRec;
private Socket handlerSnd;
private Task threadSnd;
private Task threadLstn;
private IPAddress ip;
private IPEndPoint serverEndPoint = null;
private readonly AutoResetEvent resetCache;
private Logger logger;
public ConcurrentQueue<byte[]> receivedBytes;
public ConcurrentQueue<byte[]> sendBytes;
public Listener(string address, int port)
{
_address = address;
_port = port;
sent = 0;
received = 0;
backLog = 10;
connected = false;
running = true;
debug = false;
resetCache = new AutoResetEvent(false);
logEntries = new List<string>();
receivedBytes = new ConcurrentQueue<byte[]>();
sendBytes = new ConcurrentQueue<byte[]>();
if (debug)
{
if (!Directory.Exists(@"C:\Temp")) Directory.CreateDirectory(@"C:\Temp");
logger = new Logger(@"C:\Temp\server_log.txt", true, true, true);
}
}
public void start()
{
threadLstn = Task.Factory.StartNew(new Action(listen), TaskCreationOptions.LongRunning);
threadSnd = Task.Factory.StartNew(new Action(sender), TaskCreationOptions.LongRunning);
}
public void stop()
{
socket.Close();
socket.Dispose();
}
public void restart()
{
stop();
start();
}
private void initialize()
{
try
{
if (_address == "localhost")
{
serverEndPoint = new IPEndPoint(IPAddress.Loopback, _port);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
else
{
ip = IPAddress.Parse(_address);
serverEndPoint = new IPEndPoint(ip, _port);
socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // use this one if you have a real IP
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); // Naggle disabled
socket.Blocking = true;
}
catch (Exception e) { logger.log("EXCEPTION 1 Listener: " + e.Message); }
try
{
SocketPermission permission = new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, "", SocketPermission.AllPorts);
permission.Demand();
}
catch (Exception e) { logger.log("EXCEPTION 2 Listener: " + e.Message); }
try
{ socket.Bind(serverEndPoint); }
catch (Exception e) { logger.log("EXCEPTION 3 Listener: " + e.Message); }
try
{ socket.Listen(backLog); }
catch (Exception e) { logger.log("EXCEPTION 4 Listener: " + e.Message); }
}
public void listen()
{
int bytes;
byte[] buffer;
sent = 0;
received = 0;
initialize();
connected = true;
handlerRec = socket.Accept(); // waiting for a new connection
handlerSnd = handlerRec;
while (connected) // listener loop
{
bytes = 0;
buffer = new byte[256];
try
{
bytes = handlerRec.Receive(buffer, buffer.Length, 0);
}
catch // client disconnected suddenly
{
connected = false;
break;
}
if (bytes > 0)
{
try
{
receivedBytes.Enqueue(buffer);
received++;
}
catch (Exception e) // start a new listener
{
logger.log("EXCEPTION 5 Listener: " + e.Message);
connected = false;
restart();
}
}
else
{
connected = false;
}
} // end listener loop
restart();
}
public void send(string message)
{
byte[] buffer = null;
buffer = Encoding.ASCII.GetBytes(message);
sendBytes.Enqueue(buffer);
}
private void sender()
{
int result = 0;
byte[] buffer;
while (running)
{
buffer = new byte[256];
if (sendBytes.Count != 0)
{
while (sendBytes.TryDequeue(out buffer))
{
try
{
result = handlerSnd.Send(buffer, 0, buffer.Length, SocketFlags.None);
}
catch (Exception e)
{
logger.log("EXCEPTION 6 Listener: " + e.Message);
break;
}
sent++;
}
}
Thread.Sleep(1);
}
}
}
}