How many subscribe are allowed in StackExchange.Redis.ISubscriber.Subscribe - publish-subscribe

ISubscriber.Subscribe terminate abruptly when too many subscription are requested and there are no exception or so to understand what was happening or to check on How many subscription are allowed?
Incase of too many subscrption this program terminate abruptly without any exception?
does StackExchange.Redis.ISubscriber.Subscribe any limitation for subscription?
is there any do's & don'ts
here is my code for pub & sub
public class Subscriber
{
static Timer _timer = null;
static long batchNumber = 0;
static Subscriber subscriber;
public static void Main(string[] args)
{
try
{
_timer = new Timer(async (obj) => await StartIncrement(obj), null, 500, Timeout.Infinite);
subscriber = new Subscriber();
RedisWrapper.Instance.Ip = "127.0.0.1";
RedisWrapper.Instance.Port = 6379;
RedisWrapper.Instance.Init();
subscriber.StartSubscribe();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
private static async Task StartIncrement(object obj)
{
batchNumber++;
subscriber.StartSubscribe();
_timer?.Change(500, Timeout.Infinite);
}
public void StartSubscribe()
{
try
{
Console.WriteLine($"Subscribing to chennal TESTPUBSUB{batchNumber}");
GetSubscriber().Subscribe("TESTPUBSUB" + batchNumber).OnMessage((msg) => OnDataReceive(msg)); ;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
private void OnDataReceive(ChannelMessage msg)
{
var message = msg.Message;
Console.WriteLine(JsonConvert.DeserializeObject<JObject>(message)["batchnumber"]);
GetSubscriber().Unsubscribe("TESTPUBSUB" + JsonConvert.DeserializeObject<JObject>(message)["batchnumber"]);
}
private ISubscriber GetSubscriber()
{
return RedisWrapper.Instance.Connection.GetSubscriber();
}
}
Publisher code
public class Publisher
{
public static void Main(string[] args)
{
Publisher publisher = new Publisher();
RedisWrapper.Instance.Ip = "127.0.0.1";
RedisWrapper.Instance.Port = 6379;
RedisWrapper.Instance.Init();
publisher.StartPubliser();
Console.ReadKey();
}
Timer _timer;
public void StartPubliser()
{
_timer = new Timer(async (obj) => await StartPublish(obj), null, 550, Timeout.Infinite);
}
long batchNumber;
private async Task StartPublish(object obj)
{
batchNumber++;
try
{
GetSubscriber().Publish("TESTPUBSUB" + batchNumber, JsonConvert.SerializeObject(new { batchnumber = batchNumber, data = "Some message")}) );
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine($"Published Batch {batchNumber}");
_timer?.Change(550, Timeout.Infinite);
}
private ISubscriber GetSubscriber()
{
return RedisWrapper.Instance.Connection.GetSubscriber();
}
}

Related

Background Service displays a timeout exception after some time in Android 6

This is a service running in the background, no Activity, began operating normally, but After about four hours, there will be a ConnectTimeoutException.
Connect to xxx.xxx.xxx.xxx time out.
This problem occurs in Android 6, I did not find this issue Android 4. When this problem occurs, I have to restart this phone, after which it connects properly for some time. When this problem occurs, other network applications on the phone runs properly.
public class mService extends Service{
Intent intent;
private Handler objHandlerCheckNetwork = new Handler();
private boolean mReflectFlg = false;
private static final int NOTIFICATION_ID = 101;
private static final Class<?>[] mSetForegroundSignature = new Class[] { boolean.class };
private static final Class<?>[] mStartForegroundSignature = new Class[] { int.class , Notification.class };
private static final Class<?>[] mStopForegroundSignature = new Class[] { boolean.class };
private NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
private Runnable mHttpTestRunnable = new Runnable() {
#Override
public void run() {
if (httpTest()){
Log.e(GlobalData.getClassMethodName(),"true");
}else{
Log.e(GlobalData.getClassMethodName(),"false");
}
}
};
private Runnable mTasksCheckNetwork = new Runnable()
{
public void run()
{
Thread httpTestThread = new Thread(mHttpTestRunnable);;
httpTestThread.start();
objHandlerCheckNetwork.postDelayed(mTasksCheckNetwork, 1000*30);
}
};
#SuppressLint("NewApi")
#Override
public void onCreate() {
super.onCreate();
mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE );
try {
mStartForeground = mService.class.getMethod("startForeground" , mStartForegroundSignature);
mStopForeground = mService.class.getMethod("stopForeground" , mStopForegroundSignature);
} catch (NoSuchMethodException e) {
mStartForeground = mStopForeground = null;
}
try {
mSetForeground = getClass().getMethod( "setForeground", mSetForegroundSignature);
} catch (NoSuchMethodException e) {
throw new IllegalStateException( "OS doesn't have Service.startForeground OR Service.setForeground!");
}
Intent intent = new Intent(this,UploadTableDataService.class );
intent.putExtra( "ficationId", NOTIFICATION_ID);
Notification.Builder builder = new Notification.Builder(this);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
builder.setContentIntent(contentIntent);
builder.setSmallIcon(R.drawable.gps);
builder.setContentTitle( "test" );
builder.setContentText( "test111" );
Notification notification = builder.getNotification();
startForegroundCompat( NOTIFICATION_ID, notification);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
//startService( new Intent( this, WifiService. class));
//startService( new Intent( this, VoiceService. class));
this.intent = intent;
Log.e(GlobalData.getClassMethodName(),"mService start!");
objHandlerCheckNetwork.postDelayed(mTasksCheckNetwork, 1000);
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
try{
objHandlerCheckNetwork.removeCallbacks(mTasksCheckNetwork);
}catch (Exception e) {
Log.d("DEBUG->", "onDestroy error - removeUpdates: ");
}
//stopForegroundCompat( NOTIFICATION_ID);
}
void invokeMethod(Method method, Object[] args) {
try {
method.invoke( this, args);
} catch (InvocationTargetException e) {
// Should not happen.
Log. w("ApiDemos" , "Unable to invoke method" , e);
} catch (IllegalAccessException e) {
// Should not happen.
Log. w("ApiDemos" , "Unable to invoke method" , e);
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat( int id, Notification notification) {
if ( mReflectFlg) {
// If we have the new startForeground API, then use it.
if ( mStartForeground != null) {
mStartForegroundArgs[0] = Integer. valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod( mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean. TRUE;
invokeMethod( mSetForeground, mSetForegroundArgs);
mNM.notify(id, notification);
} else {
if (Build.VERSION. SDK_INT >= 5) {
startForeground(id, notification);
} else {
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean. TRUE;
invokeMethod( mSetForeground, mSetForegroundArgs);
mNM.notify(id, notification);
}
}
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat( int id) {
if ( mReflectFlg) {
// If we have the new stopForeground API, then use it.
if ( mStopForeground != null) {
mStopForegroundArgs[0] = Boolean. TRUE;
invokeMethod( mStopForeground, mStopForegroundArgs);
return;
}
mNM.cancel(id);
mSetForegroundArgs[0] = Boolean. FALSE;
invokeMethod( mSetForeground, mSetForegroundArgs);
} else {
if (Build.VERSION. SDK_INT >= 5) {
stopForeground( true);
} else {
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
mSetForegroundArgs[0] = Boolean. FALSE;
invokeMethod( mSetForeground, mSetForegroundArgs);
}
}
}
public static Boolean httpTest() {
HttpClient client= new DefaultHttpClient();;
try {
StringBuilder sb = new StringBuilder();
HttpParams httpParams = client.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 1000*5);
HttpConnectionParams.setSoTimeout(httpParams, 1000*10);
HttpResponse response = client.execute(new HttpGet("http://www.itnanny.com/default.htm"));
HttpEntity entity = response.getEntity();
if (entity != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"), 8192);
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
reader.close();
}
Log.e(GlobalData.getClassMethodName(),"result:"+sb.toString());
if (sb.toString().indexOf("ok") > -1){
return true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
client.getConnectionManager().shutdown();;
}
return false;
}
}

Service-Unavailable(503) Error in Smack XEP-0198: Stream Management

I am using below class to enable stream management("urn:xmpp:sm:3") in our ejabberd server(we have latest version of ejabberd). But when I send the Enable packet to server it says Service Unavailable(503). But when I use "yaxim" it works perfectly. Please help me to solve this problem. Thanks.
public class XmppStreamHandler {
public static final String URN_SM_3 = "urn:xmpp:sm:3";
private static final int MAX_OUTGOING_QUEUE_SIZE = 20;
private static final int OUTGOING_FILL_RATIO = 4;
private XMPPConnection mConnection;
private boolean isSmAvailable = false;
private boolean isSmEnabled = false;
private boolean isOutgoingSmEnabled = false;
private long previousIncomingStanzaCount = -1;
private String sessionId;
private long incomingStanzaCount = 0;
private long outgoingStanzaCount = 0;
private Queue<Packet> outgoingQueue;
private int maxOutgoingQueueSize = MAX_OUTGOING_QUEUE_SIZE;
private ConnectionListener mConnectionListener;
public XmppStreamHandler(XMPPConnection connection, ConnectionListener connectionListener) {
mConnection = connection;
mConnectionListener = connectionListener;
startListening();
}
/** Perform a quick shutdown of the XMPPConnection if a resume is possible */
public void quickShutdown() {
if (isResumePossible()) {
mConnection.quickShutdown();
// We will not necessarily get any notification from a quickShutdown, so adjust our state here.
closeOnError();
} else {
mConnection.shutdown();
}
}
public void setMaxOutgoingQueueSize(int maxOutgoingQueueSize) {
this.maxOutgoingQueueSize = maxOutgoingQueueSize;
}
public boolean isResumePossible() {
return sessionId != null;
}
public boolean isResumePending() {
return isResumePossible() && !isSmEnabled;
}
public static void addExtensionProviders() {
addSimplePacketExtension("sm", URN_SM_3);
addSimplePacketExtension("r", URN_SM_3);
addSimplePacketExtension("a", URN_SM_3);
addSimplePacketExtension("enabled", URN_SM_3);
addSimplePacketExtension("resumed", URN_SM_3);
addSimplePacketExtension("failed", URN_SM_3);
}
public void notifyInitialLogin() {
if (sessionId == null && isSmAvailable)
sendEnablePacket();
}
private void sendEnablePacket() {
debug("sm send enable " + sessionId);
if (sessionId != null) {
isOutgoingSmEnabled = true;
// TODO binding
StreamHandlingPacket resumePacket = new StreamHandlingPacket("resume", URN_SM_3);
resumePacket.addAttribute("h", String.valueOf(previousIncomingStanzaCount));
resumePacket.addAttribute("previd", sessionId);
mConnection.sendPacket(resumePacket);
} else {
outgoingStanzaCount = 0;
outgoingQueue = new ConcurrentLinkedQueue<Packet>();
isOutgoingSmEnabled = true;
StreamHandlingPacket enablePacket = new StreamHandlingPacket("enable", URN_SM_3);
enablePacket.addAttribute("resume", "true");
mConnection.sendPacket(enablePacket);
}
}
private void closeOnError() {
if (isSmEnabled && sessionId != null) {
previousIncomingStanzaCount = incomingStanzaCount;
}
isSmEnabled = false;
isOutgoingSmEnabled = false;
isSmAvailable = false;
}
private void startListening() {
mConnection.forceAddConnectionListener(new ConnectionListener() {
public void reconnectionSuccessful() {
}
public void reconnectionFailed(Exception e) {
}
public void reconnectingIn(int seconds) {
}
public void connectionClosedOnError(Exception e) {
if (e instanceof XMPPException &&
((XMPPException)e).getStreamError() != null) {
// Non-resumable stream error
close();
} else {
// Resumable
closeOnError();
}
}
public void connectionClosed() {
previousIncomingStanzaCount = -1;
}
});
mConnection.addPacketSendingListener(new PacketListener() {
public void processPacket(Packet packet) {
// Ignore our own request for acks - they are not counted
if (!isStanza(packet)) {
trace("send " + packet.toXML());
return;
}
if (isOutgoingSmEnabled && !outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
outgoingQueue.add(packet);
trace("send " + outgoingStanzaCount + " : " + packet.toXML());
// Don't let the queue grow beyond max size. Request acks and drop old packets
// if acks are not coming.
if (outgoingQueue.size() >= maxOutgoingQueueSize / OUTGOING_FILL_RATIO) {
mConnection.sendPacket(new StreamHandlingPacket("r", URN_SM_3));
}
if (outgoingQueue.size() > maxOutgoingQueueSize) {
// Log.e(XmppConnection.TAG, "not receiving acks? outgoing queue full");
outgoingQueue.remove();
}
} else if (isOutgoingSmEnabled && outgoingQueue.contains(packet)) {
outgoingStanzaCount++;
trace("send DUPLICATE " + outgoingStanzaCount + " : " + packet.toXML());
} else {
trace("send " + packet.toXML());
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
mConnection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
if (isSmEnabled && isStanza(packet)) {
incomingStanzaCount++;
trace("recv " + incomingStanzaCount + " : " + packet.toXML());
} else {
trace("recv " + packet.toXML());
}
if (packet instanceof StreamHandlingPacket) {
StreamHandlingPacket shPacket = (StreamHandlingPacket) packet;
String name = shPacket.getElementName();
if ("sm".equals(name)) {
debug("sm avail");
isSmAvailable = true;
if (sessionId != null)
sendEnablePacket();
} else if ("r".equals(name)) {
StreamHandlingPacket ackPacket = new StreamHandlingPacket("a", URN_SM_3);
ackPacket.addAttribute("h", String.valueOf(incomingStanzaCount));
mConnection.sendPacket(ackPacket);
} else if ("a".equals(name)) {
long ackCount = Long.valueOf(shPacket.getAttribute("h"));
removeOutgoingAcked(ackCount);
trace(outgoingQueue.size() + " in outgoing queue after ack");
} else if ("enabled".equals(name)) {
incomingStanzaCount = 0;
isSmEnabled = true;
mConnection.getRoster().setOfflineOnError(false);
String resume = shPacket.getAttribute("resume");
if ("true".equals(resume) || "1".equals(resume)) {
sessionId = shPacket.getAttribute("id");
}
debug("sm enabled " + sessionId);
} else if ("resumed".equals(name)) {
debug("sm resumed");
incomingStanzaCount = previousIncomingStanzaCount;
long resumeStanzaCount = Long.valueOf(shPacket.getAttribute("h"));
// Removed acked packets
removeOutgoingAcked(resumeStanzaCount);
trace(outgoingQueue.size() + " in outgoing queue after resume");
// Resend any unacked packets
for (Packet resendPacket : outgoingQueue) {
mConnection.sendPacket(resendPacket);
}
// Enable only after resend, so that the interceptor does not
// queue these again or increment outgoingStanzaCount.
isSmEnabled = true;
// Re-notify the listener - we are really ready for packets now
// Before this point, isSuspendPending() was true, and the listener should have
// ignored reconnectionSuccessful() from XMPPConnection.
mConnectionListener.reconnectionSuccessful();
} else if ("failed".equals(name)) {
// Failed, shutdown and the parent will retry
debug("sm failed");
mConnection.getRoster().setOfflineOnError(true);
mConnection.getRoster().setOfflinePresences();
sessionId = null;
mConnection.shutdown();
// isSmEnabled / isOutgoingSmEnabled are already false
}
}
}
}, new PacketFilter() {
public boolean accept(Packet packet) {
return true;
}
});
}
private void removeOutgoingAcked(long ackCount) {
if (ackCount > outgoingStanzaCount) {
// Log.e(XmppConnection.TAG,
// "got ack of " + ackCount + " but only sent " + outgoingStanzaCount);
// Reset the outgoing count here in a feeble attempt to re-sync. All bets
// are off.
outgoingStanzaCount = ackCount;
}
int size = outgoingQueue.size();
while (size > outgoingStanzaCount - ackCount) {
outgoingQueue.remove();
size--;
}
}
private static void addSimplePacketExtension(final String name, final String namespace) {
ProviderManager.getInstance().addExtensionProvider(name, namespace,
new PacketExtensionProvider() {
public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
StreamHandlingPacket packet = new StreamHandlingPacket(name, namespace);
int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
packet.addAttribute(parser.getAttributeName(i),
parser.getAttributeValue(i));
}
return packet;
}
});
}
private void debug(String message) {
System.out.println(message);
}
private void trace(String message) {
System.out.println(message);
}
public static class StreamHandlingPacket extends UnknownPacket {
private String name;
private String namespace;
Map<String, String> attributes;
StreamHandlingPacket(String name, String namespace) {
this.name = name;
this.namespace = namespace;
attributes = Collections.emptyMap();
}
public void addAttribute(String name, String value) {
if (attributes == Collections.EMPTY_MAP)
attributes = new HashMap<String, String>();
attributes.put(name, value);
}
public String getAttribute(String name) {
return attributes.get(name);
}
public String getNamespace() {
return namespace;
}
public String getElementName() {
return name;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName());
// TODO Xmlns??
if (getNamespace() != null) {
buf.append(" xmlns=\"").append(getNamespace()).append("\"");
}
for (String key : attributes.keySet()) {
buf.append(" ").append(key).append("=\"")
.append(StringUtils.escapeForXML(attributes.get(key))).append("\"");
}
buf.append("/>");
return buf.toString();
}
}
/** Returns true if the packet is a Stanza as defined in RFC-6121 - a Message, IQ or Presence packet. */
public static boolean isStanza(Packet packet) {
if (packet instanceof Message)
return true;
if (packet instanceof IQ)
return true;
if (packet instanceof Presence)
return true;
return false;
}
public void queue(Packet packet) {
if (outgoingQueue.size() >= maxOutgoingQueueSize) {
System.out.println("outgoing queue full");
return;
}
outgoingStanzaCount++;
outgoingQueue.add(packet);
}
private void close() {
isSmEnabled = false;
isOutgoingSmEnabled = false;
sessionId = null;
}
}
when I send the Enable packet to server it says Service
Unavailable(503)
Service Unavailable means that the service is unavailable on the server. Does ejabberd support XEP-198? Did you enable it?
You should also consider switching to Smack 4.1.0-alpha, which also runs on Android and comes with Stream Management support. yaxim will soon switch to from it's custom XmppStreamHandler implementation to Smack 4.1.

Asynchronous Client Socket ManualResetEvent holding up execution

I am attempting to utilize MSDN's Asynchronous Client Socket code sample to connect and control some home equipment. As I understand, the sample code's ReceiveCallback method uses an instance of the EventWaitHandle ManualResetEvent and the method receiveDone.WaitOne() to hold processing of the current thread until the thread receives a signal that all of the socket's data has been transmitted from the remote device. After all of the socket's data has been transmitted (the socket's data is empty and bytesRead = 0), the Waithandle is removed and the application continues processing.
Unfortunately, by stepping-through the execution of the code, it appears that after the last time that the client returns data from the remote device, ReceiveCallback never returns to see if the data-queue is empty (i.e. bytesRead = 0), and thus never enters the "else" condition in ReceiveCallback where the state of the ManualResetEvent would have been reset and the application would have continued to process. Thus, since it never enters the "else" condition, ManualResetEvent is never reset and the application freezes.
Although I can remove the "receiveDone.WaitOne()" method from the code - permitting execution without waiting for the ManualResetEvent's notification that all of the data has been received; this returns a data-string from the equipment that is typically incomplete.
Am I using this code sample incorrectly? Has anyone seen this before or had any experience on how to work-around this issue?
7/14/2012 - UPDATE: After further testing of the MSDN's Asynchronous Client Socket Example, it became clear that ReceiveCallback actually re-polls the port and the "bytesRead = 0" condition is satisfied only when the socket is released (i.e. client.Shutdown(SocketShutdown.Both); client.Close(); ). If I understand this correctly, this means that the connection has to be closed to get past the receiveDone.WaitOne() method. If the connection is closed to satisfy the WaitOne() Waithandle, it totally defeats the purpose of the application in that I had been hoping to leave the connection open so that the application could listen for equipment updates, which happen continually.
7/16/2012 - UPDATE: I have written to Microsoft Tech Support who have responded that "We're doing research on this issue. It might take some time before we get back to you." As such, it seems that it doesn't appear that this challenge can be resolved at this time through massaging this code.
Without the availability of the Asynchronous Client Socket example code as a foundation for writing asynchronous communication procedures, may I ask if anyone can please suggest a replacement routine that is more reliable? There are three pieces of equipment, each with it's own ip-address and port number. Thus, it would be ideal if a class could be utilized, where an instance could be created for each device. Additionally, the port must remain open to receive spontaneous updates continually sent by the equipment. Lastly, the updates do not have a end character or defined length signalling that the transmission of the message is complete, thus the routine must continually poll the port for available data. Any advice or suggestions would be greatly appreciated.
7/18/2012 - WORKAROUND: After spending a considerable amount of time attempting to get the MSDN's Asynchronous Client Socket code sample working, it became clear that I would have to look elsewhere to get the device responses continuously recognized by the program. In the hope to save someone else the brain-damage, I have included the work-around that I used which seems to work well to this point. If anyone has any suggestions, please don't hesitate to add to this question!
//
// ORIGINAL CODE ATTEMPT
//
public static Socket LutronClient;
public static String LutronResponse = String.Empty;
private const int LutronPort = 4999;
private const string LutronIP = "192.168.1.71";
private static ManualResetEvent LutronConnectDone = new ManualResetEvent(false);
private static ManualResetEvent LutronSendDone = new ManualResetEvent(false);
private static ManualResetEvent LutronReceiveDone = new ManualResetEvent(false);
private static void StartLutronClient()
{
try
{
var lutronIPAddress = IPAddress.Parse(LutronIP);
var lutronRemoteEP = new IPEndPoint(lutronIPAddress, LutronPort);
LutronClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
LutronClient.BeginConnect(lutronRemoteEP, LutronConnectCallback, LutronClient);
LutronConnectDone.WaitOne();
LutronSend(LutronClient, "sdl,14,100,0,S2\x0d");
LutronSendDone.WaitOne();
LutronReceive(LutronClient);
LutronReceiveDone.WaitOne(new TimeSpan(5000));
MessageBox.Show("Response received from Lutron: " + LutronResponse);
txtBoxLutron.Text = LutronResponse;
LutronClient.Shutdown(SocketShutdown.Both);
LutronClient.Close();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronConnectCallback(IAsyncResult lutronAr)
{
try
{
var lutronClient = (Socket)lutronAr.AsyncState;
lutronClient.EndConnect(lutronAr);
LutronConnectDone.Set();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronReceive(Socket lutronClient)
{
try
{
var lutronState = new LutronStateObject { LutronWorkSocket = lutronClient };
lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
private static void LutronReceiveCallback(IAsyncResult lutronAR)
{
try
{
var lutronState = (LutronStateObject)lutronAR.AsyncState;
var lutronClient = lutronState.LutronWorkSocket;
var bytesRead = lutronClient.EndReceive(lutronAR);
if (bytesRead > 0)
{
lutronState.LutronStringBuilder.AppendLine(Encoding.ASCII.GetString(lutronState.LutronBuffer, 0, bytesRead));
lutronClient.BeginReceive(lutronState.LutronBuffer, 0, LutronStateObject.BufferSize, 0, new AsyncCallback(LutronReceiveCallback), lutronState);
}
else
{
if (lutronState.LutronStringBuilder.Length > 0) { LutronResponse = lutronState.LutronStringBuilder.ToString(); }
LutronReceiveDone.Set();
}
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
public static void LutronSend(Socket client, String data)
{
var byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0, LutronSendCallback, client);
}
private static void LutronSendCallback(IAsyncResult ar)
{
try
{
var client = (Socket)ar.AsyncState;
var bytesSent = client.EndSend(ar);
LutronSendDone.Set();
}
catch (Exception e) { MessageBox.Show(e.ToString()); }
}
public class LutronStateObject
{
public Socket LutronWorkSocket;
public const int BufferSize = 256;
public byte[] LutronBuffer = new byte[BufferSize];
public StringBuilder LutronStringBuilder = new StringBuilder();
}
}
This is the work-around I used:
//
// WORK-AROUND
//
using System;
using System.Windows.Forms;
namespace _GlobalCacheInterface
{
public partial class GlobalCacheDataScreen : Form
{
//Interface objects
private static GC_Interface _lutronInterface;
private const int LutronPort = 4999;
private const string LutronIP = "192.168.1.71";
delegate void ThreadSafeLutronCallback(string text);
private static GC_Interface _elanInterface;
private const int ElanPort = 4998;
private const string ElanIP = "192.168.1.70";
delegate void ThreadSafeElanCallback(string text);
private static GC_Interface _tuneSuiteInterface;
private const int TuneSuitePort = 5000;
private const string TuneSuiteIP = "192.168.1.70";
delegate void ThreadSafeTuneSuiteCallback(string text);
public GlobalCacheDataScreen()
{
InitializeComponent();
_lutronInterface = new GC_Interface(LutronIP, LutronPort);
_elanInterface = new GC_Interface(ElanIP, ElanPort);
_tuneSuiteInterface = new GC_Interface(TuneSuiteIP, TuneSuitePort);
// Create event handlers to notify application of available updated information.
_lutronInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxLutron(_lutronInterface._returnString);
_elanInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxElan(_elanInterface._returnString);
_tuneSuiteInterface.DataAvailable += (s, e) => ThreadSafeTxtBoxTuneSuite(_tuneSuiteInterface._returnString);
_lutronInterface.Connected += (s, e) => UpdateUI();
_elanInterface.Connected += (s, e) => UpdateUI();
_tuneSuiteInterface.Connected += (s, e) => UpdateUI();
UpdateUI();
}
private void UpdateUI()
{
_buttonConnectToLutron.Enabled = !_lutronInterface._isConnected;
_buttonConnectToElan.Enabled = !_elanInterface._isConnected;
_buttonConnectToTuneSuite.Enabled = !_tuneSuiteInterface._isConnected;
_buttonDisconnectFromLutron.Enabled = _lutronInterface._isConnected;
_buttonDisconnectFromElan.Enabled = _elanInterface._isConnected;
_buttonDisconnectFromTuneSuite.Enabled = _tuneSuiteInterface._isConnected;
string connectLutronStatus = _lutronInterface._isConnected ? "Connected" : "Not Connected";
string connectElanStatus = _elanInterface._isConnected ? "Connected" : "Not Connected";
string connectTuneSuiteStatus = _tuneSuiteInterface._isConnected ? "Connected" : "Not Connected";
_textBoxLutronConnectStatus.Text = connectLutronStatus;
_textBoxElanConnectStatus.Text = connectElanStatus;
_textBoxTuneSuiteConnectStatus.Text = connectTuneSuiteStatus;
}
private void ThreadSafeTxtBoxLutron(string message) { if (_lutronRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeLutronCallback(ThreadSafeTxtBoxLutron); _lutronRichTextRxMessage.Invoke(d, new object[] { message }); } else { _lutronRichTextRxMessage.Text = message; } }
private void ThreadSafeTxtBoxElan(string message) { if (_elanRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeElanCallback(ThreadSafeTxtBoxElan); _elanRichTextRxMessage.Invoke(d, new object[] { message }); } else { _elanRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from Lutron Elan", 1000); } } }
private void ThreadSafeTxtBoxTuneSuite(string message) { if (_tuneSuiteRichTextRxMessage.InvokeRequired) { var d = new ThreadSafeTuneSuiteCallback(ThreadSafeTxtBoxTuneSuite); _tuneSuiteRichTextRxMessage.Invoke(d, new object[] { message }); } else { _tuneSuiteRichTextRxMessage.Text = message; if (message.EndsWith("\r")) { MessageBoxEx.Show(message, "Message from TuneSuite", 1000); } } }
private void _buttonConnectToLutron_Click(object sender, EventArgs e) { _lutronInterface.Connect(); }
private void _buttonDisconnectFromLutron_Click(object sender, EventArgs e) { _lutronInterface.Disconnect(); }
private void _buttonConnectToElan_Click(object sender, EventArgs e) { _elanInterface.Connect(); }
private void _buttonDisconnectFromElan_Click(object sender, EventArgs e) { _elanInterface.Disconnect(); }
private void _buttonConnectToTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Connect(); }
private void _buttonDisconnectFromTuneSuite_Click(object sender, EventArgs e) { _tuneSuiteInterface.Disconnect(); }
private void _buttonLutronSendMessage_Click(object sender, EventArgs e) { _lutronInterface.SendCommand(_lutronRichTextTxMessage.Text); }
private void _buttonElanSendMessage_Click(object sender, EventArgs e) { _elanInterface.SendCommand(_elanRichTextTxMessage.Text); }
private void _buttonTuneSuiteSendMessage_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand(_elanRichTextTxMessage.Text); }
private void _buttonLightOn_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,100,0,S2"); }
private void _buttonLightOff_Click(object sender, EventArgs e) { _lutronInterface.SendCommand("sdl,14,0,0,S2"); }
private void _buttonStereoOnOff_Click(object sender, EventArgs e) { _elanInterface.SendCommand("sendir,4:3,1,40000,4,1,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,800"); }
private void _button30_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x30\x00\x30\x21\xB8"); }
private void _button31_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x33\x31\x00\x30\x21\xB8"); }
private void _button26_Click(object sender, EventArgs e) { _tuneSuiteInterface.SendCommand("\xB8\x4D\xB5\x32\x36\x00\x30\x21\xB8"); }
}
}
and the GC_Interface class:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
namespace _GlobalCacheInterface
{
class GC_Interface
{
// Declare an event handler to notify when updates are available.
public event EventHandler<EventArgs> DataAvailable;
public string _returnString = "";
// Declare an event handler to notify status of connection.
public event EventHandler<EventArgs> Connected;
public bool _isConnected;
public AsyncCallback ReceiveCallback;
public Socket Client;
private string _ipAddress;
private int _port;
private bool _waitForEndCharacter;
private byte _endCharacter;
byte[] m_DataBuffer = new byte[10];
IAsyncResult m_Result;
public GC_Interface(string ipAddress, int port) { Init(ipAddress, port, false, 0); }
private void Init(string ipAddress, int port, bool waitForEndCharacter, byte endCharacter)
{
_ipAddress = ipAddress;
_port = port;
_waitForEndCharacter = waitForEndCharacter;
_endCharacter = endCharacter;
_isConnected = false;
}
public bool Connect()
{
try
{
// Create a TCP/IP socket.
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Establish the remote endpoint for the socket.
var address = IPAddress.Parse(_ipAddress);
var remoteEP = new IPEndPoint(address, _port);
// Connect to the remote endpoint.
Client.Connect(remoteEP);
if (Client.Connected)
{
_isConnected = true;
ConnectedEventHandler();
WaitForData();
}
return true;
}
catch (SocketException se) { MessageBox.Show("\n connection failed, is the server running?\n" + se.Message ); return false; }
}
public bool SendCommand(string command)
{
try
{
// Convert the string data to byte data using ASCII encoding.
var byteData = Encoding.Default.GetBytes(command);
// Add a carraige-return to the end.
var newArray = new byte[byteData.Length + 1];
byteData.CopyTo(newArray, 0);
newArray[newArray.Length - 1] = 13;
if (Client == null) { return false; }
Client.Send(newArray);
return true;
}
catch (SocketException se) { MessageBox.Show(se.Message); return false; }
}
public void WaitForData()
{
try
{
if (ReceiveCallback == null) { ReceiveCallback = new AsyncCallback(OnDataReceived); }
var theSocPkt = new SocketPacket { thisSocket = Client };
m_Result = Client.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length, SocketFlags.None, ReceiveCallback, theSocPkt);
}
catch (SocketException se) { MessageBox.Show(se.Message); }
}
public class SocketPacket
{
public System.Net.Sockets.Socket thisSocket;
public byte[] DataBuffer = new byte[1];
}
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
var iRx = theSockId.thisSocket.EndReceive(asyn);
char[] Chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int CharLen = d.GetChars(theSockId.DataBuffer, 0, iRx, Chars, 0);
System.String szData = new System.String(Chars);
_returnString = _returnString + szData.Replace("\0", "");
// When an update is received, raise DataAvailable event
DataAvailableEventHandler();
WaitForData();
}
catch (ObjectDisposedException) { System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n"); }
catch (SocketException se) { MessageBox.Show(se.Message); }
}
public bool Disconnect()
{
try
{
if (Client == null) { return false; }
Client.Close();
Client = null;
_isConnected = false;
return true;
}
catch (Exception) { return false; }
}
protected virtual void DataAvailableEventHandler()
{
var handler = DataAvailable;
if (handler != null) { handler(this, EventArgs.Empty); }
}
protected virtual void ConnectedEventHandler()
{
var handler = Connected;
if (handler != null) { handler(this, EventArgs.Empty); }
}
}
}
I had the same issue, adding an Available check to the code fixed my problem. Below is the revised code.
private static void ReceiveCallback( IAsyncResult ar ) {
try {
StateObject state = (StateObject) ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Check if there is anymore data on the socket
if (client.Available > 0) {
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
}
if (bytesRead == 0 || client.Available == 0) {
if (state.sb.Length > 1) {
response = state.sb.ToString();
}
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Hope that helps.
I'm sidestepping the question here. I try to answer what you need, not what you ask for:
Use synchronous code. It will be easier to understand, you don't need callbacks or events. Also, for low thread counts, it is likely to perform better.
You also avoid bugs that you have in your current code. If an exception occurs your computation never completes. Synchronous code does not have that problem.

Instance-Specific EventHandler to provide data visibility beyond the Class-Level

May I ask for help with the following?
I am attempting to connect and control three pieces of household electronic equipment by computer through a GlobalCache GC-100 and iTach. As you will see in the following code, I created a class ("GlobalCacheAdapter") that can communicate and control the equipment, and created an instance of the class for each piece of equipment. Although each instance seems to work well with communicating and in controlling each piece of equipment, the *feedback returned from the equipment* seems only to be visible at the defining class level's - "ReaderThreadProc" procedure. Further processing of the feedback is required for each piece of equipment and I am uncertain as to how to forward this feedback at the equipment specific instance-level. I suspect that an instance-specific EventHandler will need to be implemented; however I am not aware as to how to implement this type of instance-specific EventHandler in order to complete processing and update the appropriate controls.
Any help wold be greatly appreciated.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// Create three new instances of GlobalCacheAdaptor and connect.
// GC-100 (Elan) 192.168.1.70 4998
// GC-100 (TuneSuite) 192.168.1.70 5000
// GC iTach (Lighting) 192.168.1.71 4999
private GlobalCacheAdaptor elanGlobalCacheAdaptor;
private GlobalCacheAdaptor tuneSuiteGlobalCacheAdaptor;
private GlobalCacheAdaptor lutronGlobalCacheAdaptor;
public Form1()
{
InitializeComponent();
elanGlobalCacheAdaptor = new GlobalCacheAdaptor();
elanGlobalCacheAdaptor.ConnectToDevice(IPAddress.Parse("192.168.1.70"), 4998);
tuneSuiteGlobalCacheAdaptor = new GlobalCacheAdaptor();
tuneSuiteGlobalCacheAdaptor.ConnectToDevice(IPAddress.Parse("192.168.1.70"), 5000);
lutronGlobalCacheAdaptor = new GlobalCacheAdaptor();
lutronGlobalCacheAdaptor.ConnectToDevice(IPAddress.Parse("192.168.1.71"), 4999);
elanTextBox.Text = elanGlobalCacheAdaptor._line;
tuneSuiteTextBox.Text = tuneSuiteGlobalCacheAdaptor._line;
lutronTextBox.Text = lutronGlobalCacheAdaptor._line;
}
private void btnZoneOnOff_Click(object sender, EventArgs e) { elanGlobalCacheAdaptor.SendMessage("sendir,4:3,1,40000,4,1,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,181,21,800" + Environment.NewLine); }
private void btnSourceInput1_Click(object sender, EventArgs e) { elanGlobalCacheAdaptor.SendMessage("sendir,4:3,1,40000,1,1,20,179,20,179,20,179,20,179,20,179,20,179,20,179,20,278,20,179,20,179,20,179,20,780" + Environment.NewLine); }
private void btnSystemOff_Click(object sender, EventArgs e) { elanGlobalCacheAdaptor.SendMessage("sendir,4:3,1,40000,1,1,20,184,20,184,20,184,20,184,20,184,20,286,20,286,20,286,20,184,20,184,20,184,20,820" + Environment.NewLine); }
private void btnLightOff_Click(object sender, EventArgs e) { lutronGlobalCacheAdaptor.SendMessage("sdl,14,0,0,S2\x0d"); }
private void btnLightOn_Click(object sender, EventArgs e) { lutronGlobalCacheAdaptor.SendMessage("sdl,14,100,0,S2\x0d"); }
private void btnChannel31_Click(object sender, EventArgs e) { tuneSuiteGlobalCacheAdaptor.SendMessage("\xB8\x4D\xB5\x33\x31\x00\x30\x21\xB8\x0D"); }
private void btnChannel30_Click(object sender, EventArgs e) { tuneSuiteGlobalCacheAdaptor.SendMessage("\xB8\x4D\xB5\x33\x30\x00\x30\x21\xB8\x0D"); }
}
}
public class GlobalCacheAdaptor
{
public Socket _multicastListener;
public string _preferredDeviceID;
public IPAddress _deviceAddress;
public Socket _deviceSocket;
public StreamWriter _deviceWriter;
public bool _isConnected;
public int _port;
public IPAddress _address;
public string _line;
public GlobalCacheAdaptor() { }
public static readonly GlobalCacheAdaptor Instance = new GlobalCacheAdaptor();
public bool IsListening { get { return _multicastListener != null; } }
public GlobalCacheAdaptor ConnectToDevice(IPAddress address, int port)
{
if (_deviceSocket != null) _deviceSocket.Close();
try
{
_port = port;
_address = address;
_deviceSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_deviceSocket.Connect(new IPEndPoint(address, port)); ;
_deviceAddress = address;
var stream = new NetworkStream(_deviceSocket);
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream) { NewLine = "\r", AutoFlush = true };
_deviceWriter = writer;
writer.WriteLine("getdevices");
var readerThread = new Thread(ReaderThreadProc) { IsBackground = true };
readerThread.Start(reader);
_isConnected = true;
return Instance;
}
catch { DisconnectFromDevice(); MessageBox.Show("ConnectToDevice Error."); throw; }
}
public void SendMessage(string message)
{
try
{
var stream = new NetworkStream(_deviceSocket);
var reader = new StreamReader(stream);
var writer = new StreamWriter(stream) { NewLine = "\r", AutoFlush = true };
_deviceWriter = writer;
writer.WriteLine(message);
var readerThread = new Thread(ReaderThreadProc) { IsBackground = true };
readerThread.Start(reader);
}
catch { MessageBox.Show("SendMessage() Error."); }
}
public void DisconnectFromDevice()
{
if (_deviceSocket != null)
{
try { _deviceSocket.Close(); _isConnected = false; }
catch { MessageBox.Show("DisconnectFromDevice Error."); }
_deviceSocket = null;
}
_deviceWriter = null;
_deviceAddress = null;
}
**private void ReaderThreadProc(object state)**
{
var reader = (StreamReader)state;
try
{
while (true)
{
var line = reader.ReadLine();
if (line == null) break;
_line = _line + line + Environment.NewLine;
}
**// Feedback from each piece of equipment is visible here.
// Need to create EventHandler to notify the TextBoxes to update with _line**
}
catch { MessageBox.Show("ReaderThreadProc Error."); }
}
}
From my understanding of the question, you want to do something like this?
You need to know when a GlobalCacheAdapter updates and which one updated in order to update textboxes on a form. My question to you is this - do you actually need to know which updated?
If you declare in your class an event handler like this:
public class GlobalCacheAdaptor
{
public event EventHandler<EventArgs> Updated;
protected virtual void OnUpdated()
{
var handler = Updated;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
private void Foo()
{
// When an update is received, raise Updated event
OnUpdated();
}
}
Then in your form subscribe to Updated for all three GlobalCacheHandler instances
public Form1()
{
elanGlobalCacheAdaptor.Updated += (s,e) =>
{
elanTextBox.Text = elanGlobalCacheAdaptor._line;
}
tuneSuiteGlobalCacheAdaptor.Updated += (s,e) =>
{
tuneSuiteTextBox.Text = tuneSuiteGlobalCacheAdaptor._line;
}
lutronGlobalCacheAdaptor.Updated += (s,e) =>
{
lutronTextBox.Text = lutronGlobalCacheAdaptor._line;
}
}
You should be able to update the correct text box when the appropriate cache handler raises the Updated event.
Finally you may need to handle cross-thread interactions. if so, see this article on MSDN, particularly the part "Thread-Safe Calls to a Windows Forms Control"

Asynchronous Socket Client/Server throwing exception (TcpClient & TcpListener)

I am trying to write an asynchronous socket Server/Client, but I keep getting the following error:
Unable to read data from the transport connection: A blocking operation was interrupted
by a call to WSACancelBlockingCall.
Inner Exception: System.Net.Sockets.SocketException (0x80004005): A blocking operation
was interrupted by a call to WSACancelBlockingCall
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size,
SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
This exception it thrown in my SockerServer class at the following line:
BytesRead = ClientStream.Read(MessageBuffer, 0, BufferSize);
This only occurs when the Client attempts to disconnect from the server - they appear to disconnect fine and then I get this exception.
Here is the codez:
MessageHandler
There is a single instance of this class in the Client and one for each of the clients in the server. This is used to wrap the NetworkStream for each client so that messages can be written to it.
public class MessageHandler
{
private readonly ASCIIEncoding ByteEncoder = new ASCIIEncoding();
private readonly NetworkStream ClientStream;
public MessageHandler(NetworkStream Stream)
{
this.ClientStream = Stream;
}
public void Send(byte[] Message)
{
ClientStream.Write(Message, 0, Message.Length);
ClientStream.Flush();
}
public void Send(string Message)
{
byte[] MessageBytes = ByteEncoder.GetBytes(Message);
ClientStream.Write(MessageBytes, 0, MessageBytes.Length);
ClientStream.Flush();
}
}
MessageHandlerEventArgs
public class MessageHandlerEventArgs : EventArgs
{
public readonly MessageHandler SocketMessage;
public MessageHandlerEventArgs(MessageHandler Messages)
{
this.SocketMessage = Messages;
}
}
SocketHandler (Base class for SocketServer & SocketClient)
public abstract class SocketHandler
{
protected const int DefaultPort = 3000;
protected const int BufferSize = 4096;
protected readonly Action<byte[]> MessageAction;
public SocketHandler(Action<byte[]> MessageAction)
{
this.MessageAction = MessageAction;
ClientConnected += OnClientConnected;
ClientDisconnected += OnClientDisconnected;
}
protected void HandleConnection(IAsyncResult ar)
{
TcpClient Client = (TcpClient)ar.AsyncState;
ThreadPool.QueueUserWorkItem((x) => HandleConnection(Client));
}
protected void HandleConnection(TcpClient NetworkClient)
{
try
{
using (NetworkStream ClientStream = NetworkClient.GetStream())
{
MessageHandler Messages = new MessageHandler(ClientStream);
DoClientConnected(new MessageHandlerEventArgs(Messages));
byte[] MessageBuffer = new byte[BufferSize];
int BytesRead;
while (true)
{
BytesRead = 0;
BytesRead = ClientStream.Read(MessageBuffer, 0, BufferSize);
if (BytesRead == 0)
{
DoClientDisconnected(new MessageHandlerEventArgs(Messages));
break;
}
else
{
MessageAction.Invoke(MessageBuffer.ToArray());
}
}
NetworkClient.Close();
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketHandler.HandleConnection");
}
}
protected abstract void OnClientConnected(object sender, MessageHandlerEventArgs e);
protected abstract void OnClientDisconnected(object sender, MessageHandlerEventArgs e);
public event EventHandler<MessageHandlerEventArgs> ClientConnected;
protected void DoClientConnected(MessageHandlerEventArgs args)
{
if (ClientConnected != null)
{
ClientConnected(this, args);
}
}
public event EventHandler<MessageHandlerEventArgs> ClientDisconnected;
protected void DoClientDisconnected(MessageHandlerEventArgs args)
{
if (ClientDisconnected != null)
{
OnClientDisconnected(this, args);
}
}
public event EventHandler StatusChanged;
protected void DoStatusChanged()
{
if (StatusChanged != null)
{
StatusChanged(this, EventArgs.Empty);
}
}
}
Socket Server
public class SocketServer : SocketHandler
{
private static List<MessageHandler> CurrentClients = new List<MessageHandler>();
private ServerStatus _Status;
public ServerStatus Status
{
get { return _Status; }
private set
{
_Status = value;
DoStatusChanged();
}
}
private readonly TcpListener NetworkServer;
public SocketServer(Action<byte[]> MessageAction)
: base(MessageAction)
{
NetworkServer = new TcpListener(IPAddress.Any, DefaultPort);
}
public void Start()
{
try
{
if (Status == ServerStatus.Stopped)
{
ThreadPool.QueueUserWorkItem((x) => ListenForClient());
Status = ServerStatus.Started;
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketServer.Start");
}
}
public void Stop()
{
try
{
if (Status != ServerStatus.Stopped)
{
Status = ServerStatus.Stopped;
NetworkServer.Stop();
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketServer.Stop");
}
}
private void ListenForClient()
{
try
{
Status = ServerStatus.WaitingClient;
NetworkServer.Start();
while (Status != ServerStatus.Stopped)
{
if (!NetworkServer.Pending())
{
Thread.Sleep(100);
continue;
}
else
{
TcpClient NetworkClient = NetworkServer.AcceptTcpClient();
ThreadPool.QueueUserWorkItem((x) => HandleConnection(NetworkClient));
Status = ServerStatus.Connected;
}
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketServer.ListenForClient");
}
}
protected override void OnClientConnected(object sender, MessageHandlerEventArgs e)
{
CurrentClients.Add(e.SocketMessage);
}
protected override void OnClientDisconnected(object sender, MessageHandlerEventArgs e)
{
CurrentClients.Remove(e.SocketMessage);
Status = (CurrentClients.Count == 0)
? ServerStatus.WaitingClient
: ServerStatus.Connected;
}
}
SocketClient
public class SocketClient : SocketHandler
{
private ClientStatus _Status;
public ClientStatus Status
{
get { return _Status; }
set
{
_Status = value;
DoStatusChanged();
}
}
private TcpClient NetworkClient;
public MessageHandler Messages;
public SocketClient(Action<byte[]> MessageAction)
: base(MessageAction)
{
NetworkClient = new TcpClient();
}
public void Connect(IPAddress ServerAddress, int ServerPort)
{
try
{
if (Status == ClientStatus.Disconnected)
{
Status = ClientStatus.Connecting;
NetworkClient.Connect(ServerAddress, ServerPort);
ThreadPool.QueueUserWorkItem((x) => HandleConnection(NetworkClient));
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketClient.Connect");
}
}
public void Disconnect()
{
try
{
if (Status != ClientStatus.Disconnected)
{
Status = ClientStatus.Disconnecting;
NetworkClient.Close();
Status = ClientStatus.Disconnected;
}
}
catch (Exception ex)
{
Logger.ExceptionLog.AddEntry(ex, "SocketClient.Disconnect");
}
}
protected override void OnClientConnected(object sender, MessageHandlerEventArgs e)
{
Messages = e.SocketMessage;
Status = ClientStatus.Connected;
}
protected override void OnClientDisconnected(object sender, MessageHandlerEventArgs e)
{
Messages = null;
Status = ClientStatus.Disconnected;
}
}
Do not code like that. Using threads/thread pool is just wrong. The Socket/TcpClient has asynchronous methods which should be used
I've created a framework which you can use. It takes care of all processing for you. all you need to do is to handle the incoming/outgoing data.
http://blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net/