Re: TCP Newbie Question
- From: "Tym" <spamtrap@xxxxxxxxx>
- Date: Mon, 20 Oct 2008 11:05:05 +0100
Thanks Peter
I only adopted the GUI approach to try to see what was going on better with
each connection; the original that I gleaned from the web is a console app
and it may be better, as you saym to keep it in there.
I found this:
http://www.eggheadcafe.com/software/aspnet/30874323/tcp-listen-port-with-mult.aspx
with your guidance to someone else, but as it's in C#, and I'm a VBer, i'm
strugling a little, but will look at it in more detail.
I tried comverting it to VB with an online converter, but it throws up an
error in the
server.BeginAcceptTcpClient(AcceptCallback, server)
lines as the AcceptCallback is expecting a parameter. Ditto with
ReadCallback.
Imports System.Net.Sockets
Imports System.Text
Imports System
Imports System.IO
Imports System.Net
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
Dim port As Int32 = 9007
Dim localAddr As IPAddress = IPAddress.Parse("127.0.0.1")
Dim server As TcpListener = New TcpListener(localAddr, port)
server.Start()
' The above is pretty much what you had, cleaned up
' a bit. All you have to do at this point is start
' an accept operation:
server.BeginAcceptTcpClient(AcceptCallback, server) <== PROBLEM HERE
End Sub
Private Sub AcceptCallback(ByVal ar As IAsyncResult)
Dim server As TcpListener = CType(ar.AsyncState, TcpListener)
Dim state As ClientState = New ClientState()
' Once the accept operation completes, this callback will
' be called. In it, you can create a new TcpClient in much
' the same way you did it in the synchronous code you had:
state.client = server.EndAcceptTcpClient(ar)
' We're going to start reading from the client's stream, and
' we need a buffer for that:
state.buffer = New Byte(4096) {}
' Note that the TcpClient and the byte[] are both put into
' this "ClientState" object. We're going to need an easy
' way to get at those values in the callback for the read
' operation.
' Next, start a new accept operation so that we can process
' another client connection:
server.BeginAcceptTcpClient(AcceptCallback, server) <== PROBLEM HERE
' Finally, start a read operation on the client we just
' accepted. Note that you could do this before starting the
' accept operation; the order isn't really important.
state.client.GetStream().BeginRead(state.buffer, 0, state.buffer.Length,
ReadCallback, state) <== PROBLEM HERE
End Sub
Private Sub ReadCallback(ByVal ar As IAsyncResult)
Dim state As ClientState = CType(ar.AsyncState, ClientState)
Dim cbRead As Integer = state.client.GetStream().EndRead(ar)
If cbRead = 0 Then
' The client has closed the connection
Return
End If
' Your data is in state.buffer, and there are cbRead
' bytes to process in the buffer. This number may be
' anywhere from 1 up to the length of the buffer.
' The i/o completes when there is _any_ data to be read,
' not necessarily when the buffer is full.
' So, for example:
Dim strData As String = Encoding.ASCII.GetString(state.buffer, 0, cbRead)
' For ASCII you won't have to worry about partial characters
' but for pretty much any other common encoding you'll have to
' deal with that possibility, as there's no guarantee that an
' entire character will be transmitted in one piece.
' Of course, even with ASCII, you need to watch your string
' terminations. You'll have to either check the read buffer
' directly for a null terminator, or have some other means
' of detecting the actual end of a string. By the time the
' string goes through the decoding process, you'll have lost
' that information.
' As with the accept operation, we need to start a new read
' operation on this client, so that we can process the next
' bit of data that's sent:
state.client.GetStream().BeginRead(state.buffer, 0, state.buffer.Length,
ReadCallback, state) <== PROBLEM HERE
End Sub
'----------------------------------------------------------------
' Converted from C# to VB .NET using CSharpToVBConverter(1.2).
' Developed by: Kamal Patel (http://www.KamalPatel.net)
'----------------------------------------------------------------
End Class
As you say in the article - it may need a pinch of salt but i'd appreciate
guidance on where to sprinkle! :-)
I tried pasting it into a new VC# project and it threw even more problems at
me!
I'm happy to drop this back to a console app given your comments on latency.
Many thanks for your guidance so far, and I'm Googling on the
Stream.BeginRead()
Tym.
"Peter Duniho" <NpOeStPeAdM@xxxxxxxxxxxxxxxx> wrote in message
news:op.ujadmhpd8jd0ej@xxxxxxxxxxxxxxxxxxxxxxx
On Sun, 19 Oct 2008 11:33:28 -0700, Tym <spamtrap@xxxxxxxxx> wrote:
I must admint - half of what you said went straight over my head! So I
hit
google and still none the wiser - yet!
Maybe study some more? :) Try to take on a small bit at a time. Don't
try to process all the information all at once. Look for one specific
statement or concept that doesn't make sense to you, and research that one
thing until it does make sense.
Network programming isn't itself fundamentally complicated, but there are
lots of little details that need to be right, and that's made even more
complicated if (as you've done here) you want to include network
programming with a GUI. Network i/o has a fundamental latency involved in
it, and GUI programming is not at all tolerant of latencies. Getting the
two to work together is non-trivial and you need to understand both very
well in order to make that blend work.
So my first advice would be: learn network i/o and GUI programming
independently of each other, and make sure you feel you've got at least a
good solid foundation in both before you try to do both together.
Note that part of getting a good foundation in network i/o is to learn how
to handle more than one connection at a time. Windows offers a number of
strategies for this, but in .NET the big three are: a new thread for each
connection; using the Socket.Select() method to monitor multiple Socket
instances in a single thread; or using the asynchronous programming model
(e.g. Socket.BeginReceive() or Stream.BeginRead()).
I strongly recommend the last of those three, the asynchronous programming
model. It is basically a multi-threaded solution, just as the first
approach is, so you will still need to get comfortable with the idea of
different code executing in different threads (GUI in one thread, network
i/o in others). But .NET will take care of managing the actual threads
for you, which not only simplifies things a bit, it allows for .NET to do
the network i/o in a more efficient way than if you were explicitly
creating a whole new thread for each connection.
So, part of learning the GUI fundamentals is to make sure you are
comfortable with the idea of using threads in conjunction with the GUI.
You can try to learn the fundamentals as you go along if you like, but I
think you're going to find it more frustrating.
As it happens, I've been trying to work on a sort of tutorial for this
kind of thing in my spare time. Unfortunately, spare time hasn't been
readily available recently and I've gotten side-tracked. I haven't yet
gotten to the part of the tutorial that would more directly address the
issues you're specifically asking about. However, there is at least what
I think is a good introduction that might at least fill in some of the
early gaps here. You might consider reading through it and see if it
helps flesh out some of the basic concepts for you.
The first article, of three so far, is here:
http://msmvps.com/blogs/duniho/archive/2008/08/19/basic-network-programming-in-net.aspx
I do hope to resume work on the series in the near future, but I can't
make any promises.
[...]
Which as you can see sends the tcpClient off to another form each time
the
connection is made for the forn to deal with it.
My form2 routine works fine in taking the connection and getting te data
from it using more or less the same routine as before - just a basuic
question I guess, is this sufficient to handle things the way I want them
handled, with the possibility of being bombarded with connections all on
the
same port?
I assume that in your code, "Form2" is the same as "frmPORTdata"?
Unfortunately, the code you posted is pretty much entirely the wrong
approach. You are mixing your GUI code with your network i/o code, which
is not a good idea. Ideally, the network i/o code should exist in its own
class, but even if you put it in your form classes, the execution of that
code should not be done as part of the execution of GUI-related code (e.g.
Load event handler, constructor, etc.)
Three major issues:
-- You are calling Application.DoEvents() all over the place. This is
_always_ a sign of bad design. All of your GUI code should be designed so
that each method does exactly one small thing and returns quickly. If you
have more complex tasks to perform, those should be moved into a different
thread.
-- In your "Form2" class, you are disposing the form instance from
within the constructor, as well as calling Show() from the constructor.
Calling Show() would be a bad idea in any case -- not only does it take
control over the visibility of the new form out of the hands of the code
that actually created the instance, it exposes an incompletely-constructed
form instance which is a very bad thing -- but you're basically putting
the entire lifetime of the form instance into the constructor. By the
time the caller gets the reference back, the instance is dead.
-- You are creating a new form instance for each client. If you want,
as you have said, to handle 1000 clients simultaneously, this is just not
going to work well. Ignoring for the moment that the code as written
can't handle more than one client at a time anyway, even if you fixed that
issue you'd wind up having 1000 form instances visible all at the same
time. That's just not a practical thing to expect Windows to deal with
gracefully.
So, that's what's specifically wrong with the code.
I hope that I described adequately above what I think would be the _right_
way to do it, at least in broad terms. If you use the asynchronous
programming model, then every network i/o call you make can be done using
a "Begin...()" method, which you can safely call from GUI code. The
completion will either occur immediately, or later on a different thread.
Either way, the GUI won't get blocked and you won't have to call
Application.DoEvents().
And of course, creating a new form instance for each new connection is
just incorrect, at least for a situation where you expect to have 1000
simultaneous connections. You should encapsulate your connection-related
code in a non-form class, assuming you have it separate from your main
form class at all (which I think would be good).
Pete
.
- Follow-Ups:
- Re: TCP Newbie Question
- From: Peter Duniho
- Re: TCP Newbie Question
- References:
- TCP Newbie Question
- From: Tym
- Re: TCP Newbie Question
- From: Peter Duniho
- Re: TCP Newbie Question
- From: Tym
- Re: TCP Newbie Question
- From: Peter Duniho
- TCP Newbie Question
- Prev by Date: Automate MS Handwriting Recognition
- Next by Date: RE: serialization & SerializableAttribute
- Previous by thread: Re: TCP Newbie Question
- Next by thread: Re: TCP Newbie Question
- Index(es):
Relevant Pages
|