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.
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 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;
}
so I have my client-server program which was working fine, but now I ran into an issue and I have not a single clue on how to solve it. So my client connects to the server, using async sockets. But when it tries to get the async result, it contains nothing. It disposes the socket and starts all over. This is my code(client):
public class Client
{
/// <summary>
/// Occurs as a result of an unrecoverable issue with the client.
/// </summary>
public event ClientFailEventHandler ClientFail;
/// <summary>
/// Represents a method that will handle failure of the client.
/// </summary>
/// <param name="s">The client that has failed.</param>
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
public delegate void ClientFailEventHandler(Client s, Exception ex);
/// <summary>
/// Fires an event that informs subscribers that the client has failed.
/// </summary>
/// <param name="ex">The exception containing information about the cause of the client's failure.</param>
private void OnClientFail(Exception ex)
{
var handler = ClientFail;
if (handler != null)
{
handler(this, ex);
}
}
/// <summary>
/// Occurs when the state of the client has changed.
/// </summary>
public event ClientStateEventHandler ClientState;
/// <summary>
/// Represents the method that will handle a change in the client's state
/// </summary>
/// <param name="s">The client which changed its state.</param>
/// <param name="connected">The new connection state of the client.</param>
public delegate void ClientStateEventHandler(Client s, bool connected);
/// <summary>
/// Fires an event that informs subscribers that the state of the client has changed.
/// </summary>
/// <param name="connected">The new connection state of the client.</param>
private void OnClientState(bool connected)
{
if (Connected == connected) return;
Connected = connected;
var handler = ClientState;
if (handler != null)
{
handler(this, connected);
}
}
/// <summary>
/// Occurs when a packet is received from the server.
/// </summary>
public event ClientReadEventHandler ClientRead;
/// <summary>
/// Represents a method that will handle a packet from the server.
/// </summary>
/// <param name="s">The client that has received the packet.</param>
/// <param name="packet">The packet that has been received by the server.</param>
public delegate void ClientReadEventHandler(Client s, IPacket packet);
/// <summary>
/// Fires an event that informs subscribers that a packet has been received by the server.
/// </summary>
/// <param name="packet">The packet that has been received by the server.</param>
private void OnClientRead(IPacket packet)
{
var handler = ClientRead;
if (handler != null)
{
handler(this, packet);
}
}
/// <summary>
/// Occurs when a packet is sent by the client.
/// </summary>
public event ClientWriteEventHandler ClientWrite;
/// <summary>
/// Represents the method that will handle the sent packet.
/// </summary>
/// <param name="s">The client that has sent the packet.</param>
/// <param name="packet">The packet that has been sent by the client.</param>
/// <param name="length">The length of the packet.</param>
/// <param name="rawData">The packet in raw bytes.</param>
public delegate void ClientWriteEventHandler(Client s, IPacket packet, long length, byte[] rawData);
/// <summary>
/// Fires an event that informs subscribers that the client has sent a packet.
/// </summary>
/// <param name="packet">The packet that has been sent by the client.</param>
/// <param name="length">The length of the packet.</param>
/// <param name="rawData">The packet in raw bytes.</param>
private void OnClientWrite(IPacket packet, long length, byte[] rawData)
{
var handler = ClientWrite;
if (handler != null)
{
handler(this, packet, length, rawData);
}
}
/// <summary>
/// The type of the packet received.
/// </summary>
public enum ReceiveType
{
Header,
Payload
}
/// <summary>
/// The buffer size for receiving data in bytes.
/// </summary>
public int BUFFER_SIZE { get { return 1024 * 16; } } // 16KB
/// <summary>
/// The keep-alive time in ms.
/// </summary>
public uint KEEP_ALIVE_TIME { get { return 25000; } } // 25s
/// <summary>
/// The keep-alive interval in ms.
/// </summary>
public uint KEEP_ALIVE_INTERVAL { get { return 25000; } } // 25s
/// <summary>
/// The header size in bytes.
/// </summary>
public int HEADER_SIZE { get { return 4; } } // 4B
/// <summary>
/// The maximum size of a packet in bytes.
/// </summary>
public int MAX_PACKET_SIZE { get { return (1024 * 1024) * 5; } } // 5MB
/// <summary>
/// Returns an array containing all of the proxy clients of this client.
/// </summary>
public ReverseProxyClient[] ProxyClients
{
get
{
lock (_proxyClientsLock)
{
return _proxyClients.ToArray();
}
}
}
/// <summary>
/// Handle of the Client Socket.
/// </summary>
private Socket _handle;
/// <summary>
/// A list of all the connected proxy clients that this client holds.
/// </summary>
private List<ReverseProxyClient> _proxyClients;
/// <summary>
/// Lock object for the list of proxy clients.
/// </summary>
private readonly object _proxyClientsLock = new object();
/// <summary>
/// The buffer for incoming packets.
/// </summary>
private byte[] _readBuffer;
/// <summary>
/// The buffer for the client's incoming payload.
/// </summary>
private byte[] _payloadBuffer;
/// <summary>
/// The Queue which holds buffers to send.
/// </summary>
private readonly Queue<byte[]> _sendBuffers = new Queue<byte[]>();
/// <summary>
/// Determines if the client is currently sending packets.
/// </summary>
private bool _sendingPackets;
/// <summary>
/// Lock object for the sending packets boolean.
/// </summary>
private readonly object _sendingPacketsLock = new object();
/// <summary>
/// The Queue which holds buffers to read.
/// </summary>
private readonly Queue<byte[]> _readBuffers = new Queue<byte[]>();
/// <summary>
/// Determines if the client is currently reading packets.
/// </summary>
private bool _readingPackets;
/// <summary>
/// Lock object for the reading packets boolean.
/// </summary>
private readonly object _readingPacketsLock = new object();
/// <summary>
/// The temporary header to store parts of the header.
/// </summary>
/// <remarks>
/// This temporary header is used when we have i.e.
/// only 2 bytes left to read from the buffer but need more
/// which can only be read in the next Receive callback
/// </remarks>
private byte[] _tempHeader;
/// <summary>
/// Decides if we need to append bytes to the header.
/// </summary>
private bool _appendHeader;
// Receive info
private int _readOffset;
private int _writeOffset;
private int _tempHeaderOffset;
private int _readableDataLen;
private int _payloadLen;
private ReceiveType _receiveState = ReceiveType.Header;
/// <summary>
/// Gets if the client is currently connected to a server.
/// </summary>
public bool Connected { get; private set; }
/// <summary>
/// The packet serializer.
/// </summary>
protected Serializer Serializer { get; set; }
private const bool encryptionEnabled = true;
private const bool compressionEnabled = true;
protected Client()
{
_proxyClients = new List<ReverseProxyClient>();
_readBuffer = new byte[BUFFER_SIZE];
_tempHeader = new byte[HEADER_SIZE];
}
/// <summary>
/// Attempts to connect to the specified ip address on the specified port.
/// </summary>
/// <param name="ip">The ip address to connect to.</param>
/// <param name="port">The port of the host.</param>
protected void Connect(IPAddress ip, ushort port)
{
try
{
Disconnect();
_handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_handle.SetKeepAliveEx(KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
_handle.Connect(ip, port);
if (_handle.Connected)
{
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
OnClientState(true);
}
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
private void AsyncReceive(IAsyncResult result)
{
int bytesTransferred; // error occurs here
try
{
bytesTransferred = _handle.EndReceive(result);
if (bytesTransferred <= 0)
throw new Exception("no bytes transferred");
}
catch (NullReferenceException)
{
return;
}
catch (ObjectDisposedException)
{
return;
}
catch (Exception)
{
Disconnect();
return;
}
byte[] received = new byte[bytesTransferred];
try
{
Array.Copy(_readBuffer, received, received.Length);
}
catch (Exception ex)
{
OnClientFail(ex);
return;
}
lock (_readBuffers)
{
_readBuffers.Enqueue(received);
}
lock (_readingPacketsLock)
{
if (!_readingPackets)
{
_readingPackets = true;
ThreadPool.QueueUserWorkItem(AsyncReceive);
}
}
try
{
_handle.BeginReceive(_readBuffer, 0, _readBuffer.Length, SocketFlags.None, AsyncReceive, null);
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
OnClientFail(ex);
}
}
private void AsyncReceive(object state)
{
while (true)
{
byte[] readBuffer;
lock (_readBuffers)
{
if (_readBuffers.Count == 0)
{
lock (_readingPacketsLock)
{
_readingPackets = false;
}
return;
}
readBuffer = _readBuffers.Dequeue();
}
_readableDataLen += readBuffer.Length;
bool process = true;
while (process)
{
switch (_receiveState)
{
case ReceiveType.Header:
{
if (_readableDataLen + _tempHeaderOffset >= HEADER_SIZE)
{ // we can read the header
int headerLength = (_appendHeader)
? HEADER_SIZE - _tempHeaderOffset
: HEADER_SIZE;
try
{
if (_appendHeader)
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset,
headerLength);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_payloadLen = BitConverter.ToInt32(_tempHeader, 0);
_tempHeaderOffset = 0;
_appendHeader = false;
}
else
{
_payloadLen = BitConverter.ToInt32(readBuffer, _readOffset);
}
if (_payloadLen <= 0 || _payloadLen > MAX_PACKET_SIZE)
throw new Exception("invalid header");
}
catch (Exception)
{
process = false;
Disconnect();
break;
}
_readableDataLen -= headerLength;
_readOffset += headerLength;
_receiveState = ReceiveType.Payload;
}
else // _readableDataLen < HEADER_SIZE
{
try
{
Array.Copy(readBuffer, _readOffset, _tempHeader, _tempHeaderOffset, _readableDataLen);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_tempHeaderOffset += _readableDataLen;
_appendHeader = true;
process = false;
}
break;
}
case ReceiveType.Payload:
{
if (_payloadBuffer == null || _payloadBuffer.Length != _payloadLen)
_payloadBuffer = new byte[_payloadLen];
int length = (_writeOffset + _readableDataLen >= _payloadLen)
? _payloadLen - _writeOffset
: _readableDataLen;
try
{
Array.Copy(readBuffer, _readOffset, _payloadBuffer, _writeOffset, length);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
_writeOffset += length;
_readOffset += length;
_readableDataLen -= length;
if (_writeOffset == _payloadLen)
{
bool isError = _payloadBuffer.Length == 0;
if (!isError)
{
if (encryptionEnabled)
_payloadBuffer = AES.Decrypt(_payloadBuffer);
isError = _payloadBuffer.Length == 0; // check if payload decryption failed
}
if (!isError)
{
if (compressionEnabled)
{
try
{
_payloadBuffer = SafeQuickLZ.Decompress(_payloadBuffer);
}
catch (Exception)
{
process = false;
Disconnect();
break;
}
}
isError = _payloadBuffer.Length == 0; // check if payload decompression failed
}
if (isError)
{
process = false;
Disconnect();
break;
}
using (MemoryStream deserialized = new MemoryStream(_payloadBuffer))
{
try
{
IPacket packet = (IPacket)Serializer.Deserialize(deserialized);
OnClientRead(packet);
}
catch (Exception ex)
{
process = false;
OnClientFail(ex);
break;
}
}
_receiveState = ReceiveType.Header;
_payloadBuffer = null;
_payloadLen = 0;
_writeOffset = 0;
}
if (_readableDataLen == 0)
process = false;
break;
}
}
}
if (_receiveState == ReceiveType.Header)
{
_writeOffset = 0; // prepare for next packet
}
_readOffset = 0;
_readableDataLen = 0;
}
}
/// <summary>
/// Sends a packet to the connected server.
/// </summary>
/// <typeparam name="T">The type of the packet.</typeparam>
/// <param name="packet">The packet to be send.</param>
public void Send<T>(T packet) where T : IPacket
{
if (!Connected || packet == null) return;
lock (_sendBuffers)
{
using (MemoryStream ms = new MemoryStream())
{
try
{
Serializer.Serialize(ms, packet);
}
catch (Exception ex)
{
OnClientFail(ex);
return;
}
byte[] payload = ms.ToArray();
_sendBuffers.Enqueue(payload);
OnClientWrite(packet, payload.LongLength, payload);
lock (_sendingPacketsLock)
{
if (_sendingPackets) return;
_sendingPackets = true;
}
ThreadPool.QueueUserWorkItem(Send);
}
}
}
/// <summary>
/// Sends a packet to the connected server.
/// Blocks the thread until all packets have been sent.
/// </summary>
/// <typeparam name="T">The type of the packet.</typeparam>
/// <param name="packet">The packet to be send.</param>
public void SendBlocking<T>(T packet) where T : IPacket
{
Send(packet);
while (_sendingPackets)
{
Thread.Sleep(10);
}
}
private void Send(object state)
{
while (true)
{
if (!Connected)
{
SendCleanup(true);
return;
}
byte[] payload;
lock (_sendBuffers)
{
if (_sendBuffers.Count == 0)
{
SendCleanup();
return;
}
payload = _sendBuffers.Dequeue();
}
try
{
_handle.Send(BuildPacket(payload));
}
catch (Exception ex)
{
OnClientFail(ex);
SendCleanup(true);
return;
}
}
}
private byte[] BuildPacket(byte[] payload)
{
if (compressionEnabled)
payload = SafeQuickLZ.Compress(payload);
if (encryptionEnabled)
payload = AES.Encrypt(payload);
byte[] packet = new byte[payload.Length + HEADER_SIZE];
Array.Copy(BitConverter.GetBytes(payload.Length), packet, HEADER_SIZE);
Array.Copy(payload, 0, packet, HEADER_SIZE, payload.Length);
return packet;
}
private void SendCleanup(bool clear = false)
{
lock (_sendingPacketsLock)
{
_sendingPackets = false;
}
if (!clear) return;
lock (_sendBuffers)
{
_sendBuffers.Clear();
}
}
/// <summary>
/// Disconnect the client from the server, disconnect all proxies that
/// are held by this client, and dispose of other resources associated
/// with this client.
/// </summary>
public void Disconnect()
{
if (_handle != null)
{
_handle.Close();
_handle = null;
_readOffset = 0;
_writeOffset = 0;
_tempHeaderOffset = 0;
_readableDataLen = 0;
_payloadLen = 0;
_payloadBuffer = null;
_receiveState = ReceiveType.Header;
if (_proxyClients != null)
{
lock (_proxyClientsLock)
{
try
{
foreach (ReverseProxyClient proxy in _proxyClients)
proxy.Disconnect();
}
catch (Exception)
{
}
}
}
if (Commands.CommandHandler.StreamCodec != null)
{
Commands.CommandHandler.StreamCodec.Dispose();
Commands.CommandHandler.StreamCodec = null;
}
}
OnClientState(false);
}
public void ConnectReverseProxy(ReverseProxyConnect command)
{
lock (_proxyClientsLock)
{
_proxyClients.Add(new ReverseProxyClient(command, this));
}
}
public ReverseProxyClient GetReverseProxyByConnectionId(int connectionId)
{
lock (_proxyClientsLock)
{
return _proxyClients.FirstOrDefault(t => t.ConnectionId == connectionId);
}
}
public void RemoveProxyClient(int connectionId)
{
try
{
lock (_proxyClientsLock)
{
for (int i = 0; i < _proxyClients.Count; i++)
{
if (_proxyClients[i].ConnectionId == connectionId)
{
_proxyClients.RemoveAt(i);
break;
}
}
}
}
catch { }
}
}
The output from visual studio:
Exception thrown: 'System.Net.Sockets.SocketException' in System.dll
Client Fail - Exception Message: No connection could be made because the target machine actively refused it 127.0.0.1:4782
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Client Fail - Exception Message: Object reference not set to an instance of an object.
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.ObjectDisposedException' in System.dll
Exception thrown: 'System.NullReferenceException' in Client.exe
Exception thrown: 'System.NullReferenceException' in Client.exe
The refuse was because the server wasnt running, when I start the server and it connects the crsahing loop begins
Thanks in advance
Found the error, the connection thread on the client had an empty ipadress string in the code and had the hostname it used didnt exist, I should've looked better at all code. Thanks
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.
I use C#, .NET Framework 4.5, VS2015.
I implement an event handler for event (TcpRequest) that is fired by a black-boxed module.
My event handler call a private method that fires an event to client/GUI and waiting for user's response.
Meanwhile the black-boxed module keeps firing TcpRequest event. Somehow I need to queue the incoming events while waiting.
As soon as the user gives response for the first event, the queue can be "flushed".
Is there any design pattern or best practice for such case (to queue while wait for user's response)? Do I need to use await or something?
Following are my codes. Please feel free to modify them. Thank you in advance.
public void TcpRequestHandler(int id, CONN_INFO connInfo)
{
if (SomeCondition)
{
var myArgs = new MyEventArgs()
{
Id = id,
ConnInfo = connInfo
}
// this the way I tried and I know it is wrong,
// because it always fires event without "waiting in queue"
lock (_eventQueue)
{
_eventQueue.Add(myArgs);
}
FireEventToClient(myArgs);
}
}
private void FireEventToClient(MyEventArgs myArgs)
{
EventToClient(this, myArgs);
if (myArgs.Continue)
{
// "flush" the event queue
...
// do other things
...
}
}
public class MyEventArgs : EventArgs
{
public int Id {get; private set;}
public CONN_INFO ConnInfo {get; private set;}
public bool Continue {get; set;}
}
Declare your event EventToClient with return value and use the returned value for validation and flush the event queue
Check this, it may help
public class TCPServer
{
private int _port;
private TcpListener _tcpListener;
private bool _running, _disposed;
private BinaryFormatter _bFormatter;
private Thread _connectionThread;
private BackgroundWorker _bgwListener;
private BackgroundWorker _bgwSender;
private object syncobject = new object();
private object syncsendmessageobject = new object();
/// <summary>
/// Constructor - Initialises the TCPServer with the given port and a tcplistener. Starts a thread to monitor the message queue
/// </summary>
/// <param name="port"></param>
public TCPServer(int port)
{
try
{
this._port = port;
this._tcpListener = new TcpListener(IPAddress.Loopback, port);
this._running = false;
this._bFormatter = new BinaryFormatter();
Thread thread = new Thread(ReadQueue);
thread.Start();
}
catch (Exception ex)
{
}
}
/// <summary>
/// Starts the tcplistener.
/// </summary>
public void Start()
{
try
{
if (!_running)
{
this.MessageReceived -= TCPServer_MessageReceived;
this._tcpListener.Start();
this._running = true;
this._connectionThread = new Thread(new ThreadStart(ListenForClientConnections));
this._connectionThread.Start();
this._connectionThread.IsBackground = true;
this.MessageReceived += TCPServer_MessageReceived;
}
}
catch (Exception ex)
{
}
}
/// <summary>
/// Stops the tcplistener
/// </summary>
public void Stop()
{
if (this._running)
{
this._tcpListener.Stop();
this._tcpListener = null;
this._running = false;
}
}
public bool Running()
{
return this._running;
}
public void StopListening()
{
try
{
lock (this)
{
if (this._running)
{
this._tcpListener.AcceptTcpClient().Close();
_running = false;
}
}
}
catch (Exception ex)
{
}
}
/// <summary>
/// Thread body for listening for client connections
/// </summary>
private void ListenForClientConnections()
{
try
{
while (this._running)
{
lock (this)
{
TcpClient connectedTcpClient = this._tcpListener.AcceptTcpClient();
this._bgwListener = new BackgroundWorker();
this._bgwListener.DoWork += _bgwListener_DoWork;
this._bgwListener.WorkerReportsProgress = true;
this._bgwListener.ProgressChanged += _bgwListener_ProgressChanged;
this._bgwListener.RunWorkerAsync(connectedTcpClient);
}
}
}
catch (Exception ex)
{
}
}
/// <summary>
/// background worker listens to messages sent by clients. Categorises them based on messages/commands/applicationcommands/
/// progress response etc..
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private TcpClient clientGUI = null;
private Queue<string> messageQueue = new Queue<string>();
private void _bgwListener_DoWork(object sender, DoWorkEventArgs e)
{
TcpClient client = e.Argument as TcpClient;
BackgroundWorker bgWorker = sender as BackgroundWorker;
if (client != null)
{
try
{
while (this._running)
{
// Block until an instance Message is received
string message = this._bFormatter.Deserialize(client.GetStream()).ToString();
if (message.Equals("GUI", StringComparison.InvariantCultureIgnoreCase)
{
// Chek for first message from tcp client, if it is GUI then store it in a member variable, if you have multiple cleints then use a dictionary
clientGUI = client;
}
if(!clientGUI.Equals(client))
{
lock (syncobject)
{
messageQueue.Enqueue(message);
}
}
}
}
catch (Exception ex)
{
}
}
}
/// <summary>
/// Thread that continuously monitors the message queue for messages and
/// sends them to clients
/// </summary>
private void ReadQueue()
{
try
{
while (true)
{
try
{
TCPMessage message = null;
if (messageQueue.Count > 0)
{
lock (syncobject)
{
message = messageQueue.Dequeue();
}
if (!string.IsNullOrEmpty(message) && clientGUI != null)
{
SendMessage(clientGUI, message);
}
}
else { Thread.Sleep(10); }
}
catch (Exception ex)
{
}
}
}
catch (Exception ex)
{
}
}
private MessageReceivedEventHandler _messageReceived;
public event MessageReceivedEventHandler MessageReceived
{
add { _messageReceived += value; }
remove { _messageReceived -= value; }
}
private EventHandler<MessageEventArgs> _serverMessageSent;
public event EventHandler<MessageEventArgs> ServerMessageSent
{
add { _serverMessageSent += value; }
remove { _serverMessageSent -= value; }
}
private void SendMessage(TcpClient client, TCPMessage message)
{
try
{
lock (syncsendmessageobject)
_bFormatter.Serialize(client.GetStream(), message);
}
catch (Exception ex)
{
}
}
}