|
Replace the instances of "MaskedTextBox" class with System.Windows.Forms.MaskedTextBox and remove the lines/errors that show up from having a few extra properties. Works fine with those tweaks. Whole project is obsolete now though. Try http://realfiction.net/go/113[^] for how to do it in 2012.
-Jared
|
|
|
|
|
send info back to server from client
why all the threads
thanks
|
|
|
|
|
Thanks for the code
private void OnMemuStop(object sender, System.EventArgs e)
{
tcpLsn.Stop();
tcpLsn=null;
Thread tcpThd = (Thread)threadHolder[0];
errors tcpThd.Abort();
threadHolder.Remove(0);
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in system.dll
Additional information: A blocking operation was interrupted by a call to WSACancelBlockingCall
private void OnMenuExit(object sender, System.EventArgs e)
{
errors if(fThd.IsAlive)
fThd.Abort();
if (tcpLsn!=null)
tcpLsn.Stop();
An unhandled exception of type 'System.NullReferenceException' occurred in WTcpSrv.exe
Additional information: Object reference not set to an instance of an object.
xp vs 2003
|
|
|
|
|
If the client and server are in different machines. Is there a way by which the server knows the IP address of the client ?
Any help will be great.
Saleem
|
|
|
|
|
High,
i read a message, that you worked on a socket server in C# Forms.
I need to do a socket server which communicates with up to 100 client. they exchange only a few bytes.
What i want is a Form an which i can put the communication in Listviews and store thme into the sqlserver via ado.net. Is there any sample in the www for this or parts of this?
I only find Console.Apps or the sample in codeprojct which may be not save for 24/7 working environment.
Any info ?
Thanks for your help,
christian
|
|
|
|
|
Implement the tcpip server and client as windows services. Then they are around all the time. I will be creating such a model in the near future; if you like I will send you the sources.
Chuck Duncan
cduncan@GuardianAngelCorp.com
|
|
|
|
|
Can you sendme a small sample where I use your server with a VC++ (MFC) client ?
Thanks,
Marcelo Duarte
Uruguay
|
|
|
|
|
Well! I have been searching for a TCP program like this. I found it. I was very happy. I also seen the message war between Ian and Jibin. I appreciate that both of them or trying to do some thing for the CSharper Community. But I feel one has to look into oneself if some one commented on one rather than just fighting back blindly. One should try to see why the other one is commenting up on some thing that one belives is the best.
Any way I learnt some thing from this article. Thanks Jibin.
I also thank Ian for bringing up some important issues out here.
Last but not the lest; Having Self confidence is good but it should not be overconfidence.
Thanks guys.
|
|
|
|
|
High,
i read a message, that you worked on a socket server in C# Forms.
I need to do a socket server which communicates with up to 100 client. they exchange only a few bytes.
What i want is a Form an which i can put the communication in Listviews and store thme into the sqlserver via ado.net. Is there any sample in the www for this or parts of this?
I only find Console.Apps or the sample in codeprojct which may be not save for 24/7 working environment.
Any info ?
Thanks for your help,
christian
|
|
|
|
|
Guys,
How can somebody call his application - "a real-time application" - if he has 'sleep' everywhere (reading from the socket, repainting listview).
It is terrible.
|
|
|
|
|
I don't think using sleep or reading from socket or repainting a listview makes the applikation less of realtime applikation.
Consider a movie playback program, it needs to display 25 frames per second, no more no less. This is easy for a modern computer and leaves plenty of time for sleeping, reading stuff from a socket or repaint a listview. The important thing is that the movie playback is not delayed at any time.
I'm no expert on real-time programming but I don't think that 'real-time' has anything to do with keeping the CPU busy or not updating the GUI, Just that timing of certain things is important.
The program might be terrible anyway
|
|
|
|
|
Even though Ian gave you a bad review. I appreciate the effort, and the fact that you created a couple examples.
Thanks again.
Ian.... if you know how to do it so well, write a better example...
|
|
|
|
|
Just to keep the record straight, Ian Griffiths is a name I recognized right away from O'Reilly books and his association with Chris Sells, another well known MS guru, so I'd take his criticism's with more than just a quick glance.
I'm currently developing a professional TCP/IP application and I *must* do it right the first time or it's my job. I'm using asynchronous Begin*** types of calls (BeginConnect, BeginSend, BeginRead, etc. ) rather than the synchronous calls.
I'm not using one thread per connection, since I need a scalable project. That was one of the first concepts I saw as a limit in my code. I've read Ian's ideas with interest since they may still reveal some weakness in my coding needs as I progress, though luckily I believe I've done a better job with my code.
I must agree with Ian that the application has many faults that should be fixed.
Chris A.R.
|
|
|
|
|
High,
i read a message, that you worked on a socket server in C# Forms.
I need to do a socket server which communicates with up to 100 client. they exchange only a few bytes.
What i want is a Form an which i can put the communication in Listviews and store thme into the sqlserver via ado.net. Is there any sample in the www for this or parts of this?
I only find Console.Apps or the sample in codeprojct which may be not save for 24/7 working environment.
Any info ?
Thanks for your help,
christian
|
|
|
|
|
|
its a dll file in the sources
right click on references in the solution explorer and point to it
it worked for me
good luck
|
|
|
|
|
Hi,
I use Win 2000. I have client server machine. Both app were modified using your code. My server sends a message only once. The problem arises in line:
<br />
int ret = soc.Receive(receive, receive.Length, 0); <br />
If I am not mistaken, the app should pass this line only when a message sent.
And as I said above, I send only one message and it behaves like it got more then one message.
More:in line
<br />
if(tmp.Length >0)<br />
I added a counter in the if loop. It shows that it eneters the if condition 14 times even when the tmp.Length=null.
see that tmp.Length=null but the condition doesn't work and the app goes into the block. Am i missing something?
I posted my code here. If you don't mind to take a look and help me. 10x.
<br />
private void RecieveData() <br />
{ <br />
<br />
int i=0;<br />
string tmp=null; <br />
while (true) <br />
{ <br />
Byte[] receive = new Byte[38] ; <br />
try <br />
{ <br />
<br />
int ret = soc.Receive(receive, receive.Length, 0); <br />
if (ret>0) <br />
{ <br />
tmp = System.Text.Encoding.ASCII.GetString(receive); <br />
if(tmp.Length >0) <br />
{ <br />
textBox1.Text = tmp;<br />
Thread.Sleep(100); <br />
<br />
textBox2.Text=i.ToString();<br />
i++;<br />
<br />
} <br />
} <br />
tmp=null;<br />
} <br />
catch (Exception e) <br />
{ <br />
<br />
MessageBox.Show("d"+e.ToString()); <br />
<br />
} <br />
} <br />
<br />
} <br />
|
|
|
|
|
Forn1.resx can't be restored correctly
海纳百川,有容乃大
壁立千仞,无欲则刚
|
|
|
|
|
The version of listview control on the form is beat2, you can recreate it using new version. I will update it later.
Thanks
|
|
|
|
|
Hi, I have read your code and explications, but I have a problem here: I have only Visual C++ 6. So, can you port this app on this progr. language?
I could really use what you wrote,
Steve
|
|
|
|
|
Hey, this is for c#.
Get Visual Studio .NET
|
|
|
|
|
I hate to be negative, but people use this site to find example code... I don't know where to start with this. It scares me to think that people might look at this and base production code on it. (Or worse, that this might have come from production code.) Here are some things that are wrong:
In the WaitingForClient() method, the connectId code is just...wierd. It's not even guaranteed to be thread-safe, although the only scenario I can think of that will break it off the top of my head requires 10000 simultaneous threads to be running, so it's probably not that bad. (Although it's completely unclear as to why it is using that strange algorithm.)
However, the place where the value of connectId is then used - the ReadSocket method on another thread - is a hostage to fortune - what if the WaitingForClient() method manages to accept a second incoming request before the second one gets as far as reading connectId? (Also, ReadSocket implies connectId is a 'long' - why? Its value is constrained to be 10000 or lower, so having a 64 bit value is just pointless. And copying one long into another like that is not guaranteed to be thread-safe on 32 bit systems.)
The server fires up one thread per connection. This is widely recognised to be an unscaleable solution.
The ReadSocket method updates a ListView directly. This is illegal - you are not supposed to call methods on a Control on any thread other than the one that created it. These calls should be done via the Control.Invoke method or Control.BeginInvoke. (Interestingly LoadThread gets this right when updating the ListView.)
The CloseTheThread mysteriously aborts the thread. This is completely pointless, since the thread was about return out of its starting function anyway, at which point the system would have cleaned it up. Although if the race condition discussed above happens and it gets the wrong value for realId, then it will just abort some other thread... LoadThread also apparently aborts itself instead of just exiting cleanly (although it's hard to be sure - we don't get to see where fThd is set).
CloseTheThread also modifies the socketHolder collection without any synchronisation. This is not thread-safe - if the WaitingForClient code happens to receive a new incoming connection at the same time as another connection shuts down, this will go wrong, possibly corrupting the contents of socketHolder.
LoadThread uses asynchronous invocation - it calls Control.BeginInvoke, and then mysteriously calls JobDone.WaitOne() to wait for this asynchronous work to complete. Not only do you not need to go via any kind of extra object to do this (it was unnecessary to have a JobDone object - the call object returned from BeginInvoke will let you wait for the call to complete - just pass it back into EndInvoke), but as far as I can tell there was no need to use async invocation at all: the only thing this thread does in between starting the call and waiting for it to finish is to sleep for 200ms! If it's important that this loop waits 200ms each time round, then that's fine (although I'm not sure that it is importnat - this has more of a feeling of a hack to it), but that's not reason not to just call Control.Invoke - if you want to do a synchronous call, then use the facility supplied! It's much simpler than async!
The SendDataToAllClient method is fine so long as we ignore the title of the article. If this is supposed to be supporting real-time updates, then this is a particularly bad idea - these Send calls could block. So all it takes is one dead client, and everyone else is stuffed. (Although even fixing that, I would hesitate to use the term "Real Time" to describe anything in this program. I would not trust this kind of code to manage my car engine's ignition timing...)
UpdateListView calls ToString on the symbol every time round the loop where it tries to find a particular entry in the listview.
--
Ian Griffiths
DevelopMentor
|
|
|
|
|
Ian Griffiths wrote:
I hate to be negative, but people use this site to find example code... I don't know where to start with this. It scares me to think that people might look at this and base production code on it. (Or worse, that this might have come from production code.) Here are some things that are wrong:
-- The negative is welcome, but before issue your negative, your
-- negative should be correct, otherwise it is another “terrible”.
In the WaitingForClient() method, the connectId code is just...wierd. It's not even guaranteed to be thread-safe, although the only scenario I can think of that will break it off the top of my head requires 10000 simultaneous threads to be running, so it's probably not that bad. (Although it's completely unclear as to why it is using that strange algorithm.)
-- No. WaitingForClient() is thread safe. It is controlled by the
-- while loop and the tcpLsn.AcceptSocket(). The only one client
-- can be accepted at same time. I use Interlocked.Increment(ref
-- connectId) to increase it, This may don’t need to lock it. The
-- connectId is just a key of the Hashtable. The 10000 is nothing
-- to do with the thread number. connectId defines Max. value of
-- the Hashtable key. I have another variable MaxConnected that
-- control the Max. Connection. Please understand it first!
However, the place where the value of connectId is then used - the ReadSocket method on another thread - is a hostage to fortune - what if the WaitingForClient() method manages to accept a second incoming request before the second one gets as far as reading connectId? (Also, ReadSocket implies connectId is a 'long' - why? Its value is constrained to be 10000 or lower, so having a 64 bit value is just pointless. And copying one long into another like that is not guaranteed to be thread-safe on 32 bit systems.)
-- The connectId is increased by each connection. How long is the
-- server running and how many connections will have during the
-- server is up? The connectId will be a huge number eventually.
-- Even it defined as long, it can’t guarantee it is fine. So I
-- set it to be 10000, or any big number. When the connectId
-- reach 10000, then try to start it from 1 again. You can find a
-- value that is not used by previous close thread.
The server fires up one thread per connection. This is widely recognised to be an unscaleable solution.
-- It depends on how does the server is used and how many client
-- does it have. It has its advantage.
The ReadSocket method updates a ListView directly. This is illegal - you are not supposed to call methods on a Control on any thread other than the one that created it. These calls should be done via the Control.Invoke method or Control.BeginInvoke. (Interestingly LoadThread gets this right when updating the ListView.)
-- You are wrong completely and should learn something from here.
-- This is the best solution to update the control from the
-- background thread! Otherwise you should get the instances of
-- the form and the instance of the control from the background
-- thread. It is not that easy! Please read the example in how to
-- do ... that demonstrates how to create a background thread
-- that uses a MethodInvoker to update a ProgressBar control at
-- regular intervals, which come from Microsoft within the Beta2
-- DVD.
The CloseTheThread mysteriously aborts the thread. This is completely pointless, since the thread was about return out of its starting function anyway, at which point the system would have cleaned it up. Although if the race condition discussed above happens and it gets the wrong value for realId, then it will just abort some other thread... LoadThread also apparently aborts itself instead of just exiting cleanly (although it's hard to be sure - we don't get to see where fThd is set).
-- execute the Abort thread function when you don’t need the
-- thread. It is better than you depend on system to do it for
-- you. If the system does not do it for you, what happen …?
CloseTheThread also modifies the socketHolder collection without any synchronisation. This is not thread-safe - if the WaitingForClient code happens to receive a new incoming connection at the same time as another connection shuts down, this will go wrong, possibly corrupting the contents of socketHolder.
-- You are right at here.
LoadThread uses asynchronous invocation - it calls Control.BeginInvoke, and then mysteriously calls JobDone.WaitOne() to wait for this asynchronous work to complete. Not only do you not need to go via any kind of extra object to do this (it was unnecessary to have a JobDone object - the call object returned from BeginInvoke will let you wait for the call to complete - just pass it back into EndInvoke), but as far as I can tell there was no need to use async invocation at all: the only thing this thread does in between starting the call and waiting for it to finish is to sleep for 200ms! If it's important that this loop waits 200ms each time round, then that's fine (although I'm not sure that it is importnat - this has more of a feeling of a hack to it), but that's not reason not to just call Control.Invoke - if you want to do a synchronous call, then use the facility supplied! It's much simpler than async!
-- you are wrong at here. The BeginInvoke Executes the given
-- delegate on the thread that owns this Control's underlying
-- window handle. The delegate is called asynchronously, but this
-- method returns immediately. it doesn't wait the called
-- function to finished. You have to use JobDone.WaitOne() at
-- here to receive the signal from the end of the called
-- function, then read another data from file.
The SendDataToAllClient method is fine so long as we ignore the title of the article. If this is supposed to be supporting real-time updates, then this is a particularly bad idea - these Send calls could block. So all it takes is one dead client, and everyone else is stuffed. (Although even fixing that, I would hesitate to use the term "Real Time" to describe anything in this program. I would not trust this kind of code to manage my car engine's ignition timing...)
-- I can fix it easily by using try – catch to handle exception,
-- when you got the exception remove all the connected from all
-- the Hashtable.
|
|
|
|
|
I agree that on just one of the points I was wrong, but on all the others I stand by my opinion that this is a bad example. It has several programming errors, some unnecessarily complex idioms, and, crucially, fails to meet the basic requirements for an application; it functions as a toy application but it is fundamentally flawed as production code.
I concede that I failed to take into account that everything is funnelled through accept, so in fact the WaitingForClient() method doesn't *need* to be thread-safe. (However your claim that it is thread-safe remains untrue; it just doesn't need to be thread-safe when it comes to multiple incoming requests, so that particular issue isn't a problem.)
But I stand by my claim that the use of interlocked increment in this function is pointless.
--I use Interlocked.Increment(ref connectId) to increase it
Why? What's wrong with ++connectId; ? (Actually a better way of phrasing that would be: what problems with ++connectId; does Interlocked.Increment solve here? There are actually problems with ++connectId;, but using Interlocked.Increment doesn't solve them in this particular case.) Not only is Interlocked.Increment unnecessary here, it's positively misleading, since it implies that this code is trying to be thread-safe.
If the reason that you're using Interlocked.Increment here is because you are *reading* from the connectId field in another thread, I'm afraid this doesn't cut it. Just because thread A uses Interlocked.Increment, this doesn't alter the fact that if thread B reads the field, that read will be non-atomic: on a 32 bit processor, reading connectId will require 2 bus cycles, and the operation will be non-atomic. If you don't believe me go and read the part of the CLR specs that talk about which memory accesses are atomic and which aren't. The first bus cycle could occur before the Interlocked.Increment, the second could occur after the Interlocked.Increment. The only thing that Interlocked.Increment guarantees is that the read can't occur half way through the increment; it doesn't stop the opposite though - the Increment *can* happen half way through the read. Both the increment AND the read would need to be atomic for your code to be correct. (AFAIK there is no way to do an Interlocked.Read - if you could do an interlocked read on a long, you would be able to avoid this problem.)
If connectId were an int, not a long, it would be different. Reading from an int *is* guaranteed to be atomic. So that would fix the problem your code has wherebye the threads you kick off could actually read a bad value by getting rid of the potential for a read from a long being split by the increment. And since you are restricting the value of connectId to always be less than 10,000 I can see absolutely no point in making it a long. (You are aware that long is a 64 bit type aren't you?) Make it an int, and this gets rid of this particular set of problems.
However, that's not all... You completely fail to address the points in the next section:
>>However, the place where the value of connectId is then used
>>- the ReadSocket method on another thread - is a hostage to
>>fortune - what if the WaitingForClient() method manages to
>>accept a second incoming request before the second one gets
>>as far as reading connectId? (Also, ReadSocket implies
>> connectId is a 'long' - why? Its value is constrained to be
>> 10000 or lower, so having a 64 bit value is just pointless.
>> And copying one long into another like that is not guaranteed
>> to be thread-safe on 32 bit systems.)
-- The connectId is increased by each connection. How long is the
-- server running and how many connections will have during the
-- server is up? The connectId will be a huge number eventually.
-- Even it defined as long, it can’t guarantee it is fine. So I
-- set it to be 10000, or any big number. When the connectId
-- reach 10000, then try to start it from 1 again. You can find a
-- value that is not used by previous close thread.
This doesn't address either of the issues I raised, so I'll state them again:
(1) If the main thread manages to accept an incoming connection, launch a new thread, and then accept ANOTHER incoming connection before the 1st thread that it launched gets as far as reading the connectId, the connectId will already have been incremented a 2nd time. (This is possible despite the serialization that going through accept enforces: there is nothing in your main code that will wait until a newly-launched thread has actually read the value of connectId.) In this case both of the threads will read the same value from connectId. You will now have two threads that think they have the same ID. This is a bug.
(2) Why have you made connectId a long? long is a 64 bit type. You don't need a 64 bit type to hold a number that never goes over 10,000.
>> The server fires up one thread per connection. This is
>> widely recognised to be an unscaleable solution.
-- It depends on how does the server is used and how many client
-- does it have. It has its advantage.
Document this fact then. You don't make it clear that this code is only appropriate for lightweight usage, and that it will fall over under any kind of load. The system thread pool limits the maximum number of threads that it creates to 25 on a single processor system. It does this for a very good reason. Your system is OK for very small numbers of clients, but for 50 clients or more, it's not good. For 100s of clients it will have big performance problems.
The advantage your approach has is simplicity of implementation. The disadvantage is that this is no good for large (or even moderate) numbers of clients. At least mention this in your article, or people will think this is a good technique. I've seen servers that have floundered using this technique for fewer than 50 clients!
>>The ReadSocket method updates a ListView directly. This
>>is illegal - you are not supposed to call methods on a
>>Control on any thread other than the one that created it.
>>These calls should be done via the Control.Invoke method
>>or Control.BeginInvoke. Interestingly LoadThread gets
>>this right when updating the ListView.)
-- You are wrong completely and should learn something from here.
-- This is the best solution to update the control from the
-- background thread!
Well I can back my statement up with authoritative sources: I quote from Microsoft's documentation:
"Windows Forms uses the single-threaded apartment (STA) model because Windows Forms is based on native Win32 windows that are inherently apartment threaded. The STA model implies that a window can be created on any thread, but it cannot switch threads once created, and all function calls to it must occur on its creation thread."
and
"The STA model requires that any methods on a control that need to be called from outside the control's creation thread must be marshaled to (executed on) the control's creation thread. "
I also refer you to Mark Boulter's comment on this subject. Mark Boulter works for Microsoft. In fact he works for the part of Microsoft responsible for producing the Windows Forms framework. When someone did exactly what you suggest (updating a control from a background thread) here is what he said:
"DON'T DO THIS. YOU GOT LUCKY. YOU MUST USE Control.Invoke etc."
The original message is here. For a more full explanation see this message he wrote here, and to quote a small section from it, he says:
"if you are using multiple threads of execution you must be careful to marshal any calls to Win Forms controls back to the creating thread"
So the very thing you recommend (updating controls from background threads) is explicitly not allowed, according to Mark Boulter. Since he is a developer working for the part of Microsoft who produced this technology, I would very much like to know why it is that you claim to know more than him about how to use Windows Forms Controls.
--Otherwise you should get the instances of
-- the form and the instance of the control from the background
-- thread. It is not that easy! Please read the example in how to
-- do ... that demonstrates how to create a background thread
-- that uses a MethodInvoker to update a ProgressBar control at
-- regular intervals, which come from Microsoft within the Beta2
-- DVD.
I'm not sure from this whether you're having trouble with expressing the ideas in English (I'm guessing English is not your first language) or whether you genuinely don't understand the issues.
First of all this is *not* optional. The fact that it is inconvenient doesn't alter the fact that you have to do it. It's not just me who says you have to do it - the documentation, and Mark Boulter say that too.
Secondly, it's unclear what you mean by getting "the instance of the control from the background thread." This is not really the issue - there is only one instance of a given control, it's easy enough to get. The important thing is that you have to marshal calls onto the right thread if you want to use if from a background thread.
I'm well aware of the example in the documentation. It's really not *that* hard, and the whole point is that it's NOT OPTIONAL! If you find it difficult, then I'm very sorry to hear that, but that doesn't excuse you. You still have to do it. You are absolutely required to use Control.Invoke (or BeginInvoke). (You don't actually have to use MethodInvoker by the way - you can use any delegate.) It's the use of the [Begin]Invoke method that's significant.
Your code doesn't use this technique. It should. It's not just me who says it, but the documentation and the guys at Microsoft who design this stuff. So don't go telling me that I'm the one who could learn here.
>>The CloseTheThread mysteriously aborts the thread.
>> This is completely pointless, since the thread was
>> about return out of its starting function anyway,
>> at which point the system would have cleaned it up.
-- execute the Abort thread function when you don’t need the
-- thread. It is better than you depend on system to do it for
-- you. If the system does not do it for you, what happen …?
If the system does not do it for you then there is a bug in the system. So you are writing code that presumes that at some point Microsoft will release a version of the CLR which has a bug wherebye threads don't terminate when they should. Don't you think that this is a little paranoid?
As soon as the main function in a thread exits, the thread will terminate. It's not like it needs to hang around and wait for garbage collection. Aborting the thread will, if anything, slow things down, since forcibly aborting a thread is more expensive than letting it terminate naturally.
Normally you would only abort a thread from another thread. I.e. the purpose of the Abort method is to allow one thread to kill off another one. There is rarely any need for a thread to kill itself off like this. (In your case there is definitely no need, since it is easy for the threads to simply exit.)
>>LoadThread uses asynchronous invocation - it calls
>>Control.BeginInvoke, and then mysteriously calls
>>JobDone.WaitOne() to wait for this asynchronous
>>work to complete [...]
You have completely missed the point I was making here in your answer:
-- you are wrong at here. The BeginInvoke Executes the given
-- delegate on the thread that owns this Control's underlying
-- window handle. The delegate is called asynchronously,
I know. That's what I said. That's what I meant when I said that "LoadThread uses asynchronous invocation - it calls Control.BeginInvoke". I have no idea why you think that your reply contradicts what I said, so I'm not sure what you're disagreeing with.
My point is that having used asynchronous invocation, you then have a load of extremely complex code that recreates the exact same semantics as you would have had if you had used synchronous invocation in the first place. So my question is: why didn't you use synchronous invocation in the first place?
-- but this method returns immediately.
Well...yes. That's exactly what asynchronous invocation is about. If it waited until the method returned then it would be synchronous invocation. (The fact that the method runs on a different thread is orthogonal to this. A call can be synchronous despite crossing threads - that's precisely what Control.Invoke does!)
-- it doesn't wait the called function to finished.
-- You have to use JobDone.WaitOne() at
-- here to receive the signal from the end of the called
-- function,
Exactly! That's precisely my point. (Again, I'm guessing that this is a language barrier problem - you seem to be in violent agreement with me, and yet you don't agree that the code is wrong.) If you wanted to wait for the function to finish, why didn't you use the method that DOES THAT FOR YOU?!!! (Control.Invoke)
Just in case you've misunderstood the documentation, here's what Control.Invoke does: it runs the method on the Control's thread, but it doesn't return until that method has completed. These are exactly the semantics you have recreated here. But the advantage of using Control.Invoke is that it's just one line of code, rather than the convoluted solution you have presented.
>> The SendDataToAllClient method is fine so
>> long as we ignore the title of the article.
>> If this is supposed to be supporting real-time
>> updates, then this is a particularly bad idea -
>> these Send calls could block. So all it takes
>> is one dead client, and everyone else is stuffed.
-- I can fix it easily by using try – catch to handle exception,
-- when you got the exception remove all the connected from all
-- the Hashtable.
That would be better, but it's only a partial solution. It takes a long time for the TCP/IP layer to detect that a connection is dead - far too long for the demanding requirements of real time stock price information. Worse than that, if you have a connection that is flakey but not dead (such as you get on the internet all the time - when you get heavy but not complete packet loss, connections grind to a near-halt but never actually die), then send operations can block for minutes at a time. Worse than that, it's possible for a client process to be locked up but the network to be healthy, in which case the call will block indefinitely, and there will be no errors. Ever.
So even with error handling built in, this code is entirely inadequate for the problem at hand. There is absolutely no sense in which it is real time - it is limited by the slowness of the slowest client, and there is no bound on how slow that might be. The only way to deal with this is to use a multi-threaded send of some kind. I would fire up a watchdog thread which checks to make sure the sending thread hasn't blocked for more than, say, a second, and to spin up a new thread if it has. Or it might be possible to build a satisfactory solution around the system thread pool. A third approach might be to use non-blocking IO, which would probably allow the most efficient solution, but would be the most complex approach.
I remain convinced that this is an example of how not to write such an application. Sorry.
--
Ian Griffiths
DevelopMentor
|
|
|
|
|
Hi Ian,
Ian write ...
However, that's not all... You completely fail to address the points in the next section:
>>However, the place where the value of connectId is then used
>>- the ReadSocket method on another thread - is a hostage to
>>fortune - what if the WaitingForClient() method manages to
>>accept a second incoming request before the second one gets
>>as far as reading connectId? (Also, ReadSocket implies
>> connectId is a 'long' - why? Its value is constrained to be
>> 10000 or lower, so having a 64 bit value is just pointless.
>> And copying one long into another like that is not guaranteed
>> to be thread-safe on 32 bit systems.)
-- Beacause WaitingClient using while-loop to handle the new
-- connection, After accepted new connection
-- and processed, then can accept another new coming connection.
-- The ReadSocket thread only using conectId when it started.
-- After the thread is started, then WaitingClient can start to
-- accept new connection. The satuation what you concern will be
-- not happened.
>>The ReadSocket method updates a ListView directly. This
>>is illegal - you are not supposed to call methods on a
>>Control on any thread other than the one that created it.
>>These calls should be done via the Control.Invoke method
>>or Control.BeginInvoke. Interestingly LoadThread gets
>>this right when updating the ListView.)
-- You are wrong completely and should learn something from here.
-- This is the best solution to update the control from the
-- background thread!
Well I can back my statement up with authoritative sources: I quote from Microsoft's documentation:
So the very thing you recommend (updating controls from background threads) is explicitly not allowed, according to Mark Boulter. Since he is a developer working for the part of Microsoft who produced this technology, I would very much like to know why it is that you claim to know more than him about how to use Windows Forms Controls.
....
-- Please goto How Do I ... Common tasks, Windows Forms: and
-- Manipulate a control from a background thread?
-- The article: Making procedure calls across thread boundaries
-- and sample code that "demonstrates how to create a background
-- thread that uses a MethodInvoker to update a ProgressBar
-- control at regular intervals:"
-- This is not some personal opinion. It microsoft published code
-- with the Beta 2 DVD. That represents Microsoft. It is created
-- by Microsoft expert. You may think it is terrible sample.
-- Publish your idea on it. Let me know what do you think about
-- the sample. I use the same way that the sample did. I use
-- asynchronous invocation to make everything easy. Why do you
-- force me to use synchronous call that you should get the
-- instance of the form and instance of the listview control from
-- background thread that is extral work when you use
-- Control.Invoke.
So even with error handling built in, this code is entirely inadequate for the problem at hand. There is absolutely no sense in which it is real time - it is limited by the slowness of the slowest client, and there is no bound on how slow that might be. The only way to deal with this is to use a multi-threaded send of some kind. I would fire up a watchdog thread which checks to make sure the sending thread hasn't blocked for more than, say, a second, and to spin up a new thread if it has. Or it might be possible to build a satisfactory solution around the system thread pool. A third approach might be to use non-blocking IO, which would probably allow the most efficient solution, but would be the most complex approach.
-- I like this comments. But This program just a small demo, you
-- can say it is a toy. You can't expect it to solve all the
-- problems in the TCP/IP world.
-- Thanks any way. I holp reader can learn some from these.
Jibin Pan
|
|
|
|
|