Hi,
I think this works pretty good. I'm satisfied with the receiving parts.
I have this in a while loop without closing the streams or sockets.
Thanks!
I guess the next step is being able to pass short messages (the
clickpoints) from the server to the client.
Am I going to be using client in:
TcpClient client = listener.AcceptTcpClient();
to get a stream to send data from the server?
You only need to establish the connection once. You can send multiple
"pieces" of data across the wire, serially, instead of having to close and
reopen the connection each time.
So the above code can be used to establish a connection with the client
(from the server) and you can keep the client reference until it is no
longer needed.
Since our examples in this thread assumed that you were going to be closing
the client Socket immediately after the data was sent, we could simply call
Bitmap.FromStream(networkStream) or check that stream.Read returns 0,
indicating that the Socket was closed. However, now that the Socket is
going to remain open you'll need another way to find the end of each
"logical" stream (i.e., where one call to Send ends and the next one begins)
The easiest way to do that, IMO, is to pad each Send with 4 bytes of data
that indicates the size of the stream to come. It will be a data header
that can be read by the server each time data is received. Since you're
going to want high performance, I assume, I won't recommend serialization
(although I normally would). Instead, you might want to expand the data
header in each Send to include certain predetermined byte sequences that let
the client/server know what type of data is being sent. Then, instead of a
serialized object graph you could just use the BitConverter class or
BinaryWriter to convert your application's data into small byte arrays that
can be written directly to, or read directly from, the NetworkStream.
When reading, you shouldn't check that Read returns 0. Read will block
until data is present to be read on the Socket and should not return 0 until
the socket is closed. However, you should perform the blocking read on a
different thread if you want to allow other clients to connect to the
server, asynchronously.
There's a lot to think about here. That's why I originally asked why you
wanted to do this. But in light of your answer, it sounds like you're going
to need all of the performance you can get, so any other suggestions I may
have had, aside from using Win32 APIs directly, would now be inappropriate I
think (e.g., to use .NET Remoting instead, for example).
And from the client's side, I will read from the memorystream to get
data that the server sent?
In the same way as I described above, yes.
I've included some sample code after my signature.
--
Dave Sexton
const int port = 9483; // arbitrary
public static void Run()
{
StartServer();
using (TcpClient client = new TcpClient("localhost", port))
{
SendPoint(new Point(453, 391), client);
SendPoint(new Point(103, 13), client);
}
Console.WriteLine("Press [enter] to exit");
Console.ReadLine();
}
private static void SendPoint(Point point, TcpClient client)
{
Console.WriteLine("Client: Sending Point: " + point);
NetworkStream stream = client.GetStream();
// note that you can buffer this operation if you want by
// wrapping stream with a BufferedStream instance
BinaryWriter writer = new BinaryWriter(stream);
// write the header first
// first 4 bytes = size of data = 8 bytes (int X, int Y)
writer.Write(8);
// next byte indicates Point data (I arbitrarily chose 1)
writer.Write((byte) 1);
// write data
writer.Write(point.X);
writer.Write(point.Y);
Console.WriteLine("Client: Point Sent");
}
private static Point AcceptPoint(TcpClient client)
{
NetworkStream stream = client.GetStream();
BinaryReader reader = new BinaryReader(stream);
// read header first
// first 4 bytes = size of data
int size = reader.ReadInt32();
// next byte indicates type of data (Point = 1)
byte dataType = reader.ReadByte();
if (dataType != 1)
throw new InvalidOperationException(
"Server received invalid data type byte mark: " + dataType);
// point data is sent as (int X, int Y)
return new Point(reader.ReadInt32(), reader.ReadInt32());
}
private static void StartServer()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, port);
Thread thread = new Thread(delegate()
// the listener will run in the background
{
listener.Start();
using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Server: Client Connected");
Point point = AcceptPoint(client);
Console.WriteLine("Server: Received Point: {0}", point);
point = AcceptPoint(client);
Console.WriteLine("Server: Received Point: {0}", point);
}
listener.Stop();
Console.WriteLine("Server: Server Stopped");
});
thread.IsBackground = true;
thread.Start();
Console.WriteLine("Server: Server Started");
}