CavemanTcp 2.0.2

dotnet add package CavemanTcp --version 2.0.2                
NuGet\Install-Package CavemanTcp -Version 2.0.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CavemanTcp" Version="2.0.2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CavemanTcp --version 2.0.2                
#r "nuget: CavemanTcp, 2.0.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install CavemanTcp as a Cake Addin
#addin nuget:?package=CavemanTcp&version=2.0.2

// Install CavemanTcp as a Cake Tool
#tool nuget:?package=CavemanTcp&version=2.0.2                

alt tag


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.


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


Server Example

using CavemanTcp;

// Instantiate
TcpServer server = new TcpServer("", 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

// 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

Client Example

using CavemanTcp; 

// Instantiate
TcpClient client = new TcpClient("", 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

// 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


  • If you specify 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 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


Special thanks to VektorPicker for the free Caveman icon:

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.6.1

    • No dependencies.
  • .NETFramework 4.7.2

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net6.0

    • No dependencies.
  • net7.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on CavemanTcp:

Package Downloads

User-space HTTP and HTTPS server without http.sys written in C#. Please visit the project site README for details on implementation.


Simple, fast, async C# web server for handling REST requests with SSL support, targeted to .NET Core, .NET Standard, and .NET Framework. Watson.Lite has no dependency on http.sys.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on CavemanTcp:

Repository Stars
Watson is the fastest, easiest way to build scalable RESTful web servers and services in C#.
Version Downloads Last updated
2.0.2 2,746 8/25/2023
2.0.1 3,615 7/4/2023
2.0.0 4,098 12/1/2022
1.3.13 8,763 8/16/2022
1.3.12 415 8/11/2022
1.3.11 396 8/11/2022
1.3.10 5,206 5/31/2022
1.3.9 420 5/31/2022 13,564 1/13/2022 1,664 11/12/2021
1.3.8 4,518 7/7/2021
1.3.7 2,145 4/20/2021
1.3.6 351 4/20/2021
1.3.5 376 4/20/2021
1.3.4 2,658 2/15/2021 391 2/14/2021 389 2/14/2021 3,859 1/19/2021
1.3.3 1,628 12/25/2020 1,363 11/18/2020
1.3.2 435 11/17/2020
1.3.1 529 11/14/2020 743 11/10/2020 633 11/9/2020 447 11/9/2020 521 11/8/2020 700 10/10/2020 748 10/10/2020 476 9/21/2020 550 9/20/2020 457 9/17/2020 718 9/8/2020
1.3.0 1,615 9/1/2020 3,076 8/11/2020 4,360 6/30/2020 474 6/30/2020
1.2.1 490 6/30/2020
1.2.0 508 6/29/2020
1.1.1 464 6/29/2020
1.1.0 537 6/29/2020
1.0.3 549 6/27/2020
1.0.2 542 6/27/2020
1.0.1 578 6/26/2020
1.0.0 2,947 5/30/2020

Add permitted, blocked IPs