Event driven TCP Client
Asked Answered
K

4

7

I want a event-driven, windows, c#, tcp client.

When there are at least 35 bytes in the read buffer, I want a handler to be called that will read those 35 bytes, grab a length value from that "packet", then do a blocking read for that second length of data.

Kikuyu answered 1/8, 2012 at 1:48 Comment(4)
Streams is streams. Any interpretation of structures larger than a byte are in your bailiwick. Your code needs to assemble the message header, parse it, accumulate the remaining message data, buffer any additional bytes for later interpretation, ... . Sadly, the 19th century won the network protocol wars.Enthrone
Wow.... Kernigan and Ritchie made no mention of loosing the Protocol Wars...Kikuyu
Thanks-yous to ceykooo and ikh. I will dig in to your suggestions.Kikuyu
Actually it's the 21st century ;-)Betweenwhiles
A
4

There's a relatively new project that essentially provides this: https://github.com/clariuslabs/reactivesockets

From their page:

Implements a very easy to use sockets API based on IObservable. It allows very simple protocol implementations such as:

var client = new ReactiveClient("127.0.0.1", 1055);

// The parsing of messages is done in a simple Rx query over the receiver observable
// Note this protocol has a fixed header part containing the payload message length
// And the message payload itself. Bytes are consumed from the client.Receiver 
// automatically so its behavior is intuitive.
IObservable<string> messages = from header in client.Receiver.Buffer(4)
                               let length = BitConverter.ToInt32(header.ToArray(), 0)
                               let body = client.Receiver.Take(length)
                               select Encoding.UTF8.GetString(body.ToEnumerable().ToArray());

// Finally, we subscribe on a background thread to process messages when they are available
messages.SubscribeOn(TaskPoolScheduler.Default).Subscribe(message => Console.WriteLine(message));
client.ConnectAsync().Wait();
Antinomy answered 3/5, 2013 at 13:59 Comment(0)
V
2

I don't believe there is any event-based socket class available in the BCL, but if you're just looking for something a bit higher level than a bare Socket, perhaps you should look into TcpClient. It will handle buffering the underlying stream for you, letting you access it through a StreamReader and the like:

TcpClient client;
// ... construct, connect, etc ...
new StreamReader(client.GetStream());

If you were using a line based protocol, you'd only need to use StreamReader.ReadLine(), but StreamReader.Read() should easily suit your purposes as well.

Verso answered 1/8, 2012 at 2:26 Comment(0)
R
1

To get going in the right direction, check out Socket.BeginReceive() and Socket.BeginSend().

Also, here is a handy series of examples from Microsoft for how to use the above functions. That helped me get started with those.

Unfortunately I cannot see an option to invoke the callback unless there are at least 35 bytes in the read buffer; it will get invoked whenever anything is received -- even if it's zero bytes. However, chances are that the counterparty will not be sending you messages byte by byte anyway.

Rafael answered 1/8, 2012 at 2:15 Comment(0)
S
1

The answer is probably too late. I recently had a similar problem and have developed 2 projects for it. The first one provides an event based TcpClient that has been tested for all major operating systems. It delivers all incoming data as an event that can be easily subscribed to. The 2nd project takes care of the processing of the incoming data. There is an analyzer that waits for a StartToken and then analyzes the length of the package. StartTokenWithLengthInfoDataPackageAnalyzer. You can also implement your own analyzer if the existing one is not sufficient.

var dataPackageAnalyzer = new StartTokenWithLengthInfoDataPackageAnalyzer(0x01);
var dataPackageHandler = new DataPackageHandler(dataPackageAnalyzer);
dataPackageHandler.NewDataPackage += NewDataPackage;

void NewDataPackage(DataPackage dataPackage)
{
    //final package
}

void OnDataReceived(byte[] receivedData)
{
    //fragmented data
    dataPackageHandler.AddData(receivedData);
}

using var cancellationTokenSource = new CancellationTokenSource(1000);
using var tcpClient = new TcpClient();
tcpClient.DataReceived += OnDataReceived;
await tcpClient.ConnectAsync("127.0.0.1", 9000, cancellationTokenSource.Token);

//Wait for data
Console.ReadKey();

tcpClient.Disconnect();
tcpClient.DataReceived -= OnDataReceived;
dataPackageHandler.NewDataPackage -= NewDataPackage;
Shulamite answered 14/3, 2022 at 22:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.