- macOS 10.14+
- Xcode 10.0+
netcat tool (abbreviated
nc on macOS) is a UNIX tool that lets you:
Establish outbound TCP and UDP connections.
Listen for inbound TCP and UDP connections.
Transfer data between a network connection and
This sample code shows how you can build
nwcat, which provides this functionality and also adds TLS and Bonjour support.
nwcat tool supports two modes: one that makes an outbound connection and one that listens for an inbound connection. You select the mode using command-line arguments. To make an outbound connection to port 80 on
example, you run:
To listen for an inbound connection, you supply the
-l option and the port number to listen on:
nwcat uses TCP. The
-u argument switches to UDP, and the
-t argument adds TLS to TCP connections and DTLS to UDP connections. The
-b argument enables use of Bonjour service names rather than host names.
Create an Outbound Connection
In the Network framework, a bidirectional flow of data is represented as a connection object (
nw). If you’re using TCP, there’s a direct mapping between the connection object and the underlying TCP connection. If you’re using UDP, a connection object represents a bidirectional flow of datagrams between a local port and a port on a specific remote peer.
Create a Parameters Object. A parameters object, of type
nw, holds all the parameters necessary to configure a network connection. These include:
The protocols involved, like TCP or UDP, or whether to enable TLS.
Any options for those protocols.
Any constraints on the connection, like whether or not to use the cellular interface.
These functions take two arguments:
The first configures the security protocol for the connection. Pass
NWto use no security.
_PARAMETERS _DISABLE _PROTOCOL
The second configures the transport protocol for the connection. Pass
NWto get a default configuration.
_PARAMETERS _DEFAULT _CONFIGURATION
Create an Endpoint. An endpoint, of type
nw, holds a network host or service name. For an outbound connection, the endpoint determines the remote host to which you want to connect. In most cases this consists of a host name and a port number, but there are other options. For example, you can also create endpoints that target a Bonjour service.
nwcat tool, the user supplies a host name and a port number via command-line arguments, and you will need to create an endpoint from those two strings. You do this by calling
These strings support both symbolic and numeric values:
The host name can be a DNS name, like
example, or the string representation of an IP address, like
"203for IPv4 or
.0 .113 .7"
2800: 220: 1: 248: 1893: 25c8: 1946"
The port can be a numeric value, like
"80", or a service name, like
Create the Connection Object. Once you have your parameters object and endpoint, you can create a connection object by calling
Start the Connection. To start the connection establishment process:
nwto set the
_connection _set _queue
dispatchon which all callbacks will be scheduled. For a simple application like
nwcat, you can use the main queue for your callbacks. A more complex application would typically use a custom serial queue instead.
Install any update handler blocks. The most important of these is the state changed handler, discussed below.
Start the connection by calling
You must set your queue before starting the connection, and you cannot change the queue after that.
A state changed handler is a block that’s called by the connection object whenever the connection state changes. For a simple application, like
nwcat, you can use a very simple state changed handler.
Make sure you handle the
nw state. Once the connection is no longer needed, you have to release the reference you took when you started the connection.
Listen for an Inbound Connection
A listener object, of type
nw, listens for inbound connections and creates a new connection object for each one. To create a listener object you must supply a parameters object (
nw) to indicate what protocols to use and information about the local endpoint on which you want to listen.
Create a Parameters Object. Creating a parameters object for a listener object is very similar to creating a parameters object for an outbound connection. Use the
nw convenience functions to define which protocols you want your listener to use. These parameters will be applied to any inbound connections your listener accepts. For example, if you enable TLS in the parameters object, all inbound connections will negotiate TLS once you call
Set a Local Endpoint. A listener object must know what local endpoint to listen on, that is, the endpoint to which clients must connect. The local endpoint can include a port number and an interface address, both of which are optional. If you don’t specify a port number, the system chooses a port for you. If you don’t specify an interface address, the system listens on all interfaces and addresses.
Most applications don’t need to listen on a specific interface address and thus can create a listener using the
nw convenience function. However,
nwcat allows the user to specify an interface address (via a command line argument) and thus you have to use a slightly more complex technique. If the user has specified an interface address or a port, you can call
nw to create an endpoint representing the address to listen on, and then call
nw to apply that to your parameters object.
nw to create an endpoint for the local address, keep the following in mind:
portparameter can either be a numeric string, like
"443", or a service name, like
If you pass
portparameter the system will choose a port on your behalf.
Create the Listener Object. Once you’ve set up your parameters object, you can create a listener object by calling
Start the Listener. Starting a listener object is much like starting a connection object, with one significant difference: in addition to setting a state changed handler, you must also set a new connection handler, which is called whenever the listener object receives a new inbound connection.
Accept or Reject Inbound Connections. Your new connection handler is responsible for either starting the network connection or rejecting it. The
nwcat command can only handle one connection at a time, so if there’s already a network connection in place, call
nw to reject the new connection. If not, retain the network connection and then run the connection using the same
start function you used in the outbound case.
Once you have created and started a connection, either outbound or inbound, you’ll need code to transfer data on that connection. Each connection has two directions:
Inbound data is received from the network connection and written to
Outbound data is read from
stdinand sent to the network connection.
Both directions are asynchronous. When receiving data from the network, you supply a completion handler that’s called when data is available. Similarly, when writing data to the network, you supply a completion handler that’s called when the data has been accepted for transmission.
When working with asynchronous networking, you need to consider flow control. For example, if you receive data from the network faster than you can write it to
stdout, you’ll waste a lot of memory buffering that data. You’ll have similar problems if you read data from
stdin faster than you can send it over the network. You can solve this problem by using asynchronous routines for reading from
stdin and writing to
stdout. The basic strategy is this:
Start an asynchronous read.
When the read completes, start an asynchronous write.
When the write completes, set up the next asynchronous read, which starts again at step 1.
You use a similar strategy for both inbound and outbound data, but there are some subtle differences, discussed in the sections that follow.
Receive Data. You can receive data with code like this.
The function starts by calling
nw, which is an asynchronous function that receives data from the connection object.
nw takes two parameters that control the minimum and maximum amount of data to be received. The exact amount of data received isn’t relevant here, so pass
When the receive is complete,
nw calls the completion handler that you pass it. The completion handler has four parameters:
dispatchwhich, if not
NULL, contains the data received.
A content context, discussed below.
isparameter that is true if the data received is the last part of a logical unit of data.
receiveparameter, which is not
NULLif an error occurred during the receive process.
A content context, of type
nw, holds extra information about the data received. A typical application that uses only TCP can often ignore this value entirely. However, a
netcat implementation must work equally well with TCP and UDP, and you need the content context to do that.
The completion handler you pass to
nw does the following:
It processes any data that was received by starting an asynchronous write to
When that asynchronous write completes—or immediately if there was no content—it calls
scheduleto continue the receive.
schedule you must handle three cases:
If you just received the end of the data stream, call
exitto terminate the program. This is how the program stops when the remote peer closes the network connection.
If the receive failed with an error, handle that error.
Otherwise, start the next asynchronous receive by calling
To check for the end of the data stream:
Generally, you can use the technique shown by
schedule, that is, check both the
isflag and that the context is marked as final. This technique works correctly for both TCP and UDP, and thus is necessary for a
If you only handle TCP, you can simply test the
Send Data. Your send code should have the same basic structure as your receive code.
There are, however, some subtle differences:
If, as in this case, you support UDP, you must limit the amount of data you read from
stdin. If you read more than 64 Kibibytes (KiB), the resulting read won’t fit in a single UDP datagram. This code use a 8 KiB limit.
You must tell the network connection to send the data as a single message by calling
NWand passing true to the
_CONNECTION _DEFAULT _MESSAGE _CONTEXT
isparameter. This approach is appropriate for both TCP and UDP connections. For TCP connections, message boundaries don’t affect how the protocol sends data, but the boundaries are required for sending UDP datagrams.
Finally, when you receive an end of file from
stdin, you must close the send side of your connection. The code for this is shown below.
This passes a special context,
NW, and passes true to the
_CONNECTION _FINAL _MESSAGE _CONTEXT
is. Together, these actions indicate that no more data will be sent, allowing the network connection to close the sending side of the connection.