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);
}
}
}
Related
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..
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?
public class Telnet
{
private IPEndPoint iep;
private string address;
private int port;
private int timeout;
private Socket s;
Byte[] m_byBuff = new Byte[32767];
private string strWorkingData = ""; // Holds everything received from the server since our last processing
private string strFullLog = "";
public Telnet(string Address, int Port, int CommandTimeout)
{
address = Address;
port = Port;
timeout = CommandTimeout;
}
private void OnRecievedData(IAsyncResult ar)
{
// Get The connection socket from the callback
Socket sock = (Socket)ar.AsyncState;
// Get The data , if any
int nBytesRec = sock.EndReceive(ar);
if (nBytesRec > 0)
{
// Decode the received data
string sRecieved = CleanDisplay(Encoding.ASCII.GetString(m_byBuff, 0, nBytesRec));
// Write out the data
if (sRecieved.IndexOf("[c") != -1) Negotiate(1);
if (sRecieved.IndexOf("[6n") != -1) Negotiate(2);
// Console.WriteLine(sRecieved);
strWorkingData += sRecieved;
strFullLog += sRecieved;
// Launch another callback to listen for data
AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
sock.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock);
}
else
{
// If no data was recieved then the connection is probably dead
Console.WriteLine("Disconnected", sock.RemoteEndPoint);
sock.Shutdown(SocketShutdown.Both);
sock.Close();
//Application.Exit();
}
}
private void DoSend(string strText)
{
try
{
Byte[] smk = new Byte[strText.Length];
for (int i = 0; i < strText.Length; i++)
{
Byte ss = Convert.ToByte(strText[i]);
smk[i] = ss;
}
s.Send(smk, 0, smk.Length, SocketFlags.None);
}
catch (Exception ers)
{
Console.Error.WriteLine(ers.ToString());
//MessageBox.Show("ERROR IN RESPOND OPTIONS");
}
}
private void Negotiate(int WhichPart)
{
StringBuilder x;
string neg;
if (WhichPart == 1)
{
x = new StringBuilder();
x.Append((char)27);
x.Append((char)91);
x.Append((char)63);
x.Append((char)49);
x.Append((char)59);
x.Append((char)50);
x.Append((char)99);
neg = x.ToString();
}
else
{
x = new StringBuilder();
x.Append((char)27);
x.Append((char)91);
x.Append((char)50);
x.Append((char)52);
x.Append((char)59);
x.Append((char)56);
x.Append((char)48);
x.Append((char)82);
neg = x.ToString();
}
SendMessage(neg, true);
}
private string CleanDisplay(string input)
{
return input;
}
/// <summary>
/// Connects to the telnet server.
/// </summary>
/// <returns>True upon connection, False if connection fails</returns>
public bool Connect()
{
IPHostEntry IPHost = Dns.Resolve(address);
string[] aliases = IPHost.Aliases;
IPAddress[] addr = IPHost.AddressList;
try
{
// Try a blocking connection to the server
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
iep = new IPEndPoint(addr[0], port);
s.Connect(iep);
// If the connect worked, setup a callback to start listening for incoming data
AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
s.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, s);
// All is good
return true;
}
catch (Exception)
{
// Something failed
return false;
}
}
public void Disconnect()
{
if (s.Connected) s.Close();
}
/// <summary>
/// Waits for a specific string to be found in the stream from the server
/// </summary>
/// <param name="DataToWaitFor">The string to wait for</param>
/// <returns>Always returns 0 once the string has been found</returns>
public int WaitFor(string DataToWaitFor)
{
// Get the starting time
long lngStart = DateTime.Now.AddSeconds(this.timeout).Ticks;
long lngCurTime = 0;
while (strWorkingData.ToLower().IndexOf(DataToWaitFor.ToLower()) == -1)
{
// Timeout logic
lngCurTime = DateTime.Now.Ticks;
if (lngCurTime > lngStart)
{
throw new Exception("Timed Out waiting for : " + DataToWaitFor);
}
Thread.Sleep(1);
}
strWorkingData = "";
return 0;
}
/// <summary>
/// Waits for one of several possible strings to be found in the stream from the server
/// </summary>
/// <param name="DataToWaitFor">A delimited list of strings to wait for</param>
/// <param name="BreakCharacters">The character to break the delimited string with</param>
/// <returns>The index (zero based) of the value in the delimited list which was matched</returns>
public int WaitFor(string DataToWaitFor, string BreakCharacter)
{
// Get the starting time
long lngStart = DateTime.Now.AddSeconds(this.timeout).Ticks;
long lngCurTime = 0;
string[] Breaks = DataToWaitFor.Split(BreakCharacter.ToCharArray());
int intReturn = -1;
while (intReturn == -1)
{
// Timeout logic
lngCurTime = DateTime.Now.Ticks;
if (lngCurTime > lngStart)
{
throw new Exception("Timed Out waiting for : " + DataToWaitFor);
}
Thread.Sleep(1);
for (int i = 0; i < Breaks.Length; i++)
{
if (strWorkingData.ToLower().IndexOf(Breaks[i].ToLower()) != -1)
{
intReturn = i;
}
}
}
return intReturn;
}
/// <summary>
/// Sends a message to the server
/// </summary>
/// <param name="Message">The message to send to the server</param>
/// <param name="SuppressCarriageReturn">True if you do not want to end the message with a carriage return</param>
public void SendMessage(string Message, bool SuppressCarriageReturn)
{
strFullLog += "\r\nSENDING DATA ====> " + Message.ToUpper() + "\r\n";
// Console.WriteLine("SENDING DATA ====> " + Message.ToUpper());
if (!SuppressCarriageReturn)
{
DoSend(Message + "\r");
}
else
{
DoSend(Message);
}
}
/// <summary>
/// Sends a message to the server, automatically appending a carriage return to it
/// </summary>
/// <param name="Message">The message to send to the server</param>
public void SendMessage(string Message)
{
strFullLog += "\r\nSENDING DATA ====> " + Message.ToUpper() + "\r\n";
// Console.WriteLine("SENDING DATA ====> " + Message.ToUpper());
DoSend(Message + "\r");
}
/// <summary>
/// Waits for a specific string to be found in the stream from the server.
/// Once that string is found, sends a message to the server
/// </summary>
/// <param name="WaitFor">The string to be found in the server stream</param>
/// <param name="Message">The message to send to the server</param>
/// <returns>Returns true once the string has been found, and the message has been sent</returns>
public bool WaitAndSend(string WaitFor, string Message)
{
this.WaitFor(WaitFor);
SendMessage(Message);
return true;
}
/// <summary>
/// Sends a message to the server, and waits until the designated
/// response is received
/// </summary>
/// <param name="Message">The message to send to the server</param>
/// <param name="WaitFor">The response to wait for</param>
/// <returns>True if the process was successful</returns>
public int SendAndWait(string Message, string WaitFor)
{
SendMessage(Message);
this.WaitFor(WaitFor);
return 0;
}
public int SendAndWait(string Message, string WaitFor, string BreakCharacter)
{
SendMessage(Message);
int t = this.WaitFor(WaitFor, BreakCharacter);
return t;
}
/// <summary>
/// A full log of session activity
/// </summary>
public string SessionLog
{
get
{
return strFullLog;
}
}
/// <summary>
/// Clears all data in the session log
/// </summary>
public void ClearSessionLog()
{
strFullLog = "";
}
/// <summary>
/// Searches for two strings in the session log, and if both are found, returns
/// all the data between them.
/// </summary>
/// <param name="StartingString">The first string to find</param>
/// <param name="EndingString">The second string to find</param>
/// <param name="ReturnIfNotFound">The string to be returned if a match is not found</param>
/// <returns>All the data between the end of the starting string and the beginning of the end string</returns>
public string FindStringBetween(string StartingString, string EndingString, string ReturnIfNotFound)
{
int intStart;
int intEnd;
intStart = strFullLog.ToLower().IndexOf(StartingString.ToLower());
if (intStart == -1)
{
return ReturnIfNotFound;
}
intStart += StartingString.Length;
intEnd = strFullLog.ToLower().IndexOf(EndingString.ToLower(), intStart);
if (intEnd == -1)
{
// The string was not found
return ReturnIfNotFound;
}
// The string was found, let's clean it up and return it
return strFullLog.Substring(intStart, intEnd - intStart).Trim();
}
}
I need to connect Cisco router using Simple Network Management Protocol (SNMP) from a C# application and get the below details:
CPU and Memory Utilization of Cisco router.
Below is the code:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CiscoNoEnable cNE = new CiscoNoEnable();
cNE.sHostName = "172.22.5.143";
cNE.sUsername = "Eyespot#123";
cNE.sPassword = "eyespot#123";
cNE.getConfig();
}
}
public class CiscoNoEnable
{
public string sHostName;
public string sUsername;
public string sPassword;
private void Initialize_Components()
{
sHostName = "";
sUsername = "";
sPassword = "";
}
public CiscoNoEnable()
{
Initialize_Components();
}
public void getConfig()
{
this.sHostName = this.sHostName.Trim();
this.sUsername = this.sUsername.Trim();
this.sPassword = this.sPassword.Trim();
RafiTEL.Telnet mST = new RafiTEL.Telnet(this.sHostName, 23, 8);
if (mST.Connect() == false)
{
Console.WriteLine("");
Console.WriteLine("Error: ");
Console.WriteLine("Timeout connecting to: " + this.sHostName);
Console.WriteLine("");
}
else
{
try
{
mST.WaitFor("Username:");
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
mST.SendMessage(this.sUsername);
mST.WaitFor("Password:");
mST.SendMessage(this.sPassword);
mST.WaitFor("#");
mST.SendMessage("term len 0");
mST.WaitFor("#");
mST.SendMessage("show run");
mST.WaitFor("#");
mST.SendMessage("exit");
Console.Write(mST.FindStringBetween("bytes\r\n", "\r\n\r\n", "Error: Configuration not obtained"));
}
}
}
}
I am developing a WinCE application (.Net 3.5) which allow connection to terminal via TCPIP, Serial Port and USB.
TCPIP and Serial Port is done but USB is problematic. As the client need to see proof on USB can be done, we need to prove that we can send Hex command through ActiveSync.
I have google for some time and found that ActiveSync/WMDC will provide IP to allow connection with each other. The problem is i couldn't ping or connect via C# socket from PC to WinCE via ActiveSync/WMDC.
The only thing i know is that my WinCE IP when connecting to PC is:
IP Address : 192.168.55.101
Subnet Mask : 255.255.255.0
Default Gateway: 192.168.55.100
Primary DNS : 127.0.0.1
Below is my WinCE server code that i use to allow all connection from ActiveSync. I am reusing my TCPIP code to connect to ActiveSync.
_listener.Bind(new IPEndPoint(IPAddress.Any, _port));
_listener.Listen(100);
_listener.BeginAccept(ConnectionReady, null);
Is there anything that i have missed to allow connection with each other? Thanks in advance.
It seem that it is workable but not in a straight forward way.
Normally we will set the PC as Server while WinCE as Slave. In this case, it is in backwards. WinCE will be the host while PC will be the slave.
Below is my code that i have used to declare and start the connection:
private void SetUSBSerialPort()
{
try
{
usbSerialPort = new USBSerialPort(config.terminal_tcpip_port);
usbSerialPort.OnReceiveData += new USBSerialPort.EventOnReceiveData(usbSerialPort_OnReceiveData);
usbSerialPort.Start();
}
catch (Exception ex)
{
}
}
And below is the class that i used to connect PC and WinCE via ActiveSync:
public class ConnectionStateUSB
{
internal Socket _conn;
//internal TcpServiceProvider _provider;
internal byte[] _buffer;
/// <SUMMARY>
/// Tells you the IP Address of the remote host.
/// </SUMMARY>
public EndPoint RemoteEndPoint
{
get { return _conn.RemoteEndPoint; }
}
/// <SUMMARY>
/// Returns the number of bytes waiting to be read.
/// </SUMMARY>
public int AvailableData
{
get { return _conn.Available; }
}
/// <SUMMARY>
/// Tells you if the socket is connected.
/// </SUMMARY>
public bool Connected
{
get { return _conn.Connected; }
}
/// <SUMMARY>
/// Reads data on the socket, returns the number of bytes read.
/// </SUMMARY>
public int Read(byte[] buffer, int offset, int count)
{
try
{
if (_conn.Available > 0)
return _conn.Receive(buffer, offset, count, SocketFlags.None);
else return 0;
}
catch
{
return 0;
}
}
/// <SUMMARY>
/// Sends Data to the remote host.
/// </SUMMARY>
public bool Write(byte[] buffer, int offset, int count)
{
try
{
_conn.Send(buffer, offset, count, SocketFlags.None);
return true;
}
catch
{
return false;
}
}
/// <SUMMARY>
/// Ends connection with the remote host.
/// </SUMMARY>
public void EndConnection()
{
if (_conn != null && _conn.Connected)
{
_conn.Shutdown(SocketShutdown.Both);
_conn.Close();
}
}
}
class USBSerialPort
{
Socket newsock;
Socket client;
int port;
IPAddress ipaddress;
private AsyncCallback ConnectionReady;
private AsyncCallback AcceptConnection;
private AsyncCallback ReceivedDataReady;
private ConnectionStateUSB currentST;
public bool IsConnected = false;
public USBSerialPort(int _port)
{
port = _port;
//ConnectionReady = new AsyncCallback(ConnectionReady_Handler);
//AcceptConnection = new AsyncCallback(AcceptConnection_Handler);
//ReceivedDataReady = new AsyncCallback(ReceivedDataReady_Handler);
}
public void Start()
{
try
{
ipaddress = Dns.GetHostEntry("ppp_peer").AddressList[0];
newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(ipaddress, port);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
}
catch (Exception ex)
{
throw ex;
this.Stop();
}
finally
{
//net_stream = null;
//tcp_client = null;
}
}
void Connected(IAsyncResult iar)
{
try
{
client = (Socket)iar.AsyncState;
client.EndConnect(iar);
ConnectionStateUSB st = new ConnectionStateUSB();
st._conn = client;
st._buffer = new byte[4];
//Queue the rest of the job to be executed latter
//ThreadPool.QueueUserWorkItem(AcceptConnection, st);
currentST = st;
//conStatus.Text = "Connected to: " + client.RemoteEndPoint.ToString();
if (client.Connected)
client.BeginReceive(st._buffer, 0, 0, SocketFlags.None,
new AsyncCallback(ReceiveData), st);
}
catch (SocketException e)
{
IsConnected = false;
//conStatus.Text = "Error connecting";
}
catch (Exception ex)
{
IsConnected = false;
}
}
void ReceiveData(IAsyncResult iar)
{
try
{
ConnectionStateUSB remote = (ConnectionStateUSB)iar.AsyncState;
remote._conn.EndReceive(iar);
try
{
this.OnReceiveData(remote);
IsConnected = true;
}
catch (Exception ex)
{
IsConnected = false;
}
//int recv = remote.EndReceive(iar);
//string stringData = Encoding.ASCII.GetString(data, 0, recv);
//results.Items.Add(stringData);
if (remote.Connected)
remote._conn.BeginReceive(remote._buffer, 0, 0, SocketFlags.None,
new AsyncCallback(ReceiveData), remote);
}
catch (Exception ex)
{
this.Stop();
}
}
public void SendData(byte[] data)
{
try
{
bool a = currentST.Write(data, 0, data.Length);
}
catch (Exception ex)
{
IsConnected = false;
MessageBox.Show("Error!\n" + ex.Message + "\n" + ex.StackTrace);
}
}
public void SendData(string data)
{
try
{
byte[] msg = Encoding.UTF8.GetBytes(data + Convert.ToChar(Convert.ToByte(3)));
bool a = currentST.Write(msg, 0, msg.Length);
msg = null;
}
catch (Exception ex)
{
IsConnected = false;
MessageBox.Show("Error!\n" + ex.Message + "\n" + ex.StackTrace);
}
}
/// <SUMMARY>
/// Shutsdown the server
/// </SUMMARY>
public void Stop()
{
//lock (this)
//{
if (newsock != null)
newsock.Close();
if (client != null)
client.Close();
if (currentST != null)
{
currentST._conn.Close();
currentST = null;
}
IsConnected = false;
//}
}
/// <SUMMARY>
/// Gets executed when the server accepts a new connection.
/// </SUMMARY>
public delegate void EventOnAcceptConnection(ConnectionStateUSB socket);
public event EventOnAcceptConnection OnAcceptConnection;
/// <SUMMARY>
/// Gets executed when the server detects incoming data.
/// This method is called only if OnAcceptConnection has already finished.
/// </SUMMARY>
public delegate void EventOnReceiveData(ConnectionStateUSB socket);
public event EventOnReceiveData OnReceiveData;
/// <SUMMARY>
/// Gets executed when the server needs to shutdown the connection.
/// </SUMMARY>
public delegate void EventOnDropConnection(ConnectionStateUSB socket);
public event EventOnDropConnection OnDropConnection;
}
Hope this help.