I'm trying to make it so that whenever a TCP client receives data, it triggers a function call. The function I'm trying to call just performs a function call on another class. But however I try it, it keeps giving me the same error: Cannot assign "OnDataReceived" because it's a 'method group'
Code from my form1:
namespace Liberly
{
public partial class Form1 : Form
{
TcpClient tcpClient;
public Form1()
{
InitializeComponent();
tcpClient = new TcpClient();
tcpClient.OnDataReceived += data;
}
private void data(string text)
{
}
private void button1_Click(object sender, EventArgs e)
{
tcpClient.Connect("127.0.0.1", 2222);
}
private void button2_Click(object sender, EventArgs e)
{
tcpClient.Disconnect();
}
}}
Code from my TCP client library:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Liberly
{
class TcpClient
{
private Socket sender;
private IPEndPoint remoteEP;
private IPAddress ipAddress;
private IPHostEntry ipHostInfo;
private bool run;
private int bytesRec;
private string data;
private byte[] bytes = new byte[1024];
Thread clientT;
/// <summary>
/// Connect with desired ip adress and port
/// </summary>
/// <param name="ip">Ip address</param>
/// <param name="port">Port</param>
public void Connect(string ip,int port)
{
//Setup ip and port
ipHostInfo = Dns.Resolve(ip);
ipAddress = ipHostInfo.AddressList[0];
remoteEP = new IPEndPoint(ipAddress, port);
//Start client thread
clientT = new Thread(new ThreadStart(client));
run = true;
clientT.Start();
}
/// <summary>
/// Disconnect from a server
/// </summary>
public void Disconnect()
{
if (run)
{
try
{
run = false;
if (sender.Connected)
{
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
clientT.Interrupt();
}
catch { }
}
else
{
Debug.WriteLine("TCP CLIENT/Client is not connected");
}
}
/// <summary>
/// Send data to the server
/// </summary>
/// <param name="text">Text</param>
public void Send(string text)
{
if (sender.Connected)
sender.Send(Encoding.ASCII.GetBytes(text));
else
Debug.WriteLine("TCP CLIENT/Unable to send, not connected");
}
/// <summary>
/// Returns bool if client is connected to the server
/// </summary>
/// <returns></returns>
public bool Connected { get { return sender.Connected; } }
//Function that runs when data is received
public string OnDataReceived()
{
return data;
}
//Client void
private void client()
{
try {
//Create socket and connect
sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sender.Connect(remoteEP);
Debug.WriteLine("TCP CLIENT/Connected");
//Loop for receiving data
while (run)
{
bytesRec = sender.Receive(bytes);
data = Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data != null)
{
Debug.WriteLine("TCP CLIENT/Received data:" + data);
if (data == "")
{
Debug.WriteLine("TCP CLIENT/Disconnected");
break;
}
else
{
//Here is the data that is received//
OnDataReceived();
}
}
data = null;
}
}
catch (ArgumentNullException ane)
{
Console.WriteLine("TCP CLIENT/ArgumentNullException : {0}", ane.ToString());
}
catch (SocketException se)
{
Console.WriteLine("TCP CLIENT/SocketException : {0}", se.ToString());
} catch (Exception e)
{
Console.WriteLine("TCP CLIENT/Unexpected exception : {0}", e.ToString());
}
}
}}
You can use events:
public delegate void onDataReceivedEvent(string message);
public event onDataReceivedEvent onDataReceived;
public void sendNewData(string message){
if(!onDataReceived!=null){
onDataReceived.Invoke(message);
}
}
Then register your event:
onDataReceived+= someMethod;
private void someMethod(string message){
//process message;
}
You have a function
//Function that runs when data is received
public string OnDataReceived()
{
return data;
}
in your TCPClient class. You need to change its name.
Or add a event / delegate combo in this library class.
Related
I´m doing multiplayer and my Unity console writes this error "Cannot access a disposed object.
Object name: 'System.Net.Sockets.UdpClient'." when I close the server. I don't know what to do with it... Can anyone please advise me how to fix this?
This is my Server.cs file:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public class Server
{
public static int MaxPlayers { get; private set; }
public static int Port { get; private set; }
public static Dictionary<int, Client> clients = new Dictionary<int, Client>();
public delegate void PacketHandler(int _fromClient, Packet _packet);
public static Dictionary<int, PacketHandler> packetHandlers;
private static TcpListener tcpListener;
private static UdpClient udpListener;
/// <summary>Starts the server.</summary>
/// <param name="_maxPlayers">The maximum players that can be connected simultaneously.</param>
/// <param name="_port">The port to start the server on.</param>
public static void Start(int _maxPlayers, int _port)
{
MaxPlayers = _maxPlayers;
Port = _port;
Debug.Log("Starting server...");
InitializeServerData();
tcpListener = new TcpListener(IPAddress.Any, Port);
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(TCPConnectCallback, null);
udpListener = new UdpClient(Port);
udpListener.BeginReceive(UDPReceiveCallback, null);
Debug.Log($"Server started on port {Port}.");
}
/// <summary>Handles new TCP connections.</summary>
private static void TCPConnectCallback(IAsyncResult _result)
{
TcpClient _client = tcpListener.EndAcceptTcpClient(_result);
tcpListener.BeginAcceptTcpClient(TCPConnectCallback, null);
Debug.Log($"Incoming connection from {_client.Client.RemoteEndPoint}...");
for (int i = 1; i <= MaxPlayers; i++)
{
if (clients[i].tcp.socket == null)
{
clients[i].tcp.Connect(_client);
return;
}
}
Debug.Log($"{_client.Client.RemoteEndPoint} failed to connect: Server full!");
}
/// <summary>Receives incoming UDP data.</summary>
private static void UDPReceiveCallback(IAsyncResult _result)
{
try
{
IPEndPoint _clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] _data = udpListener.EndReceive(_result, ref _clientEndPoint);
udpListener.BeginReceive(UDPReceiveCallback, null);
if (_data.Length < 4)
{
return;
}
using (Packet _packet = new Packet(_data))
{
int _clientId = _packet.ReadInt();
if (_clientId == 0)
{
return;
}
if (clients[_clientId].udp.endPoint == null)
{
// If this is a new connection
clients[_clientId].udp.Connect(_clientEndPoint);
return;
}
if (clients[_clientId].udp.endPoint.ToString() == _clientEndPoint.ToString())
{
// Ensures that the client is not being impersonated by another by sending a false clientID
clients[_clientId].udp.HandleData(_packet);
}
}
}
catch (Exception _ex)
{
Debug.Log($"Error receiving UDP data: {_ex}");
}
}
/// <summary>Sends a packet to the specified endpoint via UDP.</summary>
/// <param name="_clientEndPoint">The endpoint to send the packet to.</param>
/// <param name="_packet">The packet to send.</param>
public static void SendUDPData(IPEndPoint _clientEndPoint, Packet _packet)
{
try
{
if (_clientEndPoint != null)
{
udpListener.BeginSend(_packet.ToArray(), _packet.Length(), _clientEndPoint, null, null);
}
}
catch (Exception _ex)
{
Debug.Log($"Error sending data to {_clientEndPoint} via UDP: {_ex}");
}
}
/// <summary>Initializes all necessary server data.</summary>
private static void InitializeServerData()
{
for (int i = 1; i <= MaxPlayers; i++)
{
clients.Add(i, new Client(i));
}
packetHandlers = new Dictionary<int, PacketHandler>()
{
{ (int)ClientPackets.welcomeReceived, ServerHandle.WelcomeReceived },
{ (int)ClientPackets.playerMovement, ServerHandle.PlayerMovement },
{ (int)ClientPackets.playerShoot, ServerHandle.PlayerShoot },
{ (int)ClientPackets.playerThrowItem, ServerHandle.PlayerThrowItem }
};
Debug.Log("Initialized packets.");
}
public static void Stop()
{
tcpListener.Stop();
udpListener.Close();
}
}
A tutorial according to which I make a multiplayer game:
https://www.youtube.com/playlist?list=PLXkn83W0QkfnqsK8I0RAz5AbUxfg3bOQ5
It's because the garbage collector has disposed the udpClient and you are trying to call the Stop() method in a disposed object.
So , try replacing this method instead of your Stop method
public static void Stop()
{
try
{
tcpListener.Stop();
udpListener.Close();
}
catch {}
}
I'm trying to create a WPF application that once sent a message via TCP to a third-party application, listen for the answer and continue listening to incoming communication (This third-party application sends feedback to the most recently connected or authenticated controller) until another message has to be sent and so on.
It has to be async because my application must run continuously being able to perform other actions.
Basically I've adapted this (https://learn.microsoft.com/it-it/dotnet/framework/network-programming/asynchronous-client-socket-example) example to WPF using a backgoundWorker and never closing the socket.
The problem seems to be that
client.BeginReceive(state.buffer, 0, state.buffer.Length, 0,
new asyncCallback(ReceiveCallback), state);
Won't loopback if the last bytes read is less than the buffer size.
my code is:
using System;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows;
namespace socket_WPF
{
/// <summary>
/// Logica di interazione per MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly BackgroundWorker BWI;
public AsyncSocketReceiver ASR;
public string risposta = "";
public string rispostaOLD;
public bool newMessage = false;
public string Extmessage;
public int Mcounter = 0;
public MainWindow()
{
InitializeComponent();
BWI = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
BWI.DoWork += new DoWorkEventHandler(BWI_DoWork);
BWI.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BWI_RunWorkerCompleted);
BWI.ProgressChanged += new ProgressChangedEventHandler(BWI_ProgressChangedEventHandler);
}
private void BWI_DoWork(object sender, DoWorkEventArgs e)
{
int i = -1;
BackgroundWorker myBW = sender as BackgroundWorker;
ASR = new AsyncSocketReceiver("192.168.1.106", 3040);
ASR.Connect();
ASR.Send(string.Format("[{0}] ping \r", Mcounter));
ASR.sendDone.WaitOne();
newMessage = false;
while (AsyncSocketReceiver.client.Connected)
{
if (newMessage)
{
var toSend = string.Format("[{0}] " + Extmessage + "\r", Mcounter++);
ASR.Send(toSend);
ASR.sendDone.WaitOne();
newMessage = false;
}
ASR.Receive();
ASR.receiveDone.WaitOne();
risposta = ASR.response;
myBW.ReportProgress(i++);
}
}
private void BWI_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BWI.RunWorkerAsync();
}
private void BWI_ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e)
{
CueAnswer.Document.Blocks.Clear();
CueAnswer.AppendText(risposta);
}
private void AsyncSocket_Click(object sender, RoutedEventArgs e) => BWI.RunWorkerAsync();
private void AskC_Click(object sender, RoutedEventArgs e)
{
ASR.receiveDone.Set();
Extmessage = MessageToSend.Text;
newMessage = true;
}
}
public class AsyncSocketReceiver
{
private static IPEndPoint remoteEP;
public static Socket client;
public ManualResetEvent connectDone = new ManualResetEvent(false);
public ManualResetEvent sendDone = new ManualResetEvent(false);
public ManualResetEvent receiveDone = new ManualResetEvent(false);
public String response;
public String query;
public AsyncSocketReceiver(String myIp, int myPort)
{
remoteEP = new IPEndPoint(IPAddress.Parse(myIp), myPort);
client = new Socket(IPAddress.Parse(myIp).AddressFamily,
SocketType.Stream, ProtocolType.Tcp)
{
Blocking = false
};
}
public void Connect()
{
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
client.EndConnect(ar);
connectDone.Set();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void Send(String data)
{
query = data;
response = "";
byte[] byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
sendDone.Set();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void Receive()
{
try
{
StateObject state = new StateObject
{
workSocket = client
};
receiveDone.Reset();
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
client.BeginReceive(state.buffer, 0, state.buffer.Length, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
response = state.sb.ToString();
receiveDone.Set();
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 256;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
}
I have created a class that acts as a plugin for another application. It should hold functions to use in the main application. It works in general - that means i can handle usual functions like calculations and even reading files. But i have problems implementing a socket class. I know how to work with sockets in general but in this case i have a problem.
As you may see in the code, there is an internal class SockAttrib that should manage the socket creation, the listening and also the messages. Received messages are stored in a dictionary.
public class Net : Module {
private static ReadOnlyCollection<CustomMethod> customMethods;
internal class SockAttrib {
public Socket listener;
public Socket handler;
/* create the listener */
public SockAttrib(int port) {
IPHostEntry host = Dns.GetHostEntry("localhost");
IPAddress ipAddress = host.AddressList[1];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
try {
listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(10);
handler = listener.Accept();
} catch (Exception e) { Console.WriteLine("socket() " + e); }
}
/* listen */
public SockAttrib() {
try {
// Incoming data from the client.
string data = "";
byte[] bytes = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data.IndexOf("<EOF>") > -1)
{
messages[++idm] = data;
//return;
}
}
}
catch (Exception e) { Console.WriteLine("listen() "+e); }
}
public void Close() {
handler.Close();
}
}
/* message index */
private static int idm = 0;
/* list of messages */
private static Dictionary<int, String> messages = new Dictionary<int, String>();
public Net() {
if (customMethods != null) return;
List<CustomMethod> moduleMethods = new List<CustomMethod>();
moduleMethods.Add(new CustomMethod(typeof(int), "socket", typeof(int)));
moduleMethods.Add(new CustomMethod(typeof(int), "listen" ));
moduleMethods.Add(new CustomMethod(typeof(string), "sockread"));
customMethods = moduleMethods.AsReadOnly();
}
public ReadOnlyCollection<CustomMethod> Prototypes {
get { return customMethods; }
}
public object OnMethodInvoke(String functionName, List<object> parameters) {
if( functionName == "socket") {
int port = (int)parameters[0];
SockAttrib sa = new SockAttrib( port );
return 1;
}
if (functionName == "listen") {
SockAttrib sa = new SockAttrib();
return 1;
}
if (functionName == "sockread") {
if (idm > 0) {
String message = messages[--idm];
return message;
} else {
return "nope";
}
}
return false;
}
}
My problem is the handler. The creation of the socket works but as soon as i connect to the socket using netcat the socket stop listening and i dont get any responses. I hope its not too much code and it should also be easy readable.
Finally the module gets exported as a library (dll) so i cant really give a minimal working example without also posting the module handler.
Though your requirements are still a bit fuzzy, I will give it a try.
First I would recommend to create a class containing the core functionality of your TCP server. This makes it easier to unit-test your code and to adapt it to changing requirements.
/// <summary>
/// This class encapsulates the TCP server
/// </summary>
public class Server : IDisposable
{
private static TcpListener _listener;
private static TcpClient _client;
private static NetworkStream _stream;
private static byte[] _buffer;
private static readonly StringBuilder _receivedText = new StringBuilder();
private const string EOF = "<EOF>";
/// <summary>
/// Starts listening on the specified port
/// </summary>
/// <param name="port">The port number</param>
public Server(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
_listener.BeginAcceptTcpClient(Accepted, null);
}
public void Dispose()
{
if (_client != null)
{
_client.Dispose();
}
if (_listener != null)
{
_listener.Stop();
}
}
/// <summary>
/// Returns any text that has been sent via TCP to the port specified in the constructor.
/// </summary>
/// <returns>The received text, or null if no (complete) text has been received yet.</returns>
/// <remarks>
/// The method returns the next text delimited by "<EOF>".
/// </remarks>
public string Read()
{
lock (_receivedText)
{
var receivedText = _receivedText.ToString();
var eofIndex = receivedText.IndexOf(EOF);
if (eofIndex < 0)
return null; // no (complete) text has been received yet
var result = receivedText.Substring(0, eofIndex);
_receivedText.Remove(0, eofIndex + EOF.Length);
return result;
}
}
// called whenever a client has connected to our server.
private static void Accepted(IAsyncResult ar)
{
_client = _listener.EndAcceptTcpClient(ar);
_stream = _client.GetStream();
_buffer = new byte[4096];
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
// called whenever data has arrived or if the client closed the TCP connection
private static void Read(IAsyncResult ar)
{
var bytesReceived = _stream.EndRead(ar);
if (bytesReceived == 0)
{
// TCP connection closed
_client.Close();
_client = null;
_stream.Dispose();
_stream = null;
// prepare for accepting the next TCP connection
_listener.BeginAcceptTcpClient(Accepted, null);
return;
}
lock (_receivedText)
{
_receivedText.Append(Encoding.ASCII.GetString(_buffer, 0, bytesReceived));
}
// prepare for reading more
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
}
Integrating this into your Net class should then be fairly simple:
// static or not? Depends on your "Module plugin" architecture
private static Server _server;
public object OnMethodInvoke(String functionName, List<object> parameters)
{
if (functionName == "socket")
{
if (_server != null)
{
// oops, already open
return 0;
}
int port = (int)parameters[0];
_server = new Server(port);
return 1;
}
if (functionName == "sockread")
{
if (_server != null)
{
return _server.Read() ?? "nope";
}
else
{
return "nope";
}
}
return false;
}
I need to implement a TCP client application. The client and the server send messages to each other. I want to make this program scalable enough to handle connections to multiple servers at the same time. It seems like asynchronous sockets is the way to go for this. I'm new to C# so I'm pretty sure I don't know what I'm doing here. I wrote some classes and a simple console program to get started with. Eventually, I want to create a Windows Forms application but I want to start small and simple first. The Client class runs in its own thread. Is this all thread-safe and correctly done? It's a lot of code and I tried to cut out some fat.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace FastEyeClient
{
class Program
{
static void Main(string[] args)
{
Client client = new Client();
client.ConnectEvent += new ConnectEventHandler(OnConnect);
client.SetLiveStatusEvent += new SetLiveStatusEventHandler(OnSetLiveStatus);
client.Connect("hostname", 1987);
Thread.Sleep(1000);
client.SetLiveStatus("hostname", true);
}
private static void OnConnect(object sender, ConnectEventArgs e)
{
Console.WriteLine(e.Message);
}
private static void OnSetLiveStatus(object sender, SetLiveStatusEventArgs e)
{
Console.WriteLine(e.Message);
}
}
}
Client.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace FastEyeClient
{
public delegate void ConnectEventHandler(object sender, ConnectEventArgs e);
public delegate void SetLiveStatusEventHandler(object sender, SetLiveStatusEventArgs e);
public class Client : IDisposable
{
public event ConnectEventHandler ConnectEvent;
public event SetLiveStatusEventHandler SetLiveStatusEvent;
ServerManager m_Manager;
EventWaitHandle m_WaitHandle;
readonly object m_Locker;
Queue<Event> m_Tasks;
Thread m_Thread;
public Client()
{
m_Manager = new ServerManager(this);
m_WaitHandle = new AutoResetEvent(false);
m_Locker = new object();
m_Tasks = new Queue<Event>();
m_Thread = new Thread(Run);
m_Thread.Start();
}
public void EnqueueTask(Event task)
{
lock (m_Locker)
{
m_Tasks.Enqueue(task);
}
m_WaitHandle.Set();
}
public void Dispose()
{
EnqueueTask(null);
m_Thread.Join();
m_WaitHandle.Close();
}
private void Run()
{
while (true)
{
Event task = null;
lock (m_Locker)
{
if (m_Tasks.Count > 0)
{
task = m_Tasks.Dequeue();
if (task == null)
{
return;
}
}
}
if (task != null)
{
task.DoTask(m_Manager);
}
else
{
m_WaitHandle.WaitOne();
}
}
}
public void Connect(string hostname, int port)
{
EnqueueTask(new ConnectEvent(hostname, port));
}
public void SetLiveStatus(string hostname, bool status)
{
EnqueueTask(new SetLiveEvent(hostname, status));
}
public void OnConnect(bool isConnected, string message)
{
if (ConnectEvent != null)
{
ConnectEvent(this, new ConnectEventArgs(isConnected, message));
}
}
public void OnSetLiveStatus(string hostname, string message)
{
if (SetLiveStatusEvent != null)
{
SetLiveStatusEvent(this, new SetLiveStatusEventArgs(hostname, message));
}
}
}
}
Server.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace FastEyeClient
{
public class Server
{
private ServerManager m_Manager;
private string m_Hostname;
private bool m_IsLive;
private class StateObject
{
public Socket AsyncSocket = null;
public const int BufferSize = 1024;
public byte[] Buffer = new byte[BufferSize];
public StringBuilder Builder = new StringBuilder();
}
public Server(ServerManager manager, Socket socket)
{
try
{
m_Manager = manager;
IPEndPoint endPoint = (IPEndPoint)socket.RemoteEndPoint;
IPAddress ipAddress = endPoint.Address;
IPHostEntry hostEntry = Dns.GetHostEntry(ipAddress);
Hostname = hostEntry.HostName;
IsLive = false;
StateObject state = new StateObject();
state.AsyncSocket = socket;
socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception)
{
}
}
public string Hostname
{
get
{
return m_Hostname;
}
set
{
m_Hostname = value;
}
}
public bool IsLive
{
get
{
return m_IsLive;
}
set
{
m_IsLive = value;
}
}
private void ReceiveCallback(IAsyncResult result)
{
try
{
StateObject state = (StateObject)result.AsyncState;
Socket socket = state.AsyncSocket;
int read = socket.EndReceive(result);
if (read > 0)
{
state.Builder.Append(Encoding.ASCII.GetString(state.Buffer, 0, read));
if (state.Builder.Length > 1)
{
string messages = state.Builder.ToString();
ParseMessages(messages);
}
}
StateObject newState = new StateObject();
newState.AsyncSocket = socket;
socket.BeginReceive(newState.Buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), newState);
}
catch (Exception)
{
}
}
private void ParseMessages(string messages)
{
string[] messagesArray = messages.Split('\n');
foreach (string message in messagesArray)
{
string[] tokens = message.Split(',');
if (tokens[0].Contains("#"))
{
ParseServerMessage(tokens);
}
}
}
private void ParseServerMessage(string[] tokens)
{
tokens[0].Remove(0, 1);
if (tokens[0] == "4")
{
bool status;
if (tokens[1] == "0")
{
status = false;
m_Manager.SetLiveStatus(m_Hostname, status);
}
else if (tokens[1] == "1")
{
status = true;
m_Manager.SetLiveStatus(m_Hostname, status);
}
}
}
}
}
ServerManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace FastEyeClient
{
public class ServerManager
{
private Client m_Client;
private Dictionary<string, Server> m_Servers;
private object m_Locker;
public ServerManager(Client client)
{
m_Client = client;
m_Servers = new Dictionary<string, Server>();
m_Locker = new object();
}
public void AddServer(string hostname, int port)
{
try
{
IPAddress[] IPs = Dns.GetHostAddresses(hostname);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.BeginConnect(IPs, port, new AsyncCallback(ConnectCallback), socket);
}
catch (Exception)
{
bool isConnected = false;
string message = "Could not connect to server.";
m_Client.OnConnect(isConnected, message);
}
}
private void ConnectCallback(IAsyncResult ar)
{
bool isConnected;
string message;
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
IPEndPoint endPoint = (IPEndPoint)socket.RemoteEndPoint;
IPAddress ipAddress = endPoint.Address;
IPHostEntry hostEntry = Dns.GetHostEntry(ipAddress);
string hostname = hostEntry.HostName;
lock (m_Servers)
{
if (m_Servers.ContainsKey(hostname))
{
isConnected = false;
message = "Client is already connected to server";
}
else
{
m_Servers.Add(hostname, new Server(this, socket));
isConnected = true;
message = "Successfully connected.";
}
}
m_Client.OnConnect(isConnected, message);
}
catch (Exception)
{
isConnected = false;
message = "Could not connect to server.";
m_Client.OnConnect(isConnected, message);
}
}
public void SetLiveStatus(string hostname, bool newStatus)
{
string message;
lock (m_Locker)
{
if (m_Servers.ContainsKey(hostname))
{
if (m_Servers[hostname].IsLive == newStatus)
{
message = "Server is already set to this status.";
}
else
{
m_Servers[hostname].IsLive = newStatus;
message = "Successfully set new status.";
}
}
else
{
message = "Server not found.";
}
}
m_Client.OnSetLiveStatus(hostname, message);
}
}
}
Does it Run?
Does it throw an exception(s)?
Pitfall in trying to run server code in multiple threads:
AVOID attempting to manipulate, read or write a socket in different threads. Have one thread accept connections from the server socket and spawn a thread to handle transactions. If you get too many threads going on at once, you're going to have have 1 thread handle several sockets.
No it is not thread safe.
A subscriber can have unsubscribe before between the check and the invocation:
if (ConnectEvent != null)
{
ConnectEvent(this, new ConnectEventArgs(isConnected, message));
}
Define the event as:
public event ConnectEventHandler ConnectEvent = delegate{};
and remove the event to get thread safety.
I would reduce the run loop to:
private void Run()
{
while (true)
{
m_WaitHandle.WaitOne();
Event task = null;
lock (m_Locker)
{
if (m_Tasks.Count == 0)
{
m_WaitHandle.Reset();
continue;
}
task = m_Tasks.Dequeue();
}
task.DoTask(m_Manager);
}
}
The loop will continue to run until the event is reset.
Make sure that no null items are inserted into the queue instead of checking for null.
You could simplify the producer-consumer pattern in the Client class by using BlockingCollection instead of a combination of an AutoResetEvent and plain old Queue.
The EnqueueTask method would look like:
public void EnqueueTask(Event task)
{
m_Queue.Add(task);
}
The Run method would look like:
public void Run()
{
while (true)
{
Event task = m_Queue.Take();
if (task == null)
{
return;
}
task.DoTask();
}
}
I have a small method I use to disable my socket that listens for incoming connections.
/// <summary>
/// Stops and disables the service
/// </summary>
public void Disable() {
if (Running) {
try {
thread.Abort();
}
catch (System.Threading.ThreadAbortException) {
// This catch is not raised.
// We can simply process our closing of the socket and nullify the thread
}
finally {
socket.Close();
socket = null;
thread = null;
if (socket == null && thread == null) {
m_Running = false;
OnDisabled(this, new EventArgs());
}
}
}
}
My problem is that even after I call Close() and nullify the socket, the clients still remain connected. I ran a check using netstat -a, and it shows the clients are still connected.
TCP 127.0.0.1:2161 activate:7777 ESTABLISHED
TCP 127.0.0.1:7777 activate:2161 ESTABLISHED
7777 is the port my host socket listens on. So my question is, after closing the host socket, why do the client sockets not disconnect. How do they remain connected to a socket that is null, and is no longer listenning?
Some additional info
/// <summary>
/// Enables and runs the service
/// </summary>
public void Enable() {
if (!Running) {
ThreadStart start = new ThreadStart(RunServiceAsync);
thread = new Thread(start);
thread.IsBackground = true;
thread.Start();
m_Running = true;
OnEnabled(this, new EventArgs());
}
}
The above method is how the thread is created. Everything works fine, the threading, connections; the only issue is when I close the socket (host), and nullify it the clients are still connected to it.
The question is, once the host socket is closed and set to null, what are the clients connected to? Shouldn't they disconnect and lose connection to the host, because the host socket is closed?
Here is the full code to help
// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// ***************************************
namespace CipherBox.Drivers {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.ComponentModel;
using CipherBox.Objects;
/// <summary>
/// Driver that manages the network connection between the master program and clients, also provides informational events
/// </summary>
public class NetworkDriver : IDriver {
#region Fields
private Socket socket;
private Thread thread;
#endregion
#region Properties
private int m_Port = 7777;
/// <summary>
/// Gets the port that the network runs on. The default port is 7777.
/// </summary>
public int Port {
get {
return m_Port;
}
}
#endregion
#region Events
/// <summary>
/// Delegate for when a node connects to the service
/// </summary>
public delegate void NodeConnectedEventHandler(object sender, NetworkNodeEventArgs e);
/// <summary>
/// Triggers when an node connects to the service
/// </summary>
public event NodeConnectedEventHandler NodeConnected;
/// <summary>
/// Event callback for NodeConnected
/// </summary>
private void OnNodeConnected(object sender, NetworkNodeEventArgs e) {
if (NodeConnected != null) {
foreach (NodeConnectedEventHandler handler in NodeConnected.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
NodeConnected(this, e);
}
}
}
}
/// <summary>
/// Delegate for when a node disconnects from the service
/// </summary>
public delegate void NodeDisconnectedEventHandler(object sender, NetworkNodeEventArgs e);
/// <summary>
/// Triggers when an node disconnects from the service
/// </summary>
public event NodeDisconnectedEventHandler NodeDisconnected;
/// <summary>
/// Event callback for NodeDisconnected
/// </summary>
private void OnNodeDisconnected(object sender, NetworkNodeEventArgs e) {
if (NodeDisconnected != null) {
foreach (NodeDisconnectedEventHandler handler in NodeDisconnected.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
NodeDisconnected(this, e);
}
}
}
}
#endregion
#region Methods
private NetworkNode FillNode(Socket socket) {
StringBuilder stream = new StringBuilder();
byte[] buffer = new byte[4096];
int bytesReceived = -1;
do {
try {
bytesReceived = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
}
catch (System.Net.Sockets.SocketException) {
return null;
}
finally {
stream.Append(Encoding.ASCII.GetString(buffer, 0, bytesReceived));
}
} while (!stream.ToString().EndsWith("\r\n\r\n"));
string[] packet = stream.ToString().Split(new string[] { "\r\n" }, StringSplitOptions.None);
if (packet.Length == 9) {
if (packet[0].ToLower() == "CipherBox".ToLower()) {
NetworkNode node = new NetworkNode();
node.Domain = packet[1];
node.LocalIP = IPAddress.Parse(packet[2]);
node.MachineName = packet[3];
node.Platform = packet[4];
node.RemoteIP = IPAddress.Parse(packet[5]);
node.Workgroup = packet[6];
node.Socket = socket;
return node;
}
else {
return null;
}
}
else {
return null;
}
}
private bool IsDisconnected(Socket socket) {
bool connected = false;
try {
connected = !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
}
catch (System.Net.Sockets.SocketException) {
connected = false;
}
return !connected;
}
private void MonitorNode(NetworkNode node) {
ParameterizedThreadStart start = new ParameterizedThreadStart(MonitorNodeAsync);
Thread thread = new Thread(start);
thread.IsBackground = true;
thread.Start(node);
}
private void MonitorNodeAsync(object obj) {
NetworkNode node = obj as NetworkNode;
while (Running || node != null) {
if (IsDisconnected(node.Socket)) {
node.Socket.Shutdown(SocketShutdown.Both);
node.Socket.Close();
node.Socket = null;
OnNodeDisconnected(null, new NetworkNodeEventArgs(node));
return;
}
else {
Thread.Sleep(1000);
}
}
}
private void RunServiceAsync() {
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, Port);
socket.Bind(localEP);
socket.Listen(1);
do {
Socket client;
try {
client = socket.Accept();
}
catch (System.Net.Sockets.SocketException) {
continue;
}
NetworkNode node = FillNode(client);
if (node != null) {
OnNodeConnected(null, new NetworkNodeEventArgs(node));
MonitorNode(node);
}
} while (Running);
}
/// <summary>
/// Sets the port that the network runs on
/// </summary>
/// <param name="port">The port to set</param>
public void SetPort(int port) {
m_Port = port;
}
#endregion
#region IDriver Members
/// <summary>
/// Triggered when the network driver is disabled
/// </summary>
public event EventHandler<EventArgs> Disabled;
/// <summary>
/// Event callback for Disabled
/// </summary>
private void OnDisabled(object sender, System.EventArgs e) {
if (Disabled != null) {
foreach (EventHandler<EventArgs> handler in Disabled.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
Disabled(this, e);
}
}
}
}
/// <summary>
/// Triggered when the network driver is enabled
/// </summary>
public event EventHandler<EventArgs> Enabled;
/// <summary>
/// Event callback for Enabled
/// </summary>
private void OnEnabled(object sender, System.EventArgs e) {
if (Enabled != null) {
foreach (EventHandler<EventArgs> handler in Enabled.GetInvocationList()) {
ISynchronizeInvoke syncInvoke = handler.Target as ISynchronizeInvoke;
if (syncInvoke != null && syncInvoke.InvokeRequired) {
syncInvoke.Invoke(handler, new object[] { handler.Target, e });
}
else {
Enabled(this, e);
}
}
}
}
/// <summary>
/// Stops and disables the service
/// </summary>
public void Disable() {
if (Running) {
try {
thread.Abort();
}
catch (System.Threading.ThreadAbortException) {
// This catch is not raised.
// We can simply process our closing of the socket and nullify the thread
}
finally {
socket.Close();
socket = null;
thread = null;
if (socket == null && thread == null) {
m_Running = false;
OnDisabled(this, new EventArgs());
}
}
}
}
/// <summary>
/// Enables and runs the service
/// </summary>
public void Enable() {
if (!Running) {
ThreadStart start = new ThreadStart(RunServiceAsync);
thread = new Thread(start);
thread.IsBackground = true;
thread.Start();
m_Running = true;
OnEnabled(this, new EventArgs());
}
}
private bool m_Running = false;
/// <summary>
/// Gets a System.Boolean value indicating whether the service is running or not
/// </summary>
public bool Running {
get {
return m_Running;
}
}
#endregion
}
}
Yo have to call socket.shutdown(Both) the parameter could be Send, Receive or Both Depending of how you like to end the connection. This function send the necessary TCP message to the client to close the connection.
I corrected the issue by storing references to each connection in a collection and closing all the connections using the client sockets.
private void DestructConnections() {
foreach (Socket connection in connections) {
connection.Shutdown(SocketShutdown.Both);
connection.Close();
}
connections.Clear();
}
It is recommended that when using a connection oriented socket you should call Shutdown before you Close the connection. Shutdown is for notifying communication end. Close is for releasing the managed / unmanaged resources
Resource:
http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown.aspx
When you work with sockets in any language, you create server socket which only listen and accept connections and there're many p2p sockets to send and/or receive data.
So your answer is design of sockets subsystem in Microsoft Windows, BSD, Linux, etc.