Getting Started
This tutorial gets you started with Netpoll through some simple examples, includes how to use Server, Client and nocopy APIs.
1. Use Server
Here is a simple server demo, we will explain how it is constructed next.
1.1 Create Listener
First we need to get a Listener
, it can be net.Listener
or netpoll.Listener
, which is no difference for server
usage. Create a Listener
as shown below:
package main
import "net"
func main() {
listener, err := net.Listen(network, address)
if err != nil {
panic("create net listener failed")
}
...
}
or
package main
import "github.com/cloudwego/netpoll"
func main() {
listener, err := netpoll.CreateListener(network, address)
if err != nil {
panic("create netpoll listener failed")
}
...
}
1.2 New EventLoop
EventLoop
is an event-driven scheduler, a real NIO Server, responsible for connection management, event scheduling,
etc.
params:
OnRequest
andOnConnect
are interface that users should implement by themselves to process business logic. Code Comment describes their behavior in detail.Option
is used to customize the configuration when creatingEventLoop
, and the following example shows its usage. For more details, please refer to options.
The creation process is as follows:
package main
import (
"time"
"github.com/cloudwego/netpoll"
)
var eventLoop netpoll.EventLoop
func main() {
...
eventLoop, _ := netpoll.NewEventLoop(
handle,
netpoll.WithOnPrepare(prepare),
netpoll.WithOnConnect(connect),
netpoll.WithReadTimeout(time.Second),
)
...
}
1.3 Run Server
EventLoop
provides services by binding Listener
, as shown below.
Serve
function will block until an error occurs, such as a panic or the user actively calls Shutdown
.
package main
import (
"github.com/cloudwego/netpoll"
)
var eventLoop netpoll.EventLoop
func main() {
...
// start listen loop ...
eventLoop.Serve(listener)
}
1.4 Shutdown Server
EventLoop
provides the Shutdown
function, which is used to stop the server gracefully. The usage is as follows.
package main
import (
"context"
"time"
"github.com/cloudwego/netpoll"
)
var eventLoop netpoll.EventLoop
func main() {
// stop server ...
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
eventLoop.Shutdown(ctx)
}
2. Use Dialer
Netpoll also has the ability to be used on the Client side. It provides Dialer
, similar to net.Dialer
.
Again, here is a simple client demo, and then we introduce it in detail.
2.1 The Fast Way
Similar to Net, Netpoll provides several public functions for directly dialing a connection. such as:
DialConnection(network, address string, timeout time.Duration) (connection Connection, err error)
DialTCP(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConnection, error)
DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConnection, error)
2.2 Create Dialer
Netpoll also defines the Dialer
interface. The usage is as follows:
(of course, you can usually use the fast way)
package main
import (
"github.com/cloudwego/netpoll"
)
func main() {
// Dial a connection with Dialer.
dialer := netpoll.NewDialer()
conn, err := dialer.DialConnection(network, address, timeout)
if err != nil {
panic("dial netpoll connection failed")
}
...
}
3. Use Nocopy API
Connection
provides Nocopy APIs - Reader
and Writer
, to avoid frequent copying. Let’s introduce their simple
usage.
package main
type Connection interface {
// Recommended nocopy APIs
Reader() Reader
Writer() Writer
... // see code comments for more details
}
3.1 Simple Usage
Nocopy APIs is designed as a two-step operation.
On Reader
, after reading data through Next
, Peek
, ReadString
, etc., you still have to actively call Release
to
release the buffer(Nocopy
reads the original address of the buffer, so you must take the initiative to confirm that
the buffer is no longer used).
Similarly, on Writer
, you first need to allocate a buffer to write data, and then call Flush
to confirm that all
data has been written.
Writer
also provides rich APIs to allocate buffers, such as Malloc
, WriteString
and so on.
The following shows some simple examples of reading and writing data. For more details, please refer to the code comments.
package main
import (
"github.com/cloudwego/netpoll"
)
func main() {
var conn netpoll.Connection
var reader, writer = conn.Reader(), conn.Writer()
// reading
buf, _ := reader.Next(n)
... parse the read data ...
reader.Release()
// writing
var write_data []byte
... make the write data ...
alloc, _ := writer.Malloc(len(write_data))
copy(alloc, write_data) // write data
writer.Flush()
}
3.2 Advanced Usage
If you want to use the connection to send (or receive) multiple sets of data, then you will face the work of packing and unpacking the data.
On net, this kind of work is generally done by copying. An example is as follows:
package main
import (
"net"
)
func main() {
var conn net.Conn
var buf = make([]byte, 8192)
// reading
for {
n, _ := conn.Read(buf)
... unpacking & handling ...
var i int
for i = 0; i <= n-pkgsize; i += pkgsize {
pkg := append([]byte{}, buf[i:i+pkgsize]...)
go func() {
... handling pkg ...
}
}
buf = append(buf[:0], buf[i:n]...)
}
// writing
var write_datas <-chan []byte
... packing write ...
for {
pkg := <-write_datas
conn.Write(pkg)
}
}
But, this is not necessary in Netpoll, nocopy APIs supports operations on the original address of the buffer, and realizes automatic recycling and reuse of resources through reference counting.
Examples are as follows(use function Reader.Slice
and Writer.Append
):
package main
import (
"github.com/cloudwego/netpoll"
)
func main() {
var conn netpoll.Connection
// reading
reader := conn.Reader()
for {
... unpacking & handling ...
pkg, _ := reader.Slice(pkgsize)
go func() {
... handling pkg ...
pkg.Release()
}
}
// writing
var write_datas <-chan netpoll.Writer
... packing write ...
writer := conn.Writer()
for {
select {
case pkg := <-write_datas:
writer.Append(pkg)
default:
if writer.MallocLen() > 0 {
writer.Flush()
}
}
}
}