开发者

How to use data receive event in Socket class?

I have wrote a simple client that use TcpClient in dotnet to communicate. In order to wait for data messages from server i use a Read() thread that use blocking Read() call on socket. When i receive something i have to generate various events. These event occur in the worker thread and thus you cannot update a UI from it directly. Invoke() can be use but for end developer its difficult as my SDK would be use by users who may not use UI at all or use Presentation Framework. Presentation framework have different way of handling this. Invoke() on our test app as Microstation Addin take a lot of time at the moment. Microstation is single threaded application and call invoke on its thread is not good as it is always busy doing drawing and other stuff message take too long to process.

I want开发者_Go百科 my events to generate in same thread as UI so user donot have to go through the Dispatcher or Invoke.

Now i want to know how can i be notified by socket when data arrive? Is there a build in callback for that. I like winsock style receive event without use of separate read thread. I also do not want to use window timer to for polling for data.

I found IOControlCode.AsyncIO flag in IOControl() function which help says

Enable notification for when data is waiting to be received. This value is equal to the Winsock 2 FIOASYNC constant.

I could not found any example on how to use it to get notification. If i am right in MFC/Winsock we have to create a window of size(0,0) which was just used for listening for the data receive event or other socket events. But i don't know how to do that in dotnet application.


Ok I got it up and running. What I was really looking to was how to seamlessly post events to an UI thread, in which my connection is created. After going through framework code I came up with following proof of concept. SynchronizationContext can be use to bind my component to the UI thread that created it. Then I can post events to that UI thread directly, without using Invoke.

In the following example I created a ThreadUISafeTimer which uses a seperate thread, just like my socket client that uses one for reading and raising events. In this case, context is used to post the event if not null, otherwise the event is raised using the worker thread.

[DefaultEvent("Tick")]
public class ThreadUISafeTimer : Component
{
    private const int True = 1;
    private const int False = 0;
    private int enabled = False;
    private SynchronizationContext context;

    public event EventHandler Tick = delegate { };

    [DefaultValue(false)]
    public ushort Interval { get; set; }

    public ThreadUISafeTimer() {
        Interval = 100;
        this.Events.AddHandler("Tick", Tick);
        //If this class is created by a UI thread it will always post the Tick event to it.
        //otherwise it would be null and Tick would occur in a seperate thread.
        context = SynchronizationContext.Current;

    }
    protected override bool CanRaiseEvents {
        get {
            return true;
        }
    }
    [DefaultValue(false)]
    public bool Enabled {
        get {
            return enabled == True;
        }
        set {
            int newval = value ? True : False;
            if (enabled != newval) {
                if (newval == False)
                    Thread.VolatileWrite(ref enabled, False);
                else {
                    enabled = True;
                    ThreadPool.QueueUserWorkItem(
                        new WaitCallback(delegate(object o) {
                        try {
                            do {
                                try {
                                    Thread.Sleep(Interval);
                                    if (Thread.VolatileRead(ref enabled) == True) {
                                        var callback = new SendOrPostCallback(delegate(object arg) {
                                            try {
                                                Tick(this, EventArgs.Empty);
                                            }
                                            catch (Exception exp) {
                                                Application.OnThreadException(exp);
                                                return;
                                            }
                                        });
                                        //If context is null raise Tick event from current thread
                                        if (context == null)
                                            callback(null);
                                        else
                                            //otherwise post it to the UI thread that owns this timer.
                                            context.Post(callback, null);
                                    }
                                }
                                catch (ThreadInterruptedException) {
                                }

                            } while (Thread.VolatileRead(ref enabled) == True);
                        }
                        catch (ThreadAbortException) {
                        }
                    }), null);
                }
            }
        }
    }


Take a look at this question which is roughly the same and solved by using the Event Broker pattern.

Sending instructions to a thread which is waiting for TCP?

Basically you would have one object with an event that all your threads subscribe to. It will also have a method that can be called which will invoke the event. It maybe sounds complicated, but its fairly simple.

Example code is here http://msforge.net/blogs/paki/archive/2007/11/20/EventBroker-implementation-in-C_2300_-full-source-code.aspx.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜