i have 3 days working with handling a disconnected client that when i disconnect a client it keeps sending nothing and when i connect it again it have another id . `
public class SocketServer : System.Windows.Forms.Form
{
internal const int MAX_CLIENTS = 10;
public AsyncCallback pfnWorkerCallBack;
private Socket m_mainSocket;
private Socket[] m_workerSocket = new Socket[30];// Hard limit of 30 TOTAL CONNECTIONS!!
private int m_clientCount = 0;
public SocketServer()
{
InitializeComponent();
// Display the local IP address on the GUI
textBoxIP.Text = GetIP();
}
[STAThread]
public static void Main(string[] args)
{
Application.Run(new SocketServer());
}
}`
internal void ButtonStartListenClick(object sender, System.EventArgs e)
{
try
{
// Check the port value
if (textBoxPort.Text == "")
{
MessageBox.Show("Please enter a Port Number");
return;
}
string portStr = textBoxPort.Text;
int port = System.Convert.ToInt32(portStr);
// Create the listening socket...
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
// Bind to local IP Address...
m_mainSocket.Bind(ipLocal);
// Start listening...
m_mainSocket.Listen(20);
// Create the call back for any client connections...
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
UpdateControls(true);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
/// <summary>
/// The UpdateControls
/// </summary>
/// <param name="listening">The listening<see cref="bool"/></param>
private void UpdateControls(bool listening)
{
buttonStartListen.Enabled = !listening;
buttonStopListen.Enabled = listening;
}
/// <summary>
/// The OnClientConnect
/// </summary>
/// <param name="asyn">The asyn<see cref="IAsyncResult"/></param>
public void OnClientConnect(IAsyncResult asyn)
{
try
{
// Here we complete/end the BeginAccept() asynchronous call
// by calling EndAccept() - which returns the reference to
// a new Socket object
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
// Display this client connection as a status message on the GUI
String str = String.Format("Client # {0} connected", m_clientCount);
SetFormStatus(str);
LogIncomingMessageToForm(str);
if (broadcastIncomingMessages)
{
SendMsgToAll(str);
}
// Send the client their ID (First thing the server sends!)
//byte[] byData = System.Text.Encoding.ASCII.GetBytes(m_clientCount.ToString());
//Console.WriteLine(byData);
//m_workerSocket[m_clientCount].Send(byData);
Invoke(new Action(() => checkedListBox1.Items.Add("client " + (m_clientCount).ToString() + " => " + m_workerSocket[m_clientCount].RemoteEndPoint.ToString(), CheckState.Unchecked)));
// Let the worker Socket do the further processing for the
// just connected client
WaitForData(m_clientCount);
// Now increment the client count
++m_clientCount;
// Since the main Socket is now free, it can go back and wait for
// other clients who are attempting to connect
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
/// <summary>
/// Defines the <see cref="SocketPacket" />
/// </summary>
public class SocketPacket
{
/// <summary>
/// Defines the m_currentSocket
/// </summary>
public System.Net.Sockets.Socket m_currentSocket;
/// <summary>
/// Defines the dataBuffer
/// </summary>
public byte[] dataBuffer = new byte[255];
/// <summary>
/// Defines the id
/// </summary>
public int id;
}
/// <summary>
/// The WaitForData
/// </summary>
/// <param name="id">The id<see cref="int"/></param>
public void WaitForData(int id)
{
Socket soc = m_workerSocket[id];
try
{
if (pfnWorkerCallBack == null)
{
// Specify the call back function which is to be
// invoked when there is any write activity by the
// connected client
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
SocketPacket theSocPkt = new SocketPacket();
theSocPkt.m_currentSocket = soc;
theSocPkt.id = id;
// Start receiving any data written by the connected client
// asynchronously
soc.BeginReceive(theSocPkt.dataBuffer, 0,
theSocPkt.dataBuffer.Length,
SocketFlags.None,
pfnWorkerCallBack,
theSocPkt);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
/// <summary>
/// The OnDataReceived
/// </summary>
/// <param name="asyn">The asyn<see cref="IAsyncResult"/></param>
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
int iRx = 0;
// Complete the BeginReceive() asynchronous call by EndReceive() method
// which will return the number of characters written to the stream
// by the client
iRx = socketData.m_currentSocket.EndReceive(asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(socketData.dataBuffer,
0, iRx, chars, 0);
System.String szData = new System.String(chars);
LogIncomingMessageToForm(socketData.id.ToString(), szData);
if (broadcastIncomingMessages)
{
SendMsgToAll(socketData.id.ToString(), szData);
}
// Continue the waiting for data on the Socket
WaitForData(socketData.id);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
then here i need to handle the disconnected client not to do that
I am currently working on a TcpServer which multiple clients can connect to at the same time. Connecting and such are fine, i see the clients connecting through server logging. But when clients starts giving input nothing happens.
The code is posted below:
TcpServer.cs:
/// <summary>
/// TcpServer
/// </summary>
internal class TcpServer
{
private readonly IConfigProvider _configProvider;
private readonly ILoggingProvider _loggingProvider;
private bool _accept;
private TcpListener _listener;
private readonly CancellationTokenSource _tokenSource;
public TcpServer(IConfigProvider configProvider, ILoggingProvider loggingProvider)
{
_configProvider = configProvider;
_loggingProvider = loggingProvider;
_tokenSource = new CancellationTokenSource();
}
/// <summary>
/// Configures the TCP server and starts the listener.
/// </summary>
public void Start()
{
try
{
var address = IPAddress.Parse(_configProvider.Core.ListenAddress);
_listener = new TcpListener(address, _configProvider.Core.ListenPort);
_listener.Start();
_accept = true;
_loggingProvider.LogInfo($"Listening for connections on port {_configProvider.Core.ListenPort}");
}
catch (Exception ex)
{
_loggingProvider.LogError($"Caught exception at TcpClient.StartServer(): {ex.Message}");
_accept = false;
}
}
/// <summary>
/// Listens on the open TCP socket for new connections.
/// </summary>
public void Listen()
{
Task.Run(async () =>
{
if (_listener != null && _accept)
{
while (true)
{
if (_tokenSource.Token.IsCancellationRequested)
{
_loggingProvider.LogInfo("Stopping TCP listener");
_accept = false;
_listener.Stop();
break;
}
var clientTask = _listener.AcceptTcpClientAsync();
if (clientTask.Result != null)
{
var client = clientTask.Result;
_loggingProvider.LogInfo($"New connection from {client.Client.RemoteEndPoint}");
await SessionManager.Instance.NewDescriptorAsync(client);
}
_loggingProvider.LogInfo($"Number of connected users: {SessionManager.Instance.Descriptors.Count}");
}
}
}, _tokenSource.Token);
}
/// <summary>
/// Stops the TCP server.
/// </summary>
public void Stop()
{
_loggingProvider.LogInfo("SHUTDOWN: Requesting cancellation of TCP listener task");
_tokenSource.Cancel();
}
}
SessionManager function that is called:
/// <summary>
/// Creates a new <see cref="Descriptor"/> from the <see cref="TcpClient"/>.
/// </summary>
/// <param name="client">The <see cref="TcpClient"/> to create the descriptor from.</param>
public async Task NewDescriptorAsync(TcpClient client)
{
var newDescriptor = new Descriptor(client);
Descriptors.Add(newDescriptor);
await newDescriptor.SendAsync("Username (new for new account): ");
}
SendAsync function for descriptor:
/// <summary>
/// Sends the specified message to the client.
/// </summary>
/// <param name="message">The message to send to the client.</param>
public async Task SendAsync(string message)
{
if (!IsConnected)
{
return;
}
var bytes = Encoding.UTF8.GetBytes(message);
await Client.GetStream().WriteAsync(bytes, 0, bytes.Length);
}
I hope someone can help me out..
Here is my many-years many times modified code for TCP asynchronous client.
It is able to detect connection loss (thanks to using keep alive values).
But now, I need to reconnect it to server automatically after any connection loss detection (any communication error or Disconnect() call).
It works fine with some simple SW TCP servers, when I stop them listening or disconnect my client from them. But, when I connect to some real devices and start to simulate possible errors, problems start.
For example, I disconnect client PC from network, then after some time of "reconnecting", application goes to connection state loop and hangs, specifically method OnDataReceived throws SocketException: ConnectionReset on iRx = stateObject.Socket.EndReceive(asyn); call.
As I am not adept at async code, I believe I do something very bad
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace SharpLib.Tcp
{
public enum ConnectionState
{
Connected, Disconnected
};
public delegate void ConnectionStateChangedEventHandler(object sender, ConnectionStateEventArgs args);
public class ConnectionStateEventArgs : EventArgs
{
public ConnectionState ConnectionState { get; set; }
public ConnectionStateEventArgs(ConnectionState state)
{
this.ConnectionState = state;
}
}
/// <summary>
/// Structure of received data
/// </summary>
public class StateObject
{
public byte[] DataBuffer { get; set; }
public Socket Socket { get; set; }
public StateObject(Socket socket)
{
this.DataBuffer = new byte[128];
this.Socket = socket;
}
}
/// <summary>
/// TCP client with asynchronous connecting and data receiving
/// </summary>
public class TcpAsyncClient
{
protected string address;
protected int port;
private Socket socket;
private int keepAliveTime;
private int keepAliveInterval;
private int connectTimeout;
private bool autoReconnect;
private AsyncCallback callback;
private static ManualResetEvent connectDone = new ManualResetEvent(false);
public event MessageEventHandler DataReceived = delegate { };
public event ExceptionEventHandler ExceptionCaught = delegate { };
public event ConnectionStateChangedEventHandler ConnectionStateChanged = delegate { };
public bool Connected
{
get
{
if (socket == null)
return false;
return socket.Connected;
}
}
public TcpAsyncClient(string address, int port, int keepAliveTime = 1000, int keepAliveInterval = 1000, int connectTimeout = 1000, bool autoReconnect = false)
{
this.address = address;
this.port = port;
this.keepAliveInterval = keepAliveInterval;
this.keepAliveTime = keepAliveTime;
this.connectTimeout = connectTimeout;
this.autoReconnect = autoReconnect;
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
}
/// <summary>
/// Connect to tcp server - async
/// </summary>
public void Connect()
{
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEnd = FindIpEndPoint(address, port);
socket.BeginConnect(ipEnd, new AsyncCallback(ConnectCallback), new StateObject(socket));
connectDone.WaitOne(500);
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Connect done callback
/// </summary>
/// <param name="ar"></param>
private void ConnectCallback(IAsyncResult ar)
{
try
{
// Complete the connection.
((StateObject)ar.AsyncState).Socket.EndConnect(ar);
// Signal that the connection has been made.
connectDone.Set();
WaitForData();
SetKeepAlive(true, Convert.ToUInt32(keepAliveTime), Convert.ToUInt32(keepAliveInterval));
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Connected));
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Disconnect from tcp server
/// </summary>
public void Disconnect()
{
try
{
// MSDN recommends to Shutdown() before Disconnect()
socket.Shutdown(SocketShutdown.Both);
socket.Disconnect(true);
}
catch { }
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
if (autoReconnect)
{
Connect();
}
}
/// <summary>
/// Send string message to tcp server
/// </summary>
/// <param name="message"></param>
public void Send(string message)
{
// because of this, we can Send from client imidiately after Connect() call
DateTime start = DateTime.Now;
if (!Connected)
{
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
return;
}
// make return on the end of line
message += "\r";
int sent = 0; // how many bytes is already sent
do
{
try
{
sent += socket.Send(System.Text.Encoding.UTF8.GetBytes(message), sent, message.Length - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
{
OnError(ex);
break;
}
}
}
while (sent < message.Length);
}
/// <summary>
/// Start receiving data from tcp server
/// </summary>
public void WaitForData()
{
try
{
StateObject stateObject = new StateObject(socket);
IAsyncResult result = socket.BeginReceive(stateObject.DataBuffer, 0, 128, SocketFlags.None, new AsyncCallback(OnDataReceived), stateObject);
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Data received callback
/// </summary>
/// <param name="asyn"></param>
public void OnDataReceived(IAsyncResult asyn)
{
try
{
StateObject stateObject = (StateObject)asyn.AsyncState;
if (!stateObject.Socket.Connected)
return;
int iRx = stateObject.Socket.EndReceive(asyn);
// Server probably stopped listening
if (iRx == 0)
{
Disconnect();
ConnectionStateChanged(this, new ConnectionStateEventArgs(ConnectionState.Disconnected));
return;
}
char[] chars = new char[iRx];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(stateObject.DataBuffer, 0, iRx, chars, 0);
string szData = new string(chars);
DataReceived(this, new MessageEventArgs(szData));
WaitForData();
}
catch (SocketException ex)
{
OnError(ex);
}
}
/// <summary>
/// Socket exception during connecting or communication with server
/// </summary>
/// <param name="ex"></param>
private void OnError(Exception ex)
{
ExceptionCaught(this, new ExceptionEventArgs(ex));
Disconnect();
}
/// <summary>
/// Set KeepAlive timer for socket
/// </summary>
/// <param name="on"></param>
/// <param name="time"></param>
/// <param name="interval"></param>
private void SetKeepAlive(bool on, uint time, uint interval)
{
int size = Marshal.SizeOf(new uint());
var inOptionValues = new byte[size * 3];
BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size);
BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2);
socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}
/// <summary>
/// Create ip address from known host name
/// </summary>
/// <param name="hostName"></param>
/// <param name="port"></param>
/// <returns></returns>
private IPEndPoint FindIpEndPoint(string hostName, int port)
{
var addresses = System.Net.Dns.GetHostAddresses(hostName);
if (addresses.Length == 0)
{
throw new ArgumentException(
"Unable to retrieve address from specified host name.",
"hostName"
);
}
return new IPEndPoint(addresses[0], port);
}
}
}
I'm writing a chat program using sockets in C# Winform.
It works well with single client, but when I turn on another client, previous client's (worked well) Send function never works, but it keeps receiving data.
Here's my whole source code (because idk what's wrong with it, and I got some advices that I gave them poor details)
Server:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Player__Server_
{
public partial class Form1 : Form
{
Socket Serv;
List<Socket> ClnSocket = new List<Socket>();
List<string> MusList = new List<string>();
List<string> Nickname = new List<string>();
int ClnCounter = 0;
int MusCounter = 0;
int BufferingCount = 0;
Socket socket;
Thread run;
private delegate void sDelegate(string sData, int socketIndex);
public Form1()
{
InitializeComponent();
}
new public string Left(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(0, TextLength);
}
new public string Right(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(Text.Length - TextLength, TextLength);
}
new public string Mid(string sString, int nStart, int nLength)
{
string sReturn;
--nStart;
if (nStart <= sString.Length)
{
if ((nStart + nLength) <= sString.Length)
{
sReturn = sString.Substring(nStart, nLength);
}
else
{
sReturn = sString.Substring(nStart);
}
}
else
{
sReturn = string.Empty;
}
return sReturn;
}
private void Form1_Load(object sender, EventArgs e)
{
// prevent exception
Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.OpenOrCreate);
fe.Close();
// Read "Music.server" file and add to MusList (List)
string line;
StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.server");
while ((line = fr.ReadLine()) != null)
{
MusList.Add(line);
MusCounter++;
}
fr.Close();
// prevent exception
Stream fa = new FileStream(Environment.CurrentDirectory + "/Account.server", FileMode.OpenOrCreate);
fa.Close();
Serv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Serv.Bind(new IPEndPoint(IPAddress.Any, 9180));
Serv.Listen(100);
Thread accept = new Thread(this.Accept);
accept.Start();
}
private void runChatting(object s)
{
byte[] str = new byte[2048];
socket = s as Socket;
while (true)
{
try
{
str = new byte[2048];
socket.Receive(str);
sDelegate sdelegate = new sDelegate(this.Receive);
this.Invoke(sdelegate, Encoding.Default.GetString(str), ClnSocket.IndexOf(socket));
}
catch
{
try
{
ClnSocket.Remove(socket);
ClnCounter--;
}
catch { }
return;
}
}
}
private string GetMusic(string MusName)
{
// Function :: return original information of music- search by name
int i;
for (i = 0; i < MusCounter; i++)
{
try
{
if (MusList[i].IndexOf(MusName) > 0)
{
return MusList[i];
}
}
catch { }
}
return null;
}
private void Receive(string sData, int socketIndex)
{
TextBox.AppendText("GET : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
sData = sData.Replace("\0", "");
if (Left(sData,10) == "#musicadd#")
{
string TempData = Mid(sData, 11, sData.Length);
string[] SpliteData = TempData.Split('#');
if (GetMusic(SpliteData[1]) == null)
{
Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.Append);
StreamWriter ws = new StreamWriter(fs);
ws.WriteLine(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
ws.Close();
fs.Close();
MusList.Add(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
MusCounter++;
}
SendTo("#musicadd#" + SpliteData[1], socketIndex);
}
else if (Left(sData, 7) == "#login#")
{
SendAll(Mid(sData, 8, sData.Length) + " Connected." + Environment.NewLine);
}
else if (Left(sData, 14) == "#requestmusic#")
{
string requestValue = GetMusic(Mid(sData, 15, sData.Length));
SendAll("#buffermusic#" + requestValue);
BufferingCount = 0;
}
else if (Left(sData, 12) == "#bufferdone#")
{
BufferingCount++;
if (BufferingCount == ClnCounter)
{
SendAll("#musicplay#");
}
}
else
{
SendAll(sData);
}
}
private void SendAll(string sData)
{
int i;
for (i = 0; i < ClnSocket.Count; i++)
{
try
{
ClnSocket[i].Send(Encoding.Default.GetBytes(sData));
}
catch { }
}
TextBox.AppendText("POST : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
}
private void SendTo(string sData, int socketIndex)
{
try
{
ClnSocket[socketIndex].Send(Encoding.Default.GetBytes(sData));
TextBox.AppendText("POST TO (" + socketIndex.ToString() + ") : " + sData);
if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
}
catch { }
}
private void Accept()
{
while (true)
{
ClnSocket.Add(Serv.Accept());
ClnCounter++;
run = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(this.runChatting));
run.Start(ClnSocket[ClnCounter - 1]);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.Serv.Close();
this.Serv = null;
for (int i = 0; i < this.ClnSocket.Count; i++)
{
this.ClnSocket[i].Close();
}
this.ClnSocket.Clear();
System.Environment.Exit(0);
}
}
}
Client:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.IO;
namespace MineSky_Player
{
public partial class Form1 : Form
{
WMPLib.WindowsMediaPlayer Player = new WMPLib.WindowsMediaPlayer();
public Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Thread run;
string BufferingInformation;
int current_min = 0, current_sec = 0;
int duration_min = 0, duration_sec = 0;
int MusCounter = 0;
public string mynick { get; set; }
string MyNick;
List<string> MusList = new List<string>();
public delegate void sDelegate(string sData);
public Form1()
{
try
{
InitializeComponent();
socket.Connect("localhost", 9180);
run = new Thread(new ParameterizedThreadStart(Run));
run.Start();
}
catch (Exception ex){
MessageBox.Show(ex.ToString());
System.Environment.Exit(0);
}
}
private void Form1_Load(object sender, EventArgs e)
{
// prevent exception
Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.OpenOrCreate);
fe.Close();
// Read "Music.client" file and add to MusList (List)
string line;
StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.client");
while ((line = fr.ReadLine()) != null)
{
MusList.Add(line);
MusCounter++;
MusicList.Items.Add(line);
}
fr.Close();
MyNick = mynick;
}
new public string Left(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(0, TextLength);
}
new public string Right(string Text, int TextLength)
{
if (Text.Length < TextLength)
{
TextLength = Text.Length;
}
return Text.Substring(Text.Length - TextLength, TextLength);
}
new public string Mid(string sString, int nStart, int nLength)
{
string sReturn;
--nStart;
if (nStart <= sString.Length)
{
if ((nStart + nLength) <= sString.Length)
{
sReturn = sString.Substring(nStart, nLength);
}
else
{
sReturn = sString.Substring(nStart);
}
}
else
{
sReturn = string.Empty;
}
return sReturn;
}
private void BufferTick_Tick(object sender, EventArgs e)
{
if (Player.playState.ToString() == "wmppsPlaying")
{
Player.controls.stop();
ToSocket("#bufferdone#");
BufferTick.Enabled = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Player.controls.play();
}
private void Run(object s)
{
byte[] str = new byte[2048];
try
{
while (true)
{
str = new byte[2048];
socket.Receive(str);
sDelegate sdelegate = new sDelegate(this.Receive);
IntPtr x;
if (!this.IsHandleCreated) x = this.Handle;
this.Invoke(sdelegate, Encoding.Default.GetString(str));
}
}
catch (Exception e)
{
MessageBox.Show("Connection Lost." + Environment.NewLine + e.ToString());
Application.Exit();
}
}
public void Receive(string sData)
{
//MessageBox.Show("GET : " + sData);
sData = sData.Replace("\0", "");
if (Left(sData, 10) == "#musicadd#")
{
if (MusicList.Items.Contains(Mid(sData, 11, sData.Length)))
{
MessageBox.Show("Already in the list!", "MSPlayer", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.Append);
StreamWriter ws = new StreamWriter(fs);
ws.WriteLine(Mid(sData, 11, sData.Length));
ws.Close();
fs.Close();
MusList.Add(Mid(sData, 11, sData.Length));
MusicList.Items.Add(Mid(sData, 11, sData.Length));
MusCounter++;
}
}
else if (Left(sData, 13) == "#buffermusic#")
{
PlayProgressBar.Value = 0;
current_min = 0;
current_sec = 0;
Player.URL = "";
BufferingInformation = Mid(sData, 14, sData.Length);
playingLabel.Text = BufferingInformation.Split('#')[1];
playLabel.Text = "Buffering...";
Player.URL = "https://player.soundcloud.com/player.swf?url=https%3A//api.soundcloud.com/tracks/" + BufferingInformation.Split('#')[0] + ";color=ff5500&show_comments=false&auto_play=true& color=a2eeff";
BufferTick.Enabled = true;
}
else if (Left(sData, 11) == "#musicplay#")
{
duration_min = Int32.Parse(BufferingInformation.Split('#')[2]) / 60;
duration_sec = Int32.Parse(BufferingInformation.Split('#')[2]) % 60;
playLabel.Text = "0:00 / " + duration_min.ToString() + ":" + duration_sec.ToString();
PlayProgressBar.Maximum = Int32.Parse(BufferingInformation.Split('#')[2]);
Player.controls.play();
PlayTick.Enabled = true;
}
else
{
Text_Board.AppendText(sData.Replace("\#\", "#"));
}
}
public void Send(string sData)
{
sData = sData.Replace("#", "\#\");
Send_Supplied(sData);
}
public void Send_Supplied(string sData)
{
if (Left(sData, 2) == "//")
{
sData = sData.ToLower();
if (Left(sData, 6) == "//exit") Application.Exit();
}
else
{
ToSocket(sData);
}
}
private void ToSocket(string sData)
{
socket.Send(Encoding.Default.GetBytes(sData));
}
private void Text_Chat_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
string str = this.Text_Chat.Text.Replace(Environment.NewLine, "");
this.Text_Chat.Text = "";
Send(MyNick + " : " + str + Environment.NewLine);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
socket.Close();
System.Environment.Exit(0);
}
private void Button_Exit_Click(object sender, EventArgs e)
{
socket.Close();
System.Environment.Exit(0);
}
private void MusicList_DoubleClick(object sender, EventArgs e)
{
ToSocket("#requestmusic#" + MusicList.GetItemText(MusicList.SelectedItem));
}
private void Button_MusicAdd_Click(object sender, EventArgs e)
{
AddForm addform = new AddForm();
addform.socket = socket;
addform.ShowDialog();
}
private void MusicList_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void PlayTick_Tick(object sender, EventArgs e)
{
if (Player.playState.ToString() == "wmppsPlaying")
{
current_sec++;
PlayProgressBar.Value++;
if (current_sec == 60)
{
current_min++;
current_sec = 0;
}
playLabel.Text = current_min.ToString() + ":" + current_sec.ToString() + " / " + duration_min.ToString() + ":" + duration_sec.ToString();
if (PlayProgressBar.Value == PlayProgressBar.Maximum)
{
Player.controls.stop();
playingLabel.Text = "Not Playing";
playLabel.Text = "0:00 / 0:00";
PlayProgressBar.Value = 0;
PlayTick.Enabled = false;
}
}
}
}
}
Addform and Enterform are not the problem, I think. I've tested.
I wasn't able to get your code to compile when I copied it in to a new project, so I'm not sure. One thing is for sure though, you have to many while loops taking place here. I would recommend replacing those with the async pattern that the Socket class has available. It would remove all of the while loops. A little bit of extra abstraction would help as well.
I have broken the server up in to its own class, then provided each Socket connection a wrapper class that the server will hold a reference to.
Note that I am using Visual Studio 2015, so my string formatting is using the new C# 6.0 style. If you can not use C# 6, it is easy enough to convert back to string.Format("{0}", foo);
Server Status
We need to have a way of checking what the current server status is. This can be as simple as a small enum.
/// <summary>
/// Provides status values for the server to use, indicating its current state.
/// </summary>
public enum ServerStatus
{
/// <summary>
/// The server has stopped.
/// </summary>
Stopped,
/// <summary>
/// Server is in the process of starting.
/// </summary>
Starting,
/// <summary>
/// Server is up and running.
/// </summary>
Running
}
ConnectedArgs
Next, the Server class needs to raise some events so that the Form code-behind can be told when a client connects, and when the client has sent the server a message. We will provide a class called ConnectedArgs as our event handler argument. This class will hold a reference to our actual client, using the wrapper class we will create next.
public class ConnectedArgs : EventArgs
{
/// <summary>
/// Instances a new ConnectedArgs class.
/// </summary>
/// <param name="state">The state of a client connection.</param>
public ConnectedArgs(ConnectionState state)
{
this.ConnectedClient = state;
}
/// <summary>
/// Gets the client currently connected.
/// </summary>
public ConnectionState ConnectedClient { get; private set; }
}
ConnectionState
This class is responsible for holding the Socket associated with the connected client, and handle receiving the clients message data asynchronously. This uses the BeginInvoke and EndInvoke async pattern included with the Socket.
This class will have an event that will be used to notify the Form that a new message was received. Note that this was pulled from one of my existing projects, so the data parsing basically checks the buffer and if the buffer does not include a \r\n, it is considered incomplete. It caches it and waits for the next chunk of data from the client to process and try and complete. You will want to replace the ProcessReceivedData method with your custom method that handles processing the received data. When you are done, just push the results in to the OnDataReceived method so your Form can be given it.
public sealed class ConnectionState
{
/// <summary>
/// The size of the buffer that will hold data sent from the client
/// </summary>
private readonly int bufferSize;
/// <summary>
/// A temporary collection of incomplete messages sent from the client. These must be put together and processed.
/// </summary>
private readonly List<string> currentData = new List<string>();
/// <summary>
/// What the last chunk of data sent from the client contained.
/// </summary>
private string lastChunk = string.Empty;
/// <summary>
/// Instances a new PlayerConnectionState.
/// </summary>
/// <param name="player">An instance of a Player type that will be performing network communication</param>
/// <param name="currentSocket">The Socket used to communicate with the client.</param>
/// <param name="bufferSize">The storage size of the data buffer</param>
public ConnectionState(Socket currentSocket, int bufferSize)
{
this.CurrentSocket = currentSocket;
this.bufferSize = bufferSize;
this.Buffer = new byte[bufferSize];
}
/// <summary>
/// This event is raised when the server has received new, valid, data from the client.
/// </summary>
public event EventHandler<string> DataReceived;
/// <summary>
/// Gets the Socket for the player associated with this state.
/// </summary>
public Socket CurrentSocket { get; private set; }
/// <summary>
/// Gets the data currently in the network buffer
/// </summary>
public byte[] Buffer { get; private set; }
/// <summary>
/// Gets if the current network connection is in a valid state.
/// </summary>
public bool IsConnectionValid
{
get
{
return this.CurrentSocket != null && this.CurrentSocket.Connected;
}
}
/// <summary>
/// Starts listening for network communication sent from the client to the server
/// </summary>
public void StartListeningForData()
{
this.Buffer = new byte[bufferSize];
this.CurrentSocket.BeginReceive(this.Buffer, 0, bufferSize, 0, new AsyncCallback(this.ReceiveData), null);
}
/// <summary>
/// Receives the input data from the user.
/// </summary>
/// <param name="result">The result.</param>
private void ReceiveData(IAsyncResult result)
{
// If we are no longer in a valid state, dispose of the connection.
if (!this.IsConnectionValid)
{
this.CurrentSocket?.Dispose();
return;
}
int bytesRead = this.CurrentSocket.EndReceive(result);
if (bytesRead == 0 || !this.Buffer.Any())
{
this.StartListeningForData();
return;
}
ProcessReceivedData(bytesRead);
this.StartListeningForData();
}
/// <summary>
/// Process the data we received from the client.
/// </summary>
/// <param name="bytesRead"></param>
private void ProcessReceivedData(int bytesRead)
{
// Encode our input string sent from the client
this.lastChunk = Encoding.ASCII.GetString(this.Buffer, 0, bytesRead);
// If the previous chunk did not have a new line feed, then we add this message to the collection of currentData.
// This lets us build a full message before processing it.
if (!lastChunk.Contains("\r\n"))
{
// Add this to our incomplete data stash and read again.
this.currentData.Add(lastChunk);
return;
}
// This message contained at least 1 new line, so we split it and process per line.
List<string> messages = lastChunk.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
foreach (string line in this.PruneReceivedMessages(messages))
{
this.OnDataReceived(line);
}
}
/// <summary>
/// Runs through the messages collection and prepends data from a previous, incomplete, message
/// and updates the internal message tracking state.
/// </summary>
/// <param name="messages"></param>
private List<string> PruneReceivedMessages(List<string> messages)
{
// Append the first line to the incomplete line given to us during the last pass if one exists.
if (this.currentData.Any() && messages.Any())
{
messages[0] = string.Format("{0} {1}", string.Join(" ", this.currentData), messages[0]);
this.currentData.Clear();
}
// If we have more than 1 line and the last line in the collection does not end with a line feed
// then we add it to our current data so it may be completed during the next pass.
// We then remove it from the lines collection because it can be infered that the remainder will have
// a new line due to being split on \n.
if (messages.Count > 1 && !messages.Last().EndsWith("\r\n"))
{
this.currentData.Add(messages.Last());
messages.Remove(messages.Last());
}
return messages;
}
private void OnDataReceived(string data)
{
var handler = this.DataReceived;
if (handler == null)
{
return;
}
handler(this, data);
}
}
Server
Now that we have the client async receiving and processing of data, we need to write the server component to actually accept incoming Socket connections asynchronously.
This class will have two events that the Form will subscribe to. One for when a client connects, and another for when the client disconnects. Each client that is connected, is assigned a ConnectionState and cached in a collection of List<ConnectionState>. This removes the need for you to keep a fragile counter of the number of clients connected.
You could optionally wire up a timer that periodically prunes the List<ConnectionState> collection. You can check if each instance in the collection has its IsConnectionValid set to true. If it returns false, remove it from the collection.
/// <summary>
/// The Default Desktop game Server
/// </summary>
public sealed class Server
{
/// <summary>
/// The user connection buffer size
/// </summary>
private const int UserConnectionBufferSize = 1024;
/// <summary>
/// The server socket
/// </summary>
private Socket serverSocket;
/// <summary>
/// The player connections
/// </summary>
private List<ConnectionState> connectedClients;
/// <summary>
/// Used for making access to the connectedClients collection thread-safe
/// </summary>
private object lockObject = new object();
/// <summary>
/// Initializes a new instance of the <see cref="Server"/> class.
/// </summary>
public Server()
{
this.Status = ServerStatus.Stopped;
this.connectedClients = new List<ConnectionState>();
}
/// <summary>
/// Occurs when a client connects to the server.
/// </summary>
public event EventHandler<ConnectedArgs> ClientConnected;
/// <summary>
/// Occurs when a client is disconnected from the server.
/// </summary>
public event EventHandler<ConnectedArgs> ClientDisconnected;
/// <summary>
/// Gets or sets the port that the server is running on.
/// </summary>
public int Port { get; set; }
/// <summary>
/// Gets or sets the maximum queued connections.
/// </summary>
public int MaxQueuedConnections { get; set; }
/// <summary>
/// Gets the current server status.
/// </summary>
public ServerStatus Status { get; private set; }
public void Start()
{
if (this.Status != ServerStatus.Stopped)
{
throw new InvalidOperationException("The server is either starting or already running. You must stop the server before starting it again.");
}
else if (this.Port == 0)
{
throw new InvalidOperationException("You can not start the server on Port 0.");
}
this.Status = ServerStatus.Starting;
// Get our server address information
IPHostEntry serverHost = Dns.GetHostEntry(Dns.GetHostName());
var serverEndPoint = new IPEndPoint(IPAddress.Any, this.Port);
// Instance the server socket, bind it to a port.
this.serverSocket = new Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
this.serverSocket.Bind(serverEndPoint);
this.serverSocket.Listen(this.MaxQueuedConnections);
// Begin listening for connections.
this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);
this.Status = ServerStatus.Running;
}
/// <summary>
/// Stops the server.
/// </summary>
public void Stop()
{
this.DisconnectAll();
// We test to ensure the server socket is still connected and active.
this.serverSocket.Blocking = false;
try
{
this.serverSocket.Send(new byte[1], 0, 0);
// Message was received meaning it's still receiving, so we can safely shut it down.
this.serverSocket.Shutdown(SocketShutdown.Both);
}
catch (SocketException e)
{
// Error code 10035 indicates it works, but will block the socket.
// This means it is still receiving and we can safely shut it down.
// Otherwise, it's not receiving anything and we don't need to shut down.
if (e.NativeErrorCode.Equals(10035))
{
this.serverSocket.Shutdown(SocketShutdown.Both);
}
}
finally
{
this.Status = ServerStatus.Stopped;
}
}
/// <summary>
/// Disconnects the specified IServerPlayer object.
/// </summary>
/// <param name="connection">The client to disconnect.</param>
public void Disconnect(ConnectionState connection)
{
if (connection != null && connection.IsConnectionValid)
{
connection.CurrentSocket.Shutdown(SocketShutdown.Both);
this.connectedClients.Remove(connection);
this.OnClientDisconnected(connection);
}
}
/// <summary>
/// Disconnects everyone from the server.
/// </summary>
public void DisconnectAll()
{
// Loop through each connection and disconnect them.
foreach (ConnectionState state in this.connectedClients)
{
Socket connection = state.CurrentSocket;
if (connection != null && connection.Connected)
{
connection.Shutdown(SocketShutdown.Both);
this.OnClientDisconnected(state);
}
}
this.connectedClients.Clear();
}
/// <summary>
/// Called when a client connects.
/// </summary>
private void OnClientConnected(ConnectionState connection)
{
EventHandler<ConnectedArgs> handler = this.ClientConnected;
if (handler == null)
{
return;
}
handler(this, new ConnectedArgs(connection));
}
/// <summary>
/// Called when a client disconnects.
/// </summary>
private void OnClientDisconnected(ConnectionState connection)
{
EventHandler<ConnectedArgs> handler = this.ClientDisconnected;
if (handler == null)
{
return;
}
handler(this, new ConnectedArgs(connection));
}
/// <summary>
/// Connects the client to the server and then passes the connection responsibilities to the client object.
/// </summary>
/// <param name="result">The async result.</param>
private void ConnectClient(IAsyncResult result)
{
// Connect and register for network related events.
Socket connection = this.serverSocket.EndAccept(result);
// Send our greeting
byte[] buffer = Encoding.ASCII.GetBytes("Welcome to the Music App Server!");
connection.BeginSend(buffer, 0, buffer.Length, 0, new AsyncCallback(asyncResult => connection.EndReceive(asyncResult)), null);
// Fetch the next incoming connection.
this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);
this.CompleteClientSetup(new ConnectionState(connection, UserConnectionBufferSize));
}
/// <summary>
/// Caches the ConnectionState and has the state begin listening to client data.
/// </summary>
/// <param name="connectionState"></param>
private void CompleteClientSetup(ConnectionState connectionState)
{
lock (this.lockObject)
{
this.connectedClients.Add(connectionState);
}
// Start receiving data from the client.
connectionState.StartListeningForData();
this.OnClientConnected(connectionState);
}
}
Form
Now that we have our server code put together, the Form can be constructed. I just did a simple form, with a single button for connecting and a single multi-line textbox for viewing the data.
I added an event handler for the Form's Loading event and for the Button's Clicked event.
public partial class Form1 : Form
{
private Server server;
public Form1()
{
InitializeComponent();
server = new Server { Port = 9180, MaxQueuedConnections = 100 };
}
private void Form1_Load(object sender, EventArgs e)
{
server.ClientConnected += OnClientConnected;
server.ClientDisconnected += OnClientDisconnected;
}
private void OnClientDisconnected(object sender, ConnectedArgs e)
{
this.Invoke(new Action(() => this.textBox1.AppendText("A Client disconnected.\n")));
}
private void OnClientConnected(object sender, ConnectedArgs e)
{
this.Invoke(new Action(() =>
{
this.textBox1.AppendText("New Client Connected.\n");
e.ConnectedClient.DataReceived += OnClientSentDataToServer;
}));
}
private void OnClientSentDataToServer(object sender, string e)
{
this.Invoke(new Action(() => this.textBox1.AppendText($"{e}\n")));
}
private void button1_Click(object sender, EventArgs e)
{
this.textBox1.AppendText("Server starting.\n");
server.Start();
this.textBox1.AppendText("Server running.\n");
}
}
Due to the server running as a background process, you have to marshall the event handlers back to the UI thread when accessing the TextBox. You could abstract that piece out a bit as well, so that the Server and ConnectionState objects always push to the dispatcher you provide them. This reduces the number of Invoke(Action) invocations you have to do.
This works well with multiple connections. I started the server up, and then spun up two different connections across a couple different computers without any issues. You will need to customize the data processing and pushing messages to the client. This should at least solve your multiple connection issue, and be less stress on the CPU (no more while loops!).
Hope this helps.
I have an application which simply pulls items from a queue and then attempts to send them asynchronously via a network socket.
I am experiencing some issues when things go wrong or the client aborts the host socket.
Here is some of my code: I think it may explain better than my words:
Here is my SocketState.cs class which holds the socket and related info:
public class SocketState
{
public const int BufferSize = 256;
public Socket WorkSocket { get; set; }
public byte[] Buffer { get; set; }
/// <summary>
/// Constructor receiving a socket
/// </summary>
/// <param name="socket"></param>
public SocketState(Socket socket)
{
WorkSocket = socket;
Buffer = new byte[BufferSize];
}
}
Here is my SocketHandler.cs class which controls most of the socket operations:
public class SocketHandler : IObserver
{
# region Class Variables
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private SocketState _state;
private OutgoingConnectionManager _parentConnectionManager;
private int _recieverId;
private readonly ManualResetEvent _sendDone = new ManualResetEvent(false);
public volatile bool NameIsSet = false;
private ManualResetEvent _receiveDone = new ManualResetEvent(false);
public String Name;
public readonly Guid HandlerId;
# endregion Class Variables
/// <summary>
/// Constructor
/// </summary>
public SocketHandler(SocketState state)
{
HandlerId = Guid.NewGuid();
_state = state;
_state.WorkSocket.BeginReceive(_state.Buffer, 0, SocketState.BufferSize, 0, new AsyncCallback(ReceiveCallback), this._state);
}
/// <summary>
/// Set the receiver id for this socket.
/// </summary>
public void SetReceiverId(int receiverId)
{
_recieverId = receiverId;
}
/// <summary>
/// Stops / closes a connection
/// </summary>
public void Stop()
{
CloseConnection();
}
/// <summary>
/// Used to set this connections parent connection handler
/// </summary>
/// <param name="conMan"></param>
public void SetConnectionManager(OutgoingConnectionManager conMan)
{
_parentConnectionManager = conMan;
}
/// <summary>
/// Returns if the socket is connected or not
/// </summary>
/// <returns></returns>
public bool IsConnected()
{
return _state.WorkSocket.Connected;
}
/// <summary>
/// Public Class that implements the observer interface , this function provides a portal to receive new messages which it must send
/// </summary>
/// <param name="e"> Event to execute</param>
public void OnMessageRecieveEvent(ObserverEvent e)
{
SendSignalAsync(e.Message.payload);
}
# region main working space
# region CallBack Functions
/// <summary>
/// CallBack Function that is called when a connection Receives Some Data
/// </summary>
/// <param name="ar"></param>
private void ReceiveCallback(IAsyncResult ar)
{
try
{
String content = String.Empty;
if (ar != null)
{
SocketState state = (SocketState)ar.AsyncState;
if (state != null)
{
Socket handler = state.WorkSocket;
if (handler != null)
{
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
StringBuilder Sb = new StringBuilder();
Sb.Append(Encoding.Default.GetString(state.Buffer, 0, bytesRead));
if (Sb.Length > 1)
{
content = Sb.ToString();
foreach (var s in content.Split('Ÿ'))
{
if (string.Compare(s, 0, "ID", 0, 2) == 0)
{
Name = s.Substring(2);
NameIsSet = true;
}
if (string.Compare(s, 0, "TG", 0, 2) == 0)
{
LinkReplyToTag(s.Substring(2), this.Name);
}
}
_state.WorkSocket.BeginReceive(_state.Buffer, 0, SocketState.BufferSize, 0,
new AsyncCallback(ReceiveCallback), _state);
}
}
}
}
}
}
catch
{
CloseConnection();
}
}
/// <summary>
/// Call Back Function called when data is send though this connection
/// </summary>
/// <param name="asyncResult"></param>
private void SendCallback(IAsyncResult asyncResult)
{
try
{
if (asyncResult != null)
{
Socket handler = (Socket)asyncResult.AsyncState;
if (handler != null)
{
int bytesSent = handler.EndSend(asyncResult);
// Signal that all bytes have been sent.
_sendDone.Set();
if (bytesSent > 0)
{
return;
}
}
}
}
catch (Exception e)
{
Log.Error("Transmit Failed On Send CallBack");
}
//Close socket as something went wrong
CloseConnection();
}
# endregion CallBack Functions
/// <summary>
/// Sends a signal out of the current connection
/// </summary>
/// <param name="signal"></param>
private void SendSignalAsync(Byte[] signal)
{
try
{
if (_state != null)
{
if (_state.WorkSocket != null)
{
if (_state.WorkSocket.Connected)
{
try
{
_sendDone.Reset();
_state.WorkSocket.BeginSend(signal, 0, signal.Length, 0, new AsyncCallback(SendCallback),
_state.WorkSocket);
_sendDone.WaitOne(200);
return;
}
catch (Exception e)
{
Log.Error("Transmission Failier for IP: " + ((IPEndPoint)_state.WorkSocket.RemoteEndPoint).Address, e);
}
}
}
}
//Close Connection as something went wrong
CloseConnection();
}
catch (Exception e)
{
Log.Error("An Exception has occurred in the SendSignalAsync function", e);
}
}
/// <summary>
/// Call this to Close the connection
/// </summary>
private void CloseConnection()
{
try
{
var ip = "NA";
try
{
if (_state != null)
{
ip = ((IPEndPoint)_state.WorkSocket.RemoteEndPoint).Address.ToString();
}
}
catch
{
//Cannot get the ip address
}
OutgoingListeningServer.UpdateRecieversHistory(_recieverId, ip, "Disconnected");
try
{
if (_state != null)
{
if (_state.WorkSocket != null)
{
_state.WorkSocket.Shutdown(SocketShutdown.Both);
_state.WorkSocket.Close();
//_state.WorkSocket.Dispose();
_state.WorkSocket = null;
_state = null;
}
}
}
catch (Exception e)
{
_state = null;
Log.Error("Error while trying to close socket for IP: " + ip, e);
}
if (_parentConnectionManager != null)
{
// Remove this connection from the connection list
_parentConnectionManager.ConnectionRemove(this);
}
}
catch (Exception e)
{
Log.Error("A major error occurred in the close connection function, outer try catch was hit", e);
}
}
# endregion main working space
}
And here is my thread which will then call the OnMessageRecieveEvent() function in the SocketHandler.cs class.
private void Main()
{
Log.Info("Receiver" + ReceiverDb.Id + " Thread Starting");
// Exponential back off
var eb = new ExponentialBackoff();
try
{
while (_run)
{
try
{
if (ReceiverOutgoingConnectionManager.HasConnectedClient())
{
//Fetch Latest Item
ILogItem logItem;
if (_receiverQueue.TryDequeue(out logItem))
{
//Handle the logItem
**calls the OnMessageRecieveEvent() for all my connections.
ReceiverOutgoingConnectionManager.SendItemToAllConnections(logItem);
//Reset the exponential back off counter
eb.reset();
}
else
{
//Exponential back off thread sleep
eb.sleep();
}
}
else
{
//Exponential back off thread sleep
eb.sleep();
}
}
catch (Exception e)
{
Log.Error("Error occurred in " + ReceiverDb.Id + "receiver mains thread ", e);
}
}
}
catch (Exception e)
{
Log.Error(" ** An error has occurred in a receiver holder main thread ** =====================================================================>", e);
}
Log.Info("Receiver" + ReceiverDb.Id + " Thread Exiting ** ===============================================================================>");
}
I apologize for so much code. But I fear that it might not be something obvious so I posted all related code.
Now to further explain my issue.
If an error happens on the socket. I get allot of Transmit Failed On Send CallBack. which to me means that i'm not closing the socket correctly, and there are still outstanding callback being executed.
Is there no way I can cancel all outstanding callback when i close the socket?
I am also sure there will be a few suggestions / issues with my code I posted. I would more than appreciate the feedback.
I'm going to assume this is for learning purposes, since writing your own networking code for other purposes is somewhat... hard.
Getting an exception in the send callback is fine. That's what exceptions are for. However, you need to identify the exception, rather than just catching the blanket Exception and pretty much ignoring all the information inside.
Handle the exceptions you can handle in the places where it's proper for them to be handled.
I'm not going to be very specific, because there's a lot of issues with your code. But one of the key things is ignoring proper socket handling - when you receive 0 bytes, it means you're supposed to close the socket. That's the signal from the other side saying "we're done". By ignoring this, you're working with a (perhaps partially) closed connection.
Please, use some networking library that can give you the guarantees (and simplicity) you need. WCF, Lindgren, whatever. TCP doesn't guarantee you'll get the message in one part, for example, so your message parsing code is unreliable. You need to use some message framing, you need to add proper error handling... Socket isn't a high-level construct, it doesn't work "automagically", you need to implement all this stuff on your own.
Even ignoring the network code itself, it's obvious you're just ignoring most of the complexities of asynchronous code. What's up with the SendDone? Either you want to use asynchronous code, and then get rid of the synchronicity, or you want synchronous code, and then why are you using asynchronous sockets?