diff --git a/pkg/netpoll/example_test.go b/pkg/netpoll/example_test.go new file mode 100644 index 000000000..3d89f9b04 --- /dev/null +++ b/pkg/netpoll/example_test.go @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025 The Gnet Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package netpoll_test + +import ( + "context" + "fmt" + "net" + "os" + "os/signal" + "time" + + "github.com/panjf2000/gnet/v2/pkg/errors" + "github.com/panjf2000/gnet/v2/pkg/netpoll" +) + +func Example() { + ln, err := net.Listen("tcp", "127.0.0.1:9090") + if err != nil { + panic(fmt.Sprintf("Error listening: %v", err)) + } + + defer ln.Close() + + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) + defer cancel() + + go func() { + c, err := ln.Accept() + if err != nil { + panic(fmt.Sprintf("Error accepting connection: %v", err)) + } + + defer c.Close() + + buf := make([]byte, 64) + + for { + select { + case <-ctx.Done(): + cancel() + fmt.Printf("Signal received: %v\n", ctx.Err()) + return + default: + } + + _, err := c.Read(buf) + if err != nil { + panic(fmt.Sprintf("Error reading data from client: %v", err)) + } + fmt.Printf("Received data from client: %s\n", buf) + + _, err = c.Write([]byte("Hello, client!")) + if err != nil { + panic(fmt.Sprintf("Error writing data to client: %v", err)) + } + fmt.Println("Sent data to client") + + time.Sleep(200 * time.Millisecond) + } + }() + + // Wait for the server to start running. + time.Sleep(500 * time.Millisecond) + + poller, err := netpoll.OpenPoller() + if err != nil { + panic(fmt.Sprintf("Error opening poller: %v", err)) + } + + defer poller.Close() + + addr, err := net.ResolveTCPAddr("tcp", ln.Addr().String()) + if err != nil { + panic(fmt.Sprintf("Error resolving TCP address: %v", err)) + } + c, err := net.DialTCP("tcp", nil, addr) + if err != nil { + panic(fmt.Sprintf("Error dialing TCP address: %v", err)) + } + + f, err := c.File() + if err != nil { + panic(fmt.Sprintf("Error getting file from connection: %v", err)) + } + + closeClient := func() { + c.Close() + f.Close() + } + defer closeClient() + + sendData := true + + pa := netpoll.PollAttachment{ + FD: int(f.Fd()), + Callback: func(fd int, event netpoll.IOEvent, flags netpoll.IOFlags) error { //nolint:revive + if netpoll.IsErrorEvent(event, flags) { + closeClient() + return errors.ErrEngineShutdown + } + + if netpoll.IsReadEvent(event) { + buf := make([]byte, 64) + _, err := c.Read(buf) + if err != nil { + closeClient() + fmt.Println("Error reading data from server:", err) + return errors.ErrEngineShutdown + } + fmt.Printf("Received data from server: %s\n", buf) + sendData = true + // Process the data... + } + + if netpoll.IsWriteEvent(event) && sendData { + sendData = false + // Write data to the connection... + _, err := c.Write([]byte("Hello, server!")) + if err != nil { + closeClient() + fmt.Println("Error writing data to server:", err) + return errors.ErrEngineShutdown + } + fmt.Println("Sent data to server") + } + + return nil + }, + } + + if err := poller.AddReadWrite(&pa, false); err != nil { + panic(fmt.Sprintf("Error adding file descriptor to poller: %v", err)) + } + + err = poller.Polling(func(fd int, event netpoll.IOEvent, flags netpoll.IOFlags) error { + return pa.Callback(fd, event, flags) + }) + + fmt.Printf("Poller exited with error: %v", err) +} diff --git a/pkg/netpoll/netpoll.go b/pkg/netpoll/netpoll.go index d192710d5..332a20c3d 100644 --- a/pkg/netpoll/netpoll.go +++ b/pkg/netpoll/netpoll.go @@ -52,6 +52,7 @@ The OpenPoller function creates a new Poller instance: c.Close() f.Close() } + defer closeClient() The PollAttachment consists of a file descriptor and its callback function. PollAttachment is used to register a file descriptor with the Poller. @@ -59,7 +60,7 @@ The callback function is called when an event occurs on the file descriptor: pa := netpoll.PollAttachment{ FD: int(f.Fd()), - Callback: func(i int, event netpoll.IOEvent, flags netpoll.IOFlags) error { + Callback: func(fd int, event netpoll.IOEvent, flags netpoll.IOFlags) error { if netpoll.IsErrorEvent(event, flags) { closeClient() return errors.ErrEngineShutdown @@ -67,6 +68,7 @@ The callback function is called when an event occurs on the file descriptor: if netpoll.IsReadEvent(event) { buf := make([]byte, 64) + // Read data from the connection. _, err := c.Read(buf) if err != nil { closeClient() @@ -76,7 +78,7 @@ The callback function is called when an event occurs on the file descriptor: } if netpoll.IsWriteEvent(event) { - // Write data to the connection... + // Write data to the connection. _, err := c.Write([]byte("hello")) if err != nil { closeClient() @@ -97,5 +99,11 @@ waiting for I/O events to occur: poller.Polling(func(fd int, event netpoll.IOEvent, flags netpoll.IOFlags) error { return pa.Callback(fd, event, flags) }) + +Or + + poller.Polling() + +if you've enabled the build tag `poll_opt`. */ package netpoll