
Here is the basic idea. I make no promises as to whether it actually
even compiles, never mind works, and I'm not going to bother putting in
any error checking/catching. But it should be enough to give you the
general idea.
By the way, I was looking at the docs for the NetworkStream.EndRead()
method, and the sample code there is awful (I'm not even convinced it
works). I haven't looked closely at the other stuff, but based on what
I did see I'd say definitely take their sample code with a grain of
salt. The sample code they have for the Socket class is much better;
the async methods aren't exactly the same, but they are similar enough
that the Socket sample code is still a pretty good place to look for
guidance as to general structure of the code.
Now, on to my sample code (which you should also take with a grain of
salt, but which I hope is better than MSDN's :) ):
class ClientState
{
public TcpClient client;
public byte[] buffer;
}
private void Form1_Load(object sender, EventArgs e)
{
Int32 port = 9007;
IPAddress localAddr = IPAddress.Parse(IPAddress);
TcpListener server = 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);
}
private void AcceptCallback(IAsyncResult ar)
{
TcpListener server = (TcpListener)ar.AsyncState;
ClientState state = 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);
// 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);
}
private void ReadCallback(IAsyncResult ar)
{
ClientState state = (ClientState)ar.AsyncState;
int cbRead = state.client.GetStream().EndRead(ar);
if (cbRead == 0)
{
// The client has closed the connection
return;
}
// 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:
string strData = 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);
}