Git Product home page Git Product logo

cavemantcp's Introduction

alt tag

CavemanTcp

NuGet Version NuGet

CavemanTcp gives you the ultimate control in building TCP-based applications involving clients and servers.

With CavemanTcp, you have full control over reading and writing data. CavemanTcp is designed for those that want explicit control over when data is read or written or want to build a state machine on top of TCP.

Important:

Disconnection Handling

Since CavemanTcp relies on the consuming application to specify when to read or write, there are no background threads continually monitoring the state of the TCP connection (unlike SimpleTcp and WatsonTcp). Thus, you should build your apps on the expectation that an exception may be thrown while in the middle of a read or write.

As of v1.3.0, TCP keepalive support was added for .NET Core and .NET Framework; unfortunately .NET Standard does not offer this support, so it is not present for apps using CavemanTcp targeted to .NET Standard.

New in v2.0.x

  • Breaking changes
  • Clients now referenced by Guid instead of string ipPort
  • ListClients now returns an enumeration of ClientMetadata
  • Send and Read methods using string ipPort are marked obsolete
  • AddClient moved closer to connection acceptance
  • Target net7.0 and net472
  • Better detection of disconnects
  • Disable Nagle's algorithm by default

Examples

Server Example

using CavemanTcp;

// Instantiate
TcpServer server = new TcpServer("127.0.0.1", 8000, false, null, null);
server.Logger = Logger;

// Set callbacks
server.Events.ClientConnected += (s, e) => 
{ 
    Console.WriteLine("Client " + e.Client.ToString() + " connected to server");
};
server.Events.ClientDisconnected += (s, e) => 
{ 
    Console.WriteLine("Client " + e.Client.ToString() + " disconnected from server"); 
}; 

// Start server
server.Start(); 

// Send [Data] to client at [guid] 
Guid guid = Guid.Parse("00001111-2222-3333-4444-555566667777");
WriteResult wr = null;
wr = server.Send(guid, "[Data]");
wr = server.SendWithTimeout([ms], guid, "[Data]");
wr = await server.SendAsync(guid, "[Data]");
wr = await server.SendWithTimeoutAsync([ms], guid, "[Data]");

// Receive [count] bytes of data from client at [guid]
ReadResult rr = null;
rr = server.Read(guid, [count]);
rr = server.ReadWithTimeout([ms], guid, count);
rr = await server.ReadAsync(guid, [count]);
rr = await server.ReadWithTimeoutAsync([ms], guid, [count]);

// List clients
List<ClientMetadata> clients = server.GetClients().ToList();

// Disconnect a client
server.DisconnectClient(guid);

Client Example

using CavemanTcp; 

// Instantiate
TcpClient client = new TcpClient("127.0.0.1", 8000, false, null, null);
client.Logger = Logger;

// Set callbacks
client.Events.ClientConnected += (s, e) => 
{ 
    Console.WriteLine("Connected to server"); 
};

client.Events.ClientDisconnected += (s, e) => 
{ 
    Console.WriteLine("Disconnected from server"); 
};

// Connect to server
client.Connect(10);

// Send data to server
WriteResult wr = null;
wr = client.Send("[Data]");
wr = client.SendWithTimeout([ms], "[Data]");
wr = await client.SendAsync("[Data]");
wr = await client.SendWithTimeoutAsync([ms], "[Data]");

// Read [count] bytes of data from server
ReadResult rr = null;
rr = client.Read([count]);
rr = client.ReadWithTimeout([ms], count);
rr = await client.ReadAsync([count]);
rr = await client.ReadWithTimeoutAsync([ms], [count]);

WriteResult and ReadResult

WriteResult and ReadResult contains a Status property that indicates one of the following:

  • ClientNotFound - only applicable for server read and write operations
  • Success - the operation was successful
  • Timeout - the operation timed out (reserved for future use)
  • Disconnected - the peer disconnected

WriteResult also includes:

  • BytesWritten - the number of bytes written to the socket.

ReadResult also includes:

  • BytesRead - the number of bytes read from the socket.
  • DataStream - a MemoryStream containing the requested data.
  • Data - a byte[] representation of DataStream. Using this property will fully read DataStream to the end.

Local vs External Connections

IMPORTANT

  • If you specify 127.0.0.1 as the listener IP address, it will only be able to accept connections from within the local host.
  • To accept connections from other machines:
    • Use a specific interface IP address, or
    • Use null, *, +, or 0.0.0.0 for the listener IP address (requires admin privileges to listen on any IP address)
  • Make sure you create a permit rule on your firewall to allow inbound connections on that port
  • If you use a port number under 1024, admin privileges will be required

Operations with Timeouts

When using any of the APIs that allow you to specify a timeout (i.e. SendWithTimeout, SendWithTimeoutAsync, ReadWithTimeout, and ReadWithTimeoutAsync), the resultant WriteResult and ReadResult as mentioned above will indicate if the operation timed out.

It is important to understand what a timeout indicates and more important what it doesn't.

  • A timeout on a write operation has nothing to do with whether or not the recipient read the data. Rather it is whether or not CavemanTcp was able to write the data to the underlying NetworkStream or SslStream
  • A timeout on a read operation will occur if CavemanTcp is unable to read the specified number of bytes from the underlying NetworkStream or SslStream in the allotted number of milliseconds
  • Valid values for timeoutMs are -1 or any positive integer. -1 indicates no timeout and is the same as using an API that doesn't specify a timeout
  • Pay close attention to either BytesRead or BytesWritten (if you were reading or writing) in the event of a timeout. The timeout may have occurred mid-operation and therefore it will be important to recover from the failure.
    • For example, server sends client 50,000 bytes
    • On the client, a ReadWithTimeout was initiated with a 10 second timeout, attempting to read 50,000 bytes
    • In that 10 seconds, the client was only able to read 30,000 bytes
    • A ReadResult with Status == ReadResultStatus.Timeout is returned, and the BytesRead property is set to 30,000
    • In this case, there are still 20,000 bytes from the server waiting in the client's underlying NetworkStream or SslStream
    • As such, it is recommended that, upon timeout, you reset the connection (but this is your choice)

TCP Keepalives

As of v1.3.0, support for TCP keepalives has been added to CavemanTcp, primarily to address the issue of a network interface being shut down, the cable unplugged, or the media otherwise becoming unavailable. It is important to note that keepalives are supported in .NET Core and .NET Framework, but NOT .NET Standard. As of this release, .NET Standard provides no facilities for TCP keepalives.

TCP keepalives are enabled by default.

server.Keepalive.EnableTcpKeepAlives = true;
server.Keepalive.TcpKeepAliveInterval = 5;      // seconds to wait before sending subsequent keepalive
server.Keepalive.TcpKeepAliveTime = 5;          // seconds to wait before sending a keepalive
server.Keepalive.TcpKeepAliveRetryCount = 5;    // number of failed keepalive probes before terminating connection

Some important notes about TCP keepalives:

  • This capability is enabled by the underlying framework and operating system, not provided by this library
  • Keepalives only work in .NET Core and .NET Framework
  • Keepalives can be enabled on either client or server, but generally only work on server (being investigated)
  • Keepalive.TcpKeepAliveRetryCount is only applicable to .NET Core; for .NET Framework, this value is forced to 10

Special Thanks

A special thanks to those that have helped improve the library thus far!

@LeaT113 @Kliodna @zzampong @SaintedPsycho @samisil @eatyouroats @CetinOzdil

Help or Feedback

Need help or have feedback? Please file an issue here!

Version History

Please refer to CHANGELOG.md.

Thanks

Special thanks to VektorPicker for the free Caveman icon: http://www.vectorpicker.com/caveman-icon_490587_47.html

cavemantcp's People

Contributors

jchristn avatar samisil avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

cavemantcp's Issues

Memory leak that caused by detached CancellationTokenSource objects

Operating system and version: Windows 10 x64, .NET Framework 4.7.2.
Issue encountered: Memory leak.
Expected behavior: CancellationTokenSource objects are disposed.

Hi. I've been using HttpServerLite and found a memory leak inside related CavemanTcp library during my investigation. There is also a place without proper resources disposal in HttpServerLite, but it's pretty minor (_TokenSource is canceled but not disposed in Dispose method of Webserver.cs). That memory leak is caused by creating CancellationTokenSource and leaving them hanging in memory. Therefore the more requests incoming, the more detached CancellationTokenSource objects are piling in the second Heap generation.

This leaking CancellationTokenSource issue relevant for the next methods inside CavemanTcpServer.cs:

  • AcceptConnections() [my main concern]
  • SendWithTimeoutInternal(...)
  • SendWithTimeoutInternalAsync(...)
  • ReadWithTimeoutInternal(...)
  • ReadWithTimeoutInternalAsync(...)

Also, in synchronous Start() method there is double _TokenSource assignment that lead us to one detached CancellationTokenSource.

As possible solution for CancellationTokenSource disposal in AcceptConnections(), i can suggest the next code that helped me in my tests.

var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_Token, client.Token);
var _ = HandleClientConnection(client, linkedCts.Token)
        .ContinueWith(x => linkedCts.Dispose())
        .ConfigureAwait(false);

In case of other unhandled places we can use for example using or try{}finally{}.
To prove CancellationTokenSource memory leak there is a screenshots from dotMemory profiler with fixes and without + list of alive objects where CancellationTokenSource at the top with extremely high number of retained bytes.

Screenshot 2022-01-13 194350
Screenshot 2022-01-13 194356
Screenshot 2022-01-13 194817

Garbage 0x80 bytes on standby...

Hello. After three days of unsuccessful struggle with the problem, I decided to write to you.
I would be very grateful if you can help me ...

I am using your library to connect to a board I developed based on STM32 controller. I used to start with NET CORE 3.1 and your library worked just like a charm. However, after switching to FRAMEWORK 4.8 in standby mode, the receiving buffer is filled with some periodicity with 0x80 trash bytes.

Screenshot

In the picture I am sending 2 bytes for a request and should receive 20 bytes as a response from the server.
The source code of the two applications is identical and does not differ by a single character.
The same problem is observed in your other library - SimpleTcp.

Can you somehow help me with this problem?
Thank you in advance...

TcpServer - IsClientConnected problem

Hello,

When using TcpServer my client connections were keep closing, after I dig a bit found out that IsClientConnected method returns false even when client is still connected. I don't know why but at

if (!client.Connected) return false;

client.Connected returns false but client is still connected. Even I can send an receive data.

After searching a bit found out that client.Connected is not that reliable. At StackOverflow found out this code and looks like it's working ok.

public static TcpState GetState(this TcpClient tcpClient)
{
    var foo = IPGlobalProperties.GetIPGlobalProperties()
                                .GetActiveTcpConnections()
                                .FirstOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
                                                     && x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint));

    return foo != null ? foo.State : TcpState.Unknown;
}

Thinking Packets Instead of Messages

Hello again friend!

After a few years I have returned again to your wonderful suite of TCP libraries. This time more armed with knowledge than before!

So I have read numerous articles off-and-on about the performance-woes of TCP... and about how UDP comes with its own connectivity/firewall woes.

I found an book article proposing parallel TCP connections where each connection has only one single in-flight message at any given moment. The mechanism increases overall connection reliability and consistent improved latency. It can even be used with websockets, which is slick.
Here it is:
http://ithare.com/almost-zero-additional-latency-udp-over-tcp/

Building upon that, when a person begins to scrutinize all of the metrics for deliverability, the issue of max MTU sizing also comes up. If you send a TCP packet that exceeds an MTU size, it can cause problems. So what you end up wanting in a perfect world (where latency and reliability is prized foremost) is:

  • Application takes responsibility for framing its data into small enough messages that one message can be sent in one packet and is guaranteed not to be auto-fragmented in-flight by a network device.

  • Application can then guarantee when it performs a network send, the data is received all-or-nothing with maximum deliverability, and the application can continue to send other packets on parallel connections if it chooses to do so.

So I went back to looking at off-the-shelf TCP tooling and it looks like Caveman gets the closest to allowing some kind of implementation like this without having to go to bare .NET BCL tooling.

I wonder what are your thoughts on an implementation that accomplishes the objectives above?

Random disconnect mid data transfer from client or server

I'm really out of my wits with this. I'm creating an app to send data between computers, I've already raised an issue here a while back.
In essence, the connection is randomly terminated. Mid Send and Read on client and server respectively. I have made sure I am not calling any disconnect method on either. The WriteResult just returns as Disconnected at a random point during the process. I have considered Keepalives to be the cause and once disabled, it worked once, but I then reenabled and when I disabled again, it no longer worked. I am now at the state, where I send the same data over and over and there is seemingly a random % chance of the client or server disconnecting during the data transfer (unsure as to which one causes it). So I do not believe the Keepalives to be the cause of the problem, but they now are disabled on both.

The problem is on this branch of my app. The problem is centralized to the TransferClient and its SendFile function, which does the actual file data transfer. There, there is result = client.Send(buffer); and this result sometimes equals WriteResultStatus.Disconnected, because the client disconnects.
For reference, this is my server class .

Once again, I'm not calling for any disconnects myself. I have even completely disabled my disconnecting method TerminateConnection and it still happens.

Would appreciate any help, as this is a bit urgent and I really can't find anything else to try. I was sure it must be a bug in my code, but I have tried everything I could think off and it really doesn't seem like it, so maybe the Keepalives are enabled even with Keepalive.EnableTcpKeepAlives = false?

I should add that Keepalives shouldn't even have time to fail, as they were set to the default 5,5,5 for the values before I disabled them, and the error happens within the first 1-2 seconds of the connection, usually less. But I have also tested the Keepalives to detect a ethernet cable disconnect practically immediately, so I'm not sure how they work exactly.
Also from my test it seems that the client disconnects from the server, not the other way around, as the client's Events_ClientDisconnected seems to be called earlier then the server's.

Freeze when calling Disconnect() on Client

I'm having issue disconnecting clients from a server. The server can disconnect them just fine, but when I call Disconnect() on the client, it never returns. Same with the Dispose(), which seems to be basically equivalent.

Edit: I have created a very basic server / client app with CavemanTcp and there the Disconnect on Client works. But I'm still unable to fix my issue in my actual code. From looking at the Dispose method in you code, I'm unable to think of a reason it would not return. Any help would be appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.