Showing preview only (593K chars total). Download the full file or copy to clipboard to get everything.
Repository: panjf2000/gnet
Branch: dev
Commit: 91cf015e41af
Files: 126
Total size: 558.5 KB
Directory structure:
gitextract_0awrewzb/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.yaml
│ │ ├── feature-request.yaml
│ │ └── question.yaml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── release-drafter.yml
│ └── workflows/
│ ├── codeql.yml
│ ├── cross-compile-bsd.yml
│ ├── gh-translator.yml
│ ├── pull-request.yml
│ ├── release-drafter.yml
│ ├── stale-bot.yml
│ ├── test.yml
│ ├── test_gc_opt.yml
│ ├── test_poll_opt.yml
│ └── test_poll_opt_gc_opt.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── README_ZH.md
├── acceptor_unix.go
├── acceptor_windows.go
├── client_test.go
├── client_unix.go
├── client_windows.go
├── conn_map.go
├── conn_matrix.go
├── conn_matrix_test.go
├── connection_bsd.go
├── connection_linux.go
├── connection_unix.go
├── connection_windows.go
├── context.go
├── engine_unix.go
├── engine_windows.go
├── eventloop_unix.go
├── eventloop_unix_test.go
├── eventloop_windows.go
├── gnet.go
├── gnet_test.go
├── go.mod
├── go.sum
├── internal/
│ └── gfd/
│ └── gfd.go
├── listener_unix.go
├── listener_windows.go
├── load_balancer.go
├── options.go
├── os_unix_test.go
├── os_windows_test.go
├── pkg/
│ ├── bs/
│ │ └── bs.go
│ ├── buffer/
│ │ ├── elastic/
│ │ │ ├── elastic_buffer_test.go
│ │ │ ├── elastic_ring_buffer.go
│ │ │ └── elastic_ring_list_buffer.go
│ │ ├── linkedlist/
│ │ │ ├── linked_list_buffer.go
│ │ │ └── llbuffer_test.go
│ │ └── ring/
│ │ ├── ring_buffer.go
│ │ └── ring_buffer_test.go
│ ├── errors/
│ │ └── errors.go
│ ├── io/
│ │ ├── io.go
│ │ ├── io_bsd.go
│ │ └── io_linux.go
│ ├── logging/
│ │ └── logger.go
│ ├── math/
│ │ ├── math.go
│ │ └── math_test.go
│ ├── netpoll/
│ │ ├── defs_bsd_32bit.go
│ │ ├── defs_bsd_64bit.go
│ │ ├── defs_linux.go
│ │ ├── defs_linux_386.go
│ │ ├── defs_linux_amd64.go
│ │ ├── defs_linux_arm.go
│ │ ├── defs_linux_arm64.go
│ │ ├── defs_linux_mips64x.go
│ │ ├── defs_linux_mipsx.go
│ │ ├── defs_linux_ppc64.go
│ │ ├── defs_linux_ppc64le.go
│ │ ├── defs_linux_riscv64.go
│ │ ├── defs_linux_s390x.go
│ │ ├── defs_poller.go
│ │ ├── defs_poller_bsd.go
│ │ ├── defs_poller_epoll.go
│ │ ├── defs_poller_kqueue.go
│ │ ├── defs_poller_netbsd.go
│ │ ├── example_test.go
│ │ ├── netpoll.go
│ │ ├── poller_epoll_default.go
│ │ ├── poller_epoll_ultimate.go
│ │ ├── poller_kqueue_default.go
│ │ ├── poller_kqueue_ultimate.go
│ │ ├── poller_kqueue_wakeup.go
│ │ ├── poller_kqueue_wakeup1.go
│ │ ├── poller_unix_ultimate.go
│ │ ├── syscall_epoll_generic_linux.go
│ │ ├── syscall_epoll_linux.go
│ │ ├── syscall_epoll_riscv64_arm64_linux.go
│ │ └── syscall_errors_linux.go
│ ├── pool/
│ │ ├── bytebuffer/
│ │ │ └── bytebuffer.go
│ │ ├── byteslice/
│ │ │ ├── byteslice.go
│ │ │ └── byteslice_test.go
│ │ ├── goroutine/
│ │ │ └── goroutine.go
│ │ └── ringbuffer/
│ │ └── ringbuffer.go
│ ├── queue/
│ │ ├── lock_free_queue.go
│ │ ├── queue.go
│ │ └── queue_test.go
│ └── socket/
│ ├── fd_unix.go
│ ├── sock_bsd.go
│ ├── sock_cloexec.go
│ ├── sock_linux.go
│ ├── sock_posix.go
│ ├── sockaddr.go
│ ├── socket.go
│ ├── sockopts_bsd.go
│ ├── sockopts_darwin.go
│ ├── sockopts_freebsd.go
│ ├── sockopts_linux.go
│ ├── sockopts_openbsd.go
│ ├── sockopts_posix.go
│ ├── sockopts_unix.go
│ ├── sockopts_unix1.go
│ ├── sys_cloexec.go
│ ├── tcp_socket.go
│ ├── udp_socket.go
│ └── unix_socket.go
├── reactor_default.go
└── reactor_ultimate.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [panjf2000]
patreon: panjf2000
open_collective: panjf2000
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yaml
================================================
name: Bug Report
description: Oops!..., it's a bug.
title: "[Bug]: "
labels: ["bug"]
assignees:
- panjf2000
body:
- type: markdown
id: tips
attributes:
value: |
## Before you go any further
- Please read [<u>*How To Ask Questions The Smart Way*</u>](http://www.catb.org/~esr/faqs/smart-questions.html) ( Chinese translation: [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)) before you file an issue formally.
- Keep in mind that there is always welcome to ask questions on [Discord](https://discord.gg/UyKD7NZcfH).
- First of all, visit our [website](https://gnet.host) and [Github Org](https://github.com/gnet-io) for docs and examples.
- type: checkboxes
id: checklist
attributes:
label: Actions I've taken before I'm here
description: Make sure you have tried the following ways but still the problem has not been solved.
options:
- label: I've thoroughly read the documentations on this issue but still have no clue.
required: true
- label: I've searched the Github Issues but didn't find any duplicate issues that have been resolved.
required: true
- label: I've searched the internet for this issue but didn't find anything helpful.
required: true
validations:
required: true
- type: textarea
id: bug-report
attributes:
label: What happened?
description: Describe (and illustrate) the bug that you encountered precisely.
placeholder: Please describe what happened and how it happened, the more details you provide, the faster the bug gets fixed.
validations:
required: true
- type: dropdown
id: major-version
attributes:
label: Major version of gnet
description: What major version of gnet are you running?
options:
- v2
- v1
validations:
required: true
- type: input
id: specific-version
attributes:
label: Specific version of gnet
description: What's the specific version of gnet?
placeholder: "For example: v2.1.2"
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
multiple: true
options:
- Linux
- macOS
- Windows
- BSD
validations:
required: true
- type: input
id: os-version
attributes:
label: OS version
description: What's the specific version of OS?
placeholder: "Run `uname -srm` command to get the info, for example: Darwin 21.5.0 arm64, Linux 5.4.0-137-generic x86_64"
validations:
required: true
- type: input
id: go-version
attributes:
label: Go version
description: What's the specific version of Go?
placeholder: "Run `go version` command to get the info, for example: go1.20.5 linux/amd64"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output.
render: shell
validations:
required: true
- type: textarea
id: code
attributes:
label: Reproducer
description: Please provide the minimal code to reproduce the bug.
render: go
- type: textarea
id: how-to-reproduce
attributes:
label: How to Reproduce
description: Steps to reproduce the result.
placeholder: Tell us step by step how we can replicate this bug and what we should see in the end.
value: |
Steps to reproduce the behavior:
1. Go to '....'
2. Click on '....'
3. Do '....'
4. See '....'
validations:
required: true
- type: dropdown
id: bug-in-latest-code
attributes:
label: Does this issue reproduce with the latest release?
description: Is this bug still present in the latest version?
options:
- It can reproduce with the latest release
- It gets fixed in the latest release
- I haven't verified it with the latest release
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yaml
================================================
name: Feature Request
description: Propose an idea to make gnet even better.
title: "[Feature]: "
labels: ["proposal", "enhancement"]
assignees:
- panjf2000
body:
- type: markdown
id: tips
attributes:
value: |
## Before you go any further
- Please read [<u>*How To Ask Questions The Smart Way*</u>](http://www.catb.org/~esr/faqs/smart-questions.html) ( Chinese translation: [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)) before you file an issue formally.
- Keep in mind that there is always welcome to ask questions on [Discord](https://discord.gg/UyKD7NZcfH).
- First of all, visit our [website](https://gnet.host) and [Github Org](https://github.com/gnet-io) for docs and examples.
- type: textarea
id: feature-request
attributes:
label: Description of new feature
description: Make a concise but clear description about this new feature.
placeholder: Describe this new feature with critical details.
validations:
required: true
- type: textarea
id: feature-scenario
attributes:
label: Scenarios for new feature
description: Explain why you need this feature and what scenarios can benefit from it.
placeholder: Please try to convince us that this new feature makes sense, also it will improve gnet.
validations:
required: true
- type: dropdown
id: breaking-changes
attributes:
label: Breaking changes or not?
description: Is this new feature going to break the backward compatibility of the existing APIs?
options:
- "Yes"
- "No"
validations:
required: true
- type: textarea
id: code
attributes:
label: Code snippets (optional)
description: Illustrate this new feature with source code, what it looks like in code.
render: go
- type: textarea
id: feature-alternative
attributes:
label: Alternatives for new feature
description: Alternatives in your mind in case that the feature can't be done for some reasons.
placeholder: A concise but clear description of any alternative solutions or features you had in mind.
value: None.
- type: textarea
id: additional-context
attributes:
label: Additional context (optional)
description: Any additional context about this new feature we should know.
placeholder: What else should we know before we start discussing this new feature?
value: None.
================================================
FILE: .github/ISSUE_TEMPLATE/question.yaml
================================================
name: Question
description: Ask questions about gnet.
title: "[Question]: "
labels: ["question", "help wanted"]
body:
- type: markdown
id: tips
attributes:
value: |
## Before you go any further
- Please read [<u>*How To Ask Questions The Smart Way*</u>](http://www.catb.org/~esr/faqs/smart-questions.html) ( Chinese translation: [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)) before you file an issue formally.
- Keep in mind that there is always welcome to ask questions on [Discord](https://discord.gg/UyKD7NZcfH).
- First of all, visit our [website](https://gnet.host) and [Github Org](https://github.com/gnet-io) for docs and examples.
- Make sure what you're looking for here is an issue rather than a [discussion](https://github.com/panjf2000/gnet/discussions/new/choose).
- type: checkboxes
id: checklist
attributes:
label: Actions I've taken before I'm here
description: Make sure you have tried the following ways to get the answer of your problem.
options:
- label: I've thoroughly read the documentations about this problem but still have no answer.
required: true
- label: I've searched the Github Issues/Discussions but didn't find any similar problems that have been solved.
required: true
- label: I've searched the internet for this problem but didn't find anything helpful.
required: true
validations:
required: true
- type: textarea
id: question
attributes:
label: Questions with details
description: What do you want to know?
placeholder: Describe your question with critical details here.
validations:
required: true
- type: textarea
id: code
attributes:
label: Code snippets (optional)
description: Illustrate your question with source code if needed.
render: go
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thank you for contributing to `gnet`! Please fill this out to help us review your pull request more efficiently.
Was this change discussed in an issue first? That can help save time in case the change is not a good fit for the project. Not all pull requests get merged.
It is not uncommon for pull requests to go through several, iterative reviews. Please be patient with us! Every reviewer is a volunteer, and each has their own style.
-->
## 1. Are you opening this pull request for bug-fix, optimization or new feature?
## 2. Please describe how these code changes achieve your intention.
<!-- Please be specific. Motivate the problem, and justify why this is the best solution. -->
## 3. Please link to the relevant issues (if any).
<!-- This adds crucial context to your change. -->
## 4. What documentation changes (if any) need to be made/updated because of this PR?
<!-- Reviewers will often reference this first in order to know what to expect from the change. Please be specific enough so that they can paste your wording into the documentation directly. -->
## 4. Checklist
- [ ] I have squashed all insignificant commits.
- [ ] I have commented my code for explaining package types, values, functions, and non-obvious lines.
- [ ] I have written unit tests and verified that all tests passes (if needed).
- [ ] I have documented feature info on the README (only when this PR is adding a new feature).
- [ ] (optional) I am willing to help maintain this change if there are issues with it later.
================================================
FILE: .github/release-drafter.yml
================================================
name-template: Gnet v$RESOLVED_VERSION
tag-template: v$RESOLVED_VERSION
categories:
- title: 🧨 Breaking changes
labels:
- breaking changes
- title: 🚀 Features
labels:
- proposal
- new feature
- title: 🛩 Enhancements
labels:
- enhancements
- optimization
- title: 🐛 Bugfixes
labels:
- bug
- title: 📚 Documentation
labels:
- docs
- title: 🗃 Misc
labels:
- chores
change-template: '- $TITLE (#$NUMBER)'
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- major
minor:
labels:
- minor
- new feature
- proposal
patch:
labels:
- patch
- bug
- dependencies
default: patch
autolabeler:
- label: bug
title:
- /fix/i
- /bug/i
- /resolve/i
- label: docs
files:
- '*.md'
title:
- /doc/i
- /README/i
- label: enhancement
title:
- /opt:/i
- /refactor/i
- /optimize/i
- /improve/i
- /update/i
- /remove/i
- /delete/i
- label: optimization
title:
- /opt:/i
- /refactor/i
- /optimize/i
- /improve/i
- /update/i
- /remove/i
- /delete/i
- label: new feature
title:
- /feat:/i
- /feature/i
- /implement/i
- /add/i
- /minor/i
- label: dependencies
title:
- /dep:/i
- /dependencies/i
- /upgrade/i
- /bump up/i
- label: chores
title:
- /chore/i
- /misc/i
- /cleanup/i
- /clean up/i
- label: major
title:
- /major:/i
- label: minor
title:
- /minor:/i
- label: patch
title:
- /patch:/i
template: |
## Changelogs
$CHANGES
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
Thanks to all these contributors: $CONTRIBUTORS for making this release possible.
================================================
FILE: .github/workflows/codeql.yml
================================================
name: "Code Scanning"
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/codeql.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/codeql.yml'
schedule:
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
# │ │ │ │ │
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
- cron: '30 4 * * 0'
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: go
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below).
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ✏️ If the Autobuild fails above, remove it and uncomment the following
# three lines and modify them (or add more) to build your code if your
# project uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
================================================
FILE: .github/workflows/cross-compile-bsd.yml
================================================
name: Cross-compile for *BSD
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/cross-compile-bsd.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/cross-compile-bsd.yml'
env:
GO111MODULE: on
GOPROXY: "https://proxy.golang.org"
jobs:
build:
strategy:
fail-fast: false
matrix:
go: ['1.20', '1.25']
os:
- ubuntu-latest
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Print Go environment
id: go-env
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
# Calculate the short SHA1 hash of the git commit
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "GO_CACHE=$(go env GOCACHE)" >> $GITHUB_OUTPUT
- name: Cross-compiling for DragonFlyBSD
run: GOOS=dragonfly GOARCH=amd64 go build
- name: Cross-compiling for DragonFlyBSD -tags=poll_opt,gc_opt
run: GOOS=dragonfly GOARCH=amd64 go build -tags=poll_opt,gc_opt
- name: Cross-compiling for FreeBSD
run: GOOS=freebsd GOARCH=amd64 go build
- name: Cross-compiling for FreeBSD -tags=poll_opt,gc_opt
run: GOOS=freebsd GOARCH=amd64 go build -tags=poll_opt,gc_opt
- name: Cross-compiling for NetBSD
run: GOOS=netbsd GOARCH=amd64 go build
- name: Cross-compiling for NetBSD -tags=poll_opt,gc_opt
run: GOOS=netbsd GOARCH=amd64 go build -tags=poll_opt,gc_opt
- name: Cross-compiling for OpenBSD
run: GOOS=openbsd GOARCH=amd64 go build
- name: Cross-compiling for OpenBSD -tags=poll_opt,gc_opt
run: GOOS=openbsd GOARCH=amd64 go build -tags=poll_opt,gc_opt
================================================
FILE: .github/workflows/gh-translator.yml
================================================
name: 'gh-translator'
on:
issues:
types: [opened]
pull_request:
types: [opened]
issue_comment:
types: [created, edited]
discussion:
types: [created, edited, answered]
discussion_comment:
types: [created, edited]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
with:
BOT_GITHUB_TOKEN: ${{ secrets.GH_TRANSLATOR_TOKEN }}
IS_MODIFY_TITLE: true
CUSTOM_BOT_NOTE: 🤖 Non-English text detected, translating ...
================================================
FILE: .github/workflows/pull-request.yml
================================================
name: Check pull request target
on:
pull_request:
types:
- opened
- reopened
- synchronize
branches:
- master
jobs:
check-branches:
runs-on: ubuntu-latest
steps:
- name: Check target branch
run: |
if [ ${{ github.head_ref }} != "dev" ]; then
echo "Only pull requests from dev branch are only allowed to be merged into master branch."
exit 1
fi
================================================
FILE: .github/workflows/release-drafter.yml
================================================
name: Release Drafter
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
# pull_request event is required only for autolabeler
pull_request:
# Only following types are handled by the action, but one can default to all as well
types: [opened, reopened, synchronize]
# pull_request_target event is required for autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]
permissions:
contents: read
jobs:
update_release_draft:
permissions:
# write permission is required to create a github release
contents: write
# write permission is required for autolabeler
# otherwise, read permission is required at least
pull-requests: write
runs-on: ubuntu-latest
steps:
# (Optional) GitHub Enterprise requires GHE_HOST variable set
#- name: Set GHE_HOST
# run: |
# echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
# Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v6
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
# with:
# config-name: my-config.yml
# disable-autolabeler: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/stale-bot.yml
================================================
name: Monitor inactive issues and PRs
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
jobs:
stale-issues:
runs-on: ubuntu-latest
permissions:
actions: write
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
operations-per-run: 100
days-before-issue-stale: 30
days-before-issue-close: 7
stale-issue-label: 'stale'
stale-issue-message: |
This issue is marked as stale because it has been open for 30 days with no activity.
You should take one of the following actions:
- Manually close this issue if it is no longer relevant
- Comment if you have more information to share
This issue will be automatically closed in 7 days if no further activity occurs.
close-issue-message: |
This issue was closed because it has been inactive for 7 days since being marked as stale.
If you believe this is a false alarm, please leave a comment for it or open a new issue, you can also reopen this issue directly if you have permission.
days-before-pr-stale: 21
days-before-pr-close: 7
stale-pr-label: 'stale'
stale-pr-message: |
This PR is marked as stale because it has been open for 21 days with no activity.
You should take one of the following actions:
- Manually close this PR if it is no longer relevant
- Push new commits or comment if you have more information to share
This PR will be automatically closed in 7 days if no further activity occurs.
close-pr-message: |
This PR was closed because it has been inactive for 7 days since being marked as stale.
If you believe this is a false alarm, feel free to reopen this PR or create a new one.
repo-token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/test.yml
================================================
name: Run tests
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test.yml'
env:
GO111MODULE: on
GOPROXY: "https://proxy.golang.org"
jobs:
lint:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
#- windows-latest
name: Run golangci-lint
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.1.6
args: -v -E gocritic -E misspell -E revive -E godot --timeout 5m
test:
needs: lint
strategy:
fail-fast: false
matrix:
go: ['1.20', '1.26']
os:
- ubuntu-latest
- macos-14 # check out this issue: https://github.com/actions/runner-images/issues/10924
- windows-latest
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Print Go environment
id: go-env
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
# Calculate the short SHA1 hash of the git commit
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "GO_CACHE=$(go env GOCACHE)" >> $GITHUB_OUTPUT
- name: Run unit tests for packages
run: go test $(go list ./... | tail -n +2)
# NOTE: -race is conditionally disabled for Windows with Go 1.26 due to
# known instability of the Go race detector on this platform/version
# combination (intermittent crashes and timeouts observed in CI).
# This is likely a regression in Go 1.26 on Windows. Check out #752 and #754 for details.
# Once the underlying issue is resolved and CI is stable, this
# condition should be revisited so that race testing can be re-enabled.
- name: Run integration tests
run: go test -v ${{ !(runner.os == 'Windows' && startsWith(matrix.go, '1.26')) && '-race' || '' }} -coverprofile="codecov.report" -covermode=atomic -timeout 15m -failfast
- name: Upload the code coverage report to codecov.io
uses: codecov/codecov-action@v5
with:
files: ./codecov.report
flags: unittests
name: codecov-gnet
fail_ci_if_error: true
verbose: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
================================================
FILE: .github/workflows/test_gc_opt.yml
================================================
name: Run tests with -tags=gc_opt
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_gc_opt.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_gc_opt.yml'
env:
GO111MODULE: on
GOPROXY: "https://proxy.golang.org"
jobs:
lint:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
#- windows-latest
name: Run golangci-lint
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.1.6
args: -v -E gocritic -E misspell -E revive -E godot --timeout 5m
test:
needs: lint
strategy:
fail-fast: false
matrix:
go: ['1.20', '1.26']
os:
- ubuntu-latest
- macos-14 # check out this issue: https://github.com/actions/runner-images/issues/10924
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Print Go environment
id: go-env
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
# Calculate the short SHA1 hash of the git commit
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "GO_CACHE=$(go env GOCACHE)" >> $GITHUB_OUTPUT
- name: Run unit tests for packages
run: go test $(go list ./... | tail -n +2)
- name: Run integration tests
run: go test -v -race -tags=gc_opt -coverprofile="codecov.report" -covermode=atomic -timeout 15m -failfast
- name: Upload the code coverage report to codecov.io
uses: codecov/codecov-action@v5
with:
files: ./codecov.report
flags: unittests
name: codecov-gnet
fail_ci_if_error: true
verbose: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
================================================
FILE: .github/workflows/test_poll_opt.yml
================================================
name: Run tests with -tags=poll_opt
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_poll_opt.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_poll_opt.yml'
env:
GO111MODULE: on
GOPROXY: "https://proxy.golang.org"
jobs:
lint:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
name: Run golangci-lint
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.1.6
args: -v -E gocritic -E misspell -E revive -E godot
test:
needs: lint
strategy:
fail-fast: false
matrix:
go: ['1.20', '1.26']
os:
- ubuntu-latest
- macos-14 # check out this issue: https://github.com/actions/runner-images/issues/10924
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Print Go environment
id: go-env
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
# Calculate the short SHA1 hash of the git commit
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "GO_CACHE=$(go env GOCACHE)" >> $GITHUB_OUTPUT
- name: Run unit tests for packages
run: go test $(go list ./... | tail -n +2)
- name: Run integration tests with -tags=poll_opt
run: go test -v -tags=poll_opt -coverprofile="codecov.report" -covermode=atomic -timeout 10m -failfast
- name: Upload the code coverage report to codecov.io
uses: codecov/codecov-action@v5
with:
files: ./codecov.report
flags: unittests
name: codecov-gnet-poll_opt
fail_ci_if_error: true
verbose: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
================================================
FILE: .github/workflows/test_poll_opt_gc_opt.yml
================================================
name: Run tests with -tags=poll_opt,gc_opt
on:
push:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_poll_opt_gc_opt.yml'
pull_request:
branches:
- master
- dev
- 1.x
paths-ignore:
- '**.md'
- '**.yml'
- '**.yaml'
- '!.github/workflows/test_poll_opt_gc_opt.yml'
env:
GO111MODULE: on
GOPROXY: "https://proxy.golang.org"
jobs:
lint:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
name: Run golangci-lint
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '^1.20'
cache: false
- name: Setup and run golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.1.6
args: -v -E gocritic -E misspell -E revive -E godot
test:
needs: lint
strategy:
fail-fast: false
matrix:
go: ['1.20', '1.26']
os:
- ubuntu-latest
- macos-14 # check out this issue: https://github.com/actions/runner-images/issues/10924
name: Go ${{ matrix.go }} @ ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: Print Go environment
id: go-env
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
# Calculate the short SHA1 hash of the git commit
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "GO_CACHE=$(go env GOCACHE)" >> $GITHUB_OUTPUT
- name: Run unit tests for packages
run: go test $(go list ./... | tail -n +2)
- name: Run integration tests with -tags=poll_opt
run: go test -v -tags=poll_opt,gc_opt -coverprofile="codecov.report" -covermode=atomic -timeout 10m -failfast
- name: Upload the code coverage report to codecov.io
uses: codecov/codecov-action@v5
with:
files: ./codecov.report
flags: unittests
name: codecov-gnet-poll_opt
fail_ci_if_error: true
verbose: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
================================================
FILE: .gitignore
================================================
# IDEs
.idea/
.vscode/
# dependencies
/node_modules
# production
/build
# generated files
.docusaurus
.docusaurus/
.cache-loader
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at panjf2000@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## With issues:
- Use the search tool before opening a new issue.
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
## With pull requests:
- Open your pull request against `dev`.
- Open one pull request for only one feature/proposal, if you have several those, please put them into different PRs, whereas you are allowed to open one pull request with several bug-fixes.
- Your pull request should have no more than two commits, if not, you should squash them.
- It should pass all tests in the available continuous integrations systems such as TravisCI.
- You should add/modify tests to cover your proposed code changes.
- If your pull request contains a new feature, please document it on the README.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2019-present Andy Pan
Copyright (c) 2017 Joshua J Baker
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.
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://raw.githubusercontent.com/panjf2000/logos/master/gnet/logo.png" alt="gnet" />
<br />
<a title="Build Status" target="_blank" href="https://github.com/panjf2000/gnet/actions?query=workflow%3ATests"><img src="https://img.shields.io/github/actions/workflow/status/panjf2000/gnet/test.yml?branch=dev&style=flat-square&logo=github-actions" /></a>
<a title="Codecov" target="_blank" href="https://codecov.io/gh/panjf2000/gnet"><img src="https://img.shields.io/codecov/c/github/panjf2000/gnet?style=flat-square&logo=codecov" /></a>
<a title="Supported Platforms" target="_blank" href="https://github.com/panjf2000/gnet"><img src="https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20*BSD%20%7C%20Windows-549688?style=flat-square&logo=launchpad" /></a>
<a title="Minimum Go Version" target="_blank" href="https://github.com/panjf2000/gnet"><img src="https://img.shields.io/badge/go-%3E%3D1.20-30dff3?style=flat-square&logo=go" /></a>
<br />
<a title="Go Report Card" target="_blank" href="https://goreportcard.com/report/github.com/panjf2000/gnet"><img src="https://goreportcard.com/badge/github.com/panjf2000/gnet?style=flat-square" /></a>
<a title="Doc for gnet" target="_blank" href="https://pkg.go.dev/github.com/panjf2000/gnet/v2#section-documentation"><img src="https://img.shields.io/badge/go.dev-doc-007d9c?style=flat-square&logo=read-the-docs" /></a>
<a title="Mentioned in Awesome Go" target="_blank" href="https://github.com/avelino/awesome-go#networking"><img src="https://awesome.re/mentioned-badge-flat.svg" /></a>
<a title="Release" target="_blank" href="https://github.com/panjf2000/gnet/releases"><img src="https://img.shields.io/github/v/release/panjf2000/gnet.svg?color=161823&style=flat-square&logo=smartthings" /></a>
<a title="Tag" target="_blank" href="https://github.com/panjf2000/gnet/tags"><img src="https://img.shields.io/github/v/tag/panjf2000/gnet?color=%23ff8936&logo=fitbit&style=flat-square" /></a>
</p>
<p align="center">
<a href="https://trendshift.io/repositories/9602" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9602" alt="panjf2000%2Fgnet | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
English | [中文](README_ZH.md)
### 🎉🎉🎉 Feel free to join [the channels about `gnet` on the Discord Server](https://discord.gg/UyKD7NZcfH).
# 📖 Introduction
`gnet` is an event-driven networking framework that is ultra-fast and lightweight. It is built from scratch by exploiting [epoll](https://man7.org/linux/man-pages/man7/epoll.7.html) and [kqueue](https://en.wikipedia.org/wiki/Kqueue) and it can achieve much higher performance with lower memory consumption than Go [net](https://golang.org/pkg/net/) in many specific scenarios.
`gnet` and [net](https://golang.org/pkg/net/) don't share the same philosophy in network programming. Thus, building network applications with `gnet` can be significantly different from building them with [net](https://golang.org/pkg/net/), and the philosophies can't be reconciled. There are other similar products written in other programming languages in the community, such as [libuv](https://github.com/libuv/libuv), [netty](https://github.com/netty/netty), [twisted](https://github.com/twisted/twisted), [tornado](https://github.com/tornadoweb/tornado), etc. which work in a similar pattern as `gnet` under the hood.
`gnet` is not designed to displace the Go [net](https://golang.org/pkg/net/), but to create an alternative in the Go ecosystem for building performance-critical network services. As a result of which, `gnet` is not as comprehensive as Go [net](https://golang.org/pkg/net/), it provides only the core functionality (via a concise set of APIs) required by a network application and it doesn't plan on becoming a coverall networking framework, as I think Go [net](https://golang.org/pkg/net/) has done a good enough job in that area.
`gnet` sells itself as a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go which works on the transport layer with TCP/UDP protocols and Unix Domain Socket. It enables developers to implement their own protocols(HTTP, RPC, WebSocket, Redis, etc.) of application layer upon `gnet` for building diversified network services. For instance, you get an HTTP Server if you implement HTTP protocol upon `gnet` while you have a Redis Server done with the implementation of Redis protocol upon `gnet` and so on.
**`gnet` derives from the project: `evio` with much higher performance and more features.**
# 🚀 Features
## 🦖 Milestones
- [x] [High-performance](#-performance) event-driven looping based on a networking model of multiple threads/goroutines
- [x] Built-in goroutine pool powered by the library [ants](https://github.com/panjf2000/ants)
- [x] Lock-free during the entire runtime
- [x] Concise and easy-to-use APIs
- [x] Efficient, reusable, and elastic memory buffer: (Elastic-)Ring-Buffer, Linked-List-Buffer and Elastic-Mixed-Buffer
- [x] Multiple protocols/IPC mechanisms: `TCP`, `UDP`, and `Unix Domain Socket`
- [x] Multiple load-balancing algorithms: `Round-Robin`, `Source-Addr-Hash`, and `Least-Connections`
- [x] Flexible ticker event
- [x] `gnet` client
- [x] Running on `Linux`, `macOS`, `Windows`, and *BSD: `Darwin`/`DragonFlyBSD`/`FreeBSD`/`NetBSD`/`OpenBSD`
- [x] **Edge-triggered** I/O support
- [x] Multiple network addresses binding
- [x] Support registering new connections to event-loops
## 🕊 Roadmap
- [ ] **TLS** support
- [ ] [io_uring](https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023) support
- [ ] **KCP** support
***Windows version of `gnet` should only be used in development for developing and testing, it shouldn't be used in production.***
# 🎬 Getting started
`gnet` is available as a Go module and we highly recommend that you use `gnet` via [Go Modules](https://go.dev/blog/using-go-modules), with Go 1.11 Modules enabled (Go 1.11+), you can just simply add `import "github.com/panjf2000/gnet/v2"` to the codebase and run `go mod download/go mod tidy` or `go [build|run|test]` to download the necessary dependencies automatically.
## With v2
```bash
go get -u github.com/panjf2000/gnet/v2
```
## With v1
```bash
go get -u github.com/panjf2000/gnet
```
# 🎡 Use cases
The following corporations/organizations use `gnet` as the underlying network service in production.
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://www.tencent.com/">
<img src="https://res.strikefreedom.top/static_res/logos/tencent_logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.tencentgames.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/tencent-games-logo.jpeg" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.iqiyi.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/iqiyi-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.mi.com/global/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/mi-logo.png" width="200" />
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.360.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/360-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://tieba.baidu.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/baidu-tieba-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.jd.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/jd-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.zuoyebang.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/zuoyebang-logo.jpeg" width="200" />
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.bytedance.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/ByteDance_Logo.png" width="250" />
</a>
</td>
</tr>
</tbody>
</table>
If you're also using `gnet` in production, please help us enrich this list by opening a pull request.
# 📊 Performance
## Benchmarks on TechEmpower
```bash
# Hardware Environment
* 28 HT Cores Intel(R) Xeon(R) Gold 5120 CPU @ 3.20GHz
* 32GB RAM
* Dedicated Cisco 10-gigabit Ethernet switch
* Debian 12 "bookworm"
* Go1.19.x linux/amd64
```

This is a leaderboard of the top ***50*** out of ***486*** frameworks that encompass various programming languages worldwide, in which `gnet` is ranked ***first***.

This is the full framework ranking of Go and `gnet` tops all the other frameworks, which makes `gnet` the ***fastest*** networking framework in Go.
To see the full ranking list, visit [TechEmpower Benchmark **Round 22**](https://www.techempower.com/benchmarks/#hw=ph&test=plaintext§ion=data-r22).
***Note that the HTTP implementation of gnet on TechEmpower is half-baked and fine-tuned for benchmark purposes only and far from production-ready.***
## Contrasts to the similar networking libraries
### On Linux (epoll)
#### Environment
```bash
# Machine information
OS : Ubuntu 20.04/x86_64
CPU : 8 CPU cores, AMD EPYC 7K62 48-Core Processor
Memory : 16.0 GiB
# Go version and settings
Go Version : go1.17.2 linux/amd64
GOMAXPROCS : 8
# Benchmark parameters
TCP connections : 1000/2000/5000/10000
Packet size : 512/1024/2048/4096/8192/16384/32768/65536 bytes
Test duration : 15s
```
[](https://github.com/gnet-io/gnet-benchmarks)
[]((https://github.com/gnet-io/gnet-benchmarks))
### On MacOS (kqueue)
#### Environment
```bash
# Machine information
OS : MacOS Big Sur/x86_64
CPU : 6 CPU cores, Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory : 16.0 GiB
# Go version and settings
Go Version : go1.16.5 darwin/amd64
GOMAXPROCS : 12
# Benchmark parameters
TCP connections : 300/400/500/600/700
Packet size : 512/1024/2048/4096/8192 bytes
Test duration : 15s
```
[]((https://github.com/gnet-io/gnet-benchmarks))
[]((https://github.com/gnet-io/gnet-benchmarks))
### Combat with Rust
[](https://www.youtube.com/watch?v=31R8Ef9A0iw)
# ⚠️ License
The source code of `gnet` should be distributed under the Apache-2.0 license.
# 👏 Contributors
Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a PR and thank you to all the developers who already made contributions to `gnet`!
<a href="https://github.com/panjf2000/gnet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=panjf2000/gnet" />
</a>
# ⚓ Relevant Articles
- [A Million WebSockets and Go](https://www.freecodecamp.org/news/million-websockets-and-go-cc58418460bb/)
- [Going Infinite, handling 1M websockets connections in Go](https://speakerdeck.com/eranyanay/going-infinite-handling-1m-websockets-connections-in-go)
- [Go netpoller 原生网络模型之源码全面揭秘](https://strikefreedom.top/go-netpoll-io-multiplexing-reactor)
- [gnet: 一个轻量级且高性能的 Golang 网络库](https://strikefreedom.top/go-event-loop-networking-library-gnet)
- [最快的 Go 网络框架 gnet 来啦!](https://strikefreedom.top/releasing-gnet-v1-with-techempower)
# ☕️ Buy me a coffee
> Please be sure to leave your name, GitHub account, or other social media accounts when you donate by the following means so that I can add it to the list of donors as a token of my appreciation.
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a target="_blank" href="https://buymeacoffee.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/bmc_qr.png" width="250" alt="By me coffee" />
</a>
</td>
<td align="center" valign="middle">
<a target="_blank" href="https://www.patreon.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/patreon_logo.png" width="250" alt="Patreon" />
</a>
</td>
<td align="center" valign="middle">
<a target="_blank" href="https://opencollective.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/open-collective-logo.png" width="250" alt="OpenCollective" />
</a>
</td>
</tr>
</tbody>
</table>
# 🔑 JetBrains OS licenses
`gnet` has been being developed with `GoLand` IDE under the ***free JetBrains Open Source license(s)*** granted by JetBrains s.r.o., hence I would like to express my thanks here.
<a href="https://www.jetbrains.com/?from=gnet" target="_blank"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo."></a>
# 🔋 Sponsorship
<p>
<h3>This project is supported by:</h3>
<a href="https://www.digitalocean.com/"><img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px" />
</a>
</p>
================================================
FILE: README_ZH.md
================================================
<p align="center">
<img src="https://raw.githubusercontent.com/panjf2000/logos/master/gnet/logo.png" alt="gnet" />
<br />
<a title="Build Status" target="_blank" href="https://github.com/panjf2000/gnet/actions?query=workflow%3ATests"><img src="https://img.shields.io/github/actions/workflow/status/panjf2000/gnet/test.yml?branch=dev&style=flat-square&logo=github-actions" /></a>
<a title="Codecov" target="_blank" href="https://codecov.io/gh/panjf2000/gnet"><img src="https://img.shields.io/codecov/c/github/panjf2000/gnet?style=flat-square&logo=codecov" /></a>
<a title="Supported Platforms" target="_blank" href="https://github.com/panjf2000/gnet"><img src="https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20*BSD%20%7C%20Windows-549688?style=flat-square&logo=launchpad" /></a>
<a title="Minimum Go Version" target="_blank" href="https://github.com/panjf2000/gnet"><img src="https://img.shields.io/badge/go-%3E%3D1.20-30dff3?style=flat-square&logo=go" /></a>
<br />
<a title="Go Report Card" target="_blank" href="https://goreportcard.com/report/github.com/panjf2000/gnet"><img src="https://goreportcard.com/badge/github.com/panjf2000/gnet?style=flat-square" /></a>
<a title="Doc for gnet" target="_blank" href="https://pkg.go.dev/github.com/panjf2000/gnet/v2#section-documentation"><img src="https://img.shields.io/badge/go.dev-doc-007d9c?style=flat-square&logo=read-the-docs" /></a>
<a title="Mentioned in Awesome Go" target="_blank" href="https://github.com/avelino/awesome-go#networking"><img src="https://awesome.re/mentioned-badge-flat.svg" /></a>
<a title="Release" target="_blank" href="https://github.com/panjf2000/gnet/releases"><img src="https://img.shields.io/github/v/release/panjf2000/gnet.svg?color=161823&style=flat-square&logo=smartthings" /></a>
<a title="Tag" target="_blank" href="https://github.com/panjf2000/gnet/tags"><img src="https://img.shields.io/github/v/tag/panjf2000/gnet?color=%23ff8936&logo=fitbit&style=flat-square" /></a>
</p>
<p align="center">
<a href="https://trendshift.io/repositories/9602" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9602" alt="panjf2000%2Fgnet | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
[英文](README.md) | 中文
### 🎉🎉🎉 欢迎加入 `gnet` 在 [Discord 服务器上的频道](https://discord.gg/UyKD7NZcfH).
# 📖 简介
`gnet` 是一个基于事件驱动的高性能和轻量级网络框架。这个框架是基于 [epoll](https://en.wikipedia.org/wiki/Epoll) 和 [kqueue](https://en.wikipedia.org/wiki/Kqueue) 从零开发的,而且相比 Go [net](https://golang.org/pkg/net/),它能以更低的内存占用实现更高的性能。
`gnet` 和 [net](https://golang.org/pkg/net/) 有着不一样的网络编程范式。因此,用 `gnet` 开发网络应用和用 [net](https://golang.org/pkg/net/) 开发区别很大,而且两者之间不可调和。社区里有其他同类的产品像是 [libuv](https://github.com/libuv/libuv), [netty](https://github.com/netty/netty), [twisted](https://github.com/twisted/twisted), [tornado](https://github.com/tornadoweb/tornado),`gnet` 的底层工作原理和这些框架非常类似。
`gnet` 不是为了取代 [net](https://golang.org/pkg/net/) 而生的,而是在 Go 生态中为开发者提供一个开发性能敏感的网络服务的替代品。也正因如此,`gnet` 在功能全面性上比不了 Go [net](https://golang.org/pkg/net/),它只会提供网络应用所需的最核心的功能和最精简的 APIs,而且 `gnet` 也并没有打算变成一个无所不包的网络框架,因为我觉得 Go [net](https://golang.org/pkg/net/) 在这方面已经做得足够好了。
`gnet` 的卖点在于它是一个高性能、轻量级、非阻塞的纯 Go 语言实现的传输层(TCP/UDP/Unix Domain Socket)网络框架。开发者可以使用 `gnet` 来实现自己的应用层网络协议(HTTP、RPC、Redis、WebSocket 等等),从而构建出自己的应用层网络服务。比如在 `gnet` 上实现 HTTP 协议就可以创建出一个 HTTP 服务器 或者 Web 开发框架,实现 Redis 协议就可以创建出自己的 Redis 服务器等等。
**`gnet` 衍生自另一个项目:`evio`,但拥有更丰富的功能特性,且性能远胜之。**
# 🚀 功能
## 🦖 里程碑
- [x] 基于多线程/协程网络模型的[高性能](#-性能测试)事件驱动循环
- [x] 内置 goroutine 池,由开源库 [ants](https://github.com/panjf2000/ants) 提供支持
- [x] 整个生命周期是无锁的
- [x] 简单易用的 APIs
- [x] 高效、可重用而且自动伸缩的内存 buffer:(Elastic-)Ring-Buffer, Linked-List-Buffer and Elastic-Mixed-Buffer
- [x] 多种网络协议/IPC 机制:`TCP`、`UDP` 和 `Unix Domain Socket`
- [x] 多种负载均衡算法:`Round-Robin(轮询)`、`Source-Addr-Hash(源地址哈希)` 和 `Least-Connections(最少连接数)`
- [x] 灵活的事件定时器
- [x] `gnet` 客户端支持
- [x] 支持 `Linux`, `macOS`, `Windows` 和 *BSD 操作系统: `Darwin`/`DragonFlyBSD`/`FreeBSD`/`NetBSD`/`OpenBSD`
- [x] **Edge-triggered** I/O 支持
- [x] 多网络地址绑定
- [x] 支持注册新的连接到事件循环
## 🕊 蓝图
- [ ] 支持 **TLS**
- [ ] 支持 [io_uring](https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023)
- [ ] 支持 **KCP**
***`gnet` 的 Windows 版本应该仅用于开发阶段的开发和测试,切勿用于生产环境***。
# 🎬 开始
`gnet` 是一个 Go module,而且我们也强烈推荐通过 [Go Modules](https://go.dev/blog/using-go-modules) 来使用 `gnet`,在开启 Go Modules 支持(Go 1.11+)之后可以通过简单地在代码中写 `import "github.com/panjf2000/gnet/v2"` 来引入 `gnet`,然后执行 `go mod download/go mod tidy` 或者 `go [build|run|test]` 这些命令来自动下载所依赖的包。
## 使用 v2
```bash
go get -u github.com/panjf2000/gnet/v2
```
## 使用 v1
```bash
go get -u github.com/panjf2000/gnet
```
# 🎡 用户案例
以下公司/组织在生产环境上使用了 `gnet` 作为底层网络服务。
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://www.tencent.com/">
<img src="https://res.strikefreedom.top/static_res/logos/tencent_logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.tencentgames.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/tencent-games-logo.jpeg" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.iqiyi.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/iqiyi-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.mi.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/mi-logo.png" width="200" />
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.360.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/360-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://tieba.baidu.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/baidu-tieba-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.jd.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/jd-logo.png" width="200" />
</a>
</td>
<td align="center" valign="middle">
<a href="https://www.zuoyebang.com/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/zuoyebang-logo.jpeg" width="200" />
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://www.bytedance.com/zh/" target="_blank">
<img src="https://res.strikefreedom.top/static_res/logos/ByteDance_Logo.png" width="250" />
</a>
</td>
</tr>
</tbody>
</table>
如果你也正在生产环境上使用 `gnet`,欢迎提 Pull Request 来丰富这份列表。
# 📊 性能测试
## TechEmpower 性能测试
```bash
# 硬件环境
* 28 HT Cores Intel(R) Xeon(R) Gold 5120 CPU @ 3.20GHz
* 32GB RAM
* Dedicated Cisco 10-gigabit Ethernet switch
* Debian 12 "bookworm"
* Go1.19.x linux/amd64
```

这是包含全部编程语言框架的性能排名***前 50*** 的结果,总榜单包含了全世界共计 ***486*** 个框架,其中 `gnet` 排名***第一***。

这是 Go 语言分类下的全部排名,`gnet` 超越了其他所有框架,位列第一,是***最快***的 Go 网络框架。
完整的排行可以通过 [TechEmpower Benchmark **Round 22**](https://www.techempower.com/benchmarks/#hw=ph&test=plaintext§ion=data-r22) 查看。
***请注意,TechEmpower 上的 gnet 的 HTTP 实现是不完备且针对性调优的,仅仅是用于压测目的,不是生产可用的***。
## 同类型的网络库性能对比
### On Linux (epoll)
#### Environment
```bash
# Machine information
OS : Ubuntu 20.04/x86_64
CPU : 8 CPU cores, AMD EPYC 7K62 48-Core Processor
Memory : 16.0 GiB
# Go version and settings
Go Version : go1.17.2 linux/amd64
GOMAXPROCS : 8
# Benchmark parameters
TCP connections : 1000/2000/5000/10000
Packet size : 512/1024/2048/4096/8192/16384/32768/65536 bytes
Test duration : 15s
```
[](https://github.com/gnet-io/gnet-benchmarks)
[]((https://github.com/gnet-io/gnet-benchmarks))
### On MacOS (kqueue)
#### Environment
```bash
# Machine information
OS : MacOS Big Sur/x86_64
CPU : 6 CPU cores, Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory : 16.0 GiB
# Go version and settings
Go Version : go1.16.5 darwin/amd64
GOMAXPROCS : 12
# Benchmark parameters
TCP connections : 300/400/500/600/700
Packet size : 512/1024/2048/4096/8192 bytes
Test duration : 15s
```
[]((https://github.com/gnet-io/gnet-benchmarks))
[]((https://github.com/gnet-io/gnet-benchmarks))
### "硬刚" Rust
[](https://www.youtube.com/watch?v=31R8Ef9A0iw)
# ⚠️ 证书
`gnet` 的源码需在遵循 Apache-2.0 开源证书的前提下使用。
# 👏 贡献者
请在提 PR 之前仔细阅读 [Contributing Guidelines](CONTRIBUTING.md),感谢那些为 `gnet` 贡献过代码的开发者!
<a href="https://github.com/panjf2000/gnet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=panjf2000/gnet" />
</a>
# ⚓ 相关文章
- [A Million WebSockets and Go](https://www.freecodecamp.org/news/million-websockets-and-go-cc58418460bb/)
- [Going Infinite, handling 1M websockets connections in Go](https://speakerdeck.com/eranyanay/going-infinite-handling-1m-websockets-connections-in-go)
- [Go netpoller 原生网络模型之源码全面揭秘](https://strikefreedom.top/go-netpoll-io-multiplexing-reactor)
- [gnet: 一个轻量级且高性能的 Golang 网络库](https://strikefreedom.top/go-event-loop-networking-library-gnet)
- [最快的 Go 网络框架 gnet 来啦!](https://strikefreedom.top/releasing-gnet-v1-with-techempower)
# ☕️ 打赏
> 当您通过以下方式进行捐赠时,请务必留下姓名、GitHub 账号或其他社交媒体账号,以便我将其添加到捐赠者名单中,以表谢意。
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a target="_blank" href="https://buymeacoffee.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/bmc_qr.png" width="250" alt="By me coffee" />
</a>
</td>
<td align="center" valign="middle">
<a target="_blank" href="https://www.patreon.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/patreon_logo.png" width="250" alt="Patreon" />
</a>
</td>
<td align="center" valign="middle">
<a target="_blank" href="https://opencollective.com/panjf2000">
<img src="https://res.strikefreedom.top/static_res/logos/open-collective-logo.png" width="250" alt="OpenCollective" />
</a>
</td>
</tr>
</tbody>
</table>
# 🔑 JetBrains 开源证书支持
`gnet` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 ***free JetBrains Open Source license(s)*** 正版免费授权,在此表达我的谢意。
<a href="https://www.jetbrains.com/?from=gnet" target="_blank"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo."></a>
# 🔋 赞助商
<p>
<h3>本项目由以下机构赞助:</h3>
<a href="https://www.digitalocean.com/"><img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px" />
</a>
</p>
================================================
FILE: acceptor_unix.go
================================================
// Copyright (c) 2021 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"runtime"
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
"github.com/panjf2000/gnet/v2/pkg/queue"
"github.com/panjf2000/gnet/v2/pkg/socket"
)
func (el *eventloop) accept0(fd int, _ netpoll.IOEvent, _ netpoll.IOFlags) error {
for {
nfd, sa, err := socket.Accept(fd)
switch err {
case nil:
case unix.EAGAIN: // the Accept queue has been drained out, we can return now
return nil
case unix.EINTR, unix.ECONNRESET, unix.ECONNABORTED:
// ECONNRESET or ECONNABORTED could indicate that a socket
// in the Accept queue was closed before we Accept()ed it.
// It's a silly error, let's retry it.
continue
default:
el.getLogger().Errorf("Accept() failed due to error: %v", err)
return errors.ErrAcceptSocket
}
remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa)
network := el.listeners[fd].network
if opts := el.engine.opts; opts.TCPKeepAlive > 0 && network == "tcp" &&
(runtime.GOOS != "linux" && runtime.GOOS != "freebsd" && runtime.GOOS != "dragonfly") {
// TCP keepalive options are not inherited from the listening socket
// on platforms other than Linux, FreeBSD, or DragonFlyBSD.
// We therefore need to set them on the accepted socket explicitly.
//
// Check out https://github.com/nginx/nginx/pull/337 for details.
if err = setKeepAlive(
nfd,
true,
opts.TCPKeepAlive,
opts.TCPKeepInterval,
opts.TCPKeepCount); err != nil {
el.getLogger().Errorf("failed to set TCP keepalive on fd=%d: %v", fd, err)
}
}
el := el.engine.eventLoops.next(remoteAddr)
c := newStreamConn(network, nfd, el, sa, el.listeners[fd].addr, remoteAddr)
err = el.poller.Trigger(queue.HighPriority, el.register, c)
if err != nil {
el.getLogger().Errorf("failed to enqueue the accepted socket fd=%d to poller: %v", c.fd, err)
_ = unix.Close(nfd)
c.release()
}
}
}
func (el *eventloop) accept(fd int, ev netpoll.IOEvent, flags netpoll.IOFlags) error {
network := el.listeners[fd].network
if network == "udp" {
return el.readUDP(fd, ev, flags)
}
nfd, sa, err := socket.Accept(fd)
switch err {
case nil:
case unix.EINTR, unix.EAGAIN, unix.ECONNRESET, unix.ECONNABORTED:
// ECONNRESET or ECONNABORTED could indicate that a socket
// in the Accept queue was closed before we Accept()ed it.
// It's a silly error, let's retry it.
return nil
default:
el.getLogger().Errorf("Accept() failed due to error: %v", err)
return errors.ErrAcceptSocket
}
remoteAddr := socket.SockaddrToTCPOrUnixAddr(sa)
if opts := el.engine.opts; opts.TCPKeepAlive > 0 && el.listeners[fd].network == "tcp" &&
(runtime.GOOS != "linux" && runtime.GOOS != "freebsd" && runtime.GOOS != "dragonfly") {
// TCP keepalive options are not inherited from the listening socket
// on platforms other than Linux, FreeBSD, or DragonFlyBSD.
// We therefore need to set them on the accepted socket explicitly.
//
// Check out https://github.com/nginx/nginx/pull/337 for details.
if err = setKeepAlive(
nfd,
true,
opts.TCPKeepAlive,
opts.TCPKeepInterval,
opts.TCPKeepCount); err != nil {
el.getLogger().Errorf("failed to set TCP keepalive on fd=%d: %v", fd, err)
}
}
c := newStreamConn(network, nfd, el, sa, el.listeners[fd].addr, remoteAddr)
return el.register0(c)
}
================================================
FILE: acceptor_windows.go
================================================
// Copyright (c) 2023 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 gnet
import (
"errors"
"net"
"runtime"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
func (eng *engine) listenStream(ln net.Listener) (err error) {
if eng.opts.LockOSThread {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
}
defer func() { eng.shutdown(err) }()
for {
// Accept TCP socket.
tc, e := ln.Accept()
if e != nil {
err = e
if !eng.beingShutdown.Load() {
eng.opts.Logger.Errorf("Accept() fails due to error: %v", err)
} else if errors.Is(err, net.ErrClosed) {
err = errors.Join(err, errorx.ErrEngineShutdown)
}
return
}
el := eng.eventLoops.next(tc.RemoteAddr())
c := newStreamConn(el, tc, nil)
el.ch <- &openConn{c: c}
goroutine.DefaultWorkerPool.Submit(func() {
var buffer [0x10000]byte
for {
n, err := tc.Read(buffer[:])
if err != nil {
el.ch <- &netErr{c, err}
return
}
el.ch <- packTCPConn(c, buffer[:n])
}
})
}
}
func (eng *engine) ListenUDP(pc net.PacketConn) (err error) {
if eng.opts.LockOSThread {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
}
defer func() { eng.shutdown(err) }()
var buffer [0x10000]byte
for {
// Read data from UDP socket.
n, addr, e := pc.ReadFrom(buffer[:])
if e != nil {
err = e
if !eng.beingShutdown.Load() {
eng.opts.Logger.Errorf("failed to receive data from UDP fd due to error:%v", err)
} else if errors.Is(err, net.ErrClosed) {
err = errors.Join(err, errorx.ErrEngineShutdown)
}
return
}
el := eng.eventLoops.next(addr)
c := newUDPConn(el, pc, nil, pc.LocalAddr(), addr, nil)
el.ch <- packUDPConn(c, buffer[:n])
}
}
================================================
FILE: client_test.go
================================================
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || windows
package gnet
import (
"bytes"
crand "crypto/rand"
"io"
"math/rand"
"net"
"path/filepath"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
bbPool "github.com/panjf2000/gnet/v2/pkg/pool/bytebuffer"
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
type connHandler struct {
network string
rspCh chan []byte
data []byte
}
type clientEvents struct {
*BuiltinEventEngine
tester *testing.T
svr *testClient
}
func (ev *clientEvents) OnBoot(e Engine) Action {
fd, err := e.Dup()
assert.ErrorIsf(ev.tester, err, errorx.ErrEmptyEngine, "expected error: %v, but got: %v",
errorx.ErrUnsupportedOp, err)
assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd)
fd, err = e.DupListener("tcp", "abc")
assert.ErrorIsf(ev.tester, err, errorx.ErrEmptyEngine, "expected error: %v, but got: %v",
errorx.ErrUnsupportedOp, err)
assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd)
return None
}
var pingMsg = []byte("PING\r\n")
func (ev *clientEvents) OnOpen(Conn) (out []byte, action Action) {
out = pingMsg
return
}
func (ev *clientEvents) OnClose(Conn, error) Action {
if ev.svr != nil {
if atomic.AddInt32(&ev.svr.clientActive, -1) == 0 {
return Shutdown
}
}
return None
}
func (ev *clientEvents) OnTraffic(c Conn) (action Action) {
handler := c.Context().(*connHandler)
packetLen := streamLen
if handler.network == "udp" {
packetLen = datagramLen
}
buf, err := c.Next(-1)
assert.NoError(ev.tester, err)
handler.data = append(handler.data, buf...)
if len(handler.data) < packetLen {
return
}
handler.rspCh <- handler.data
handler.data = nil
return
}
func (ev *clientEvents) OnTick() (delay time.Duration, action Action) {
delay = 200 * time.Millisecond
return
}
func (ev *clientEvents) OnShutdown(e Engine) {
fd, err := e.Dup()
assert.ErrorIsf(ev.tester, err, errorx.ErrEmptyEngine, "expected error: %v, but got: %v",
errorx.ErrUnsupportedOp, err)
assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd)
fd, err = e.DupListener("tcp", "abc")
assert.ErrorIsf(ev.tester, err, errorx.ErrEmptyEngine, "expected error: %v, but got: %v",
errorx.ErrUnsupportedOp, err)
assert.EqualValuesf(ev.tester, fd, -1, "expected -1, but got: %d", fd)
}
func TestClient(t *testing.T) {
// start an engine
// connect 10 clients
// each client will pipe random data for 1-3 seconds.
// the writes to the engine will be random sizes. 0KB - 1MB.
// the engine will echo back the data.
// waits for graceful connection closing.
t.Run("poll-LT", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{false, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{false, 0, false, true, false, false, 10, LeastConnections})
})
})
t.Run("tcp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{false, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{false, 0, false, true, true, false, 10, LeastConnections})
})
})
t.Run("udp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{false, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{false, 0, false, true, false, false, 10, LeastConnections})
})
})
t.Run("udp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{false, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{false, 0, false, true, true, false, 10, LeastConnections})
})
})
t.Run("unix", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, false, true, false, false, 10, SourceAddrHash})
})
})
t.Run("unix-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, false, true, true, false, 10, SourceAddrHash})
})
})
})
t.Run("poll-ET", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 0, false, true, false, false, 10, LeastConnections})
})
})
t.Run("tcp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 0, false, true, true, false, 10, LeastConnections})
})
})
t.Run("udp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 0, false, true, false, false, 10, LeastConnections})
})
})
t.Run("udp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 0, false, true, true, false, 10, LeastConnections})
})
})
t.Run("unix", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, false, true, false, false, 10, SourceAddrHash})
})
})
t.Run("unix-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, false, true, true, false, 10, SourceAddrHash})
})
})
})
t.Run("poll-ET-chunk", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 1 << 18, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 1 << 19, false, true, false, false, 10, LeastConnections})
})
})
t.Run("tcp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 1 << 18, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 1 << 19, false, true, true, false, 10, LeastConnections})
})
})
t.Run("udp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 1 << 18, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 1 << 19, false, true, false, false, 10, LeastConnections})
})
})
t.Run("udp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 1 << 18, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 1 << 19, false, true, true, false, 10, LeastConnections})
})
})
t.Run("unix", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 1 << 18, false, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 1 << 19, false, true, false, false, 10, SourceAddrHash})
})
})
t.Run("unix-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 1 << 18, false, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 1 << 19, false, true, true, false, 10, SourceAddrHash})
})
})
})
t.Run("poll-reuseport-LT", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{false, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{false, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("tcp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{false, 0, true, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{false, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("udp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{false, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{false, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("udp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{false, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{false, 0, true, true, true, false, 10, LeastConnections})
})
})
t.Run("unix", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("unix-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, true, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{false, 0, true, true, true, false, 10, LeastConnections})
})
})
})
t.Run("poll-reuseport-ET", func(t *testing.T) {
t.Run("tcp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("tcp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "tcp", ":9991", &testConf{true, 0, true, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "tcp", ":9992", &testConf{true, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("udp", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("udp-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "udp", ":9991", &testConf{true, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "udp", ":9992", &testConf{true, 0, true, true, true, false, 10, LeastConnections})
})
})
t.Run("unix", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, true, false, false, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, true, true, false, false, 10, LeastConnections})
})
})
t.Run("unix-async", func(t *testing.T) {
t.Run("1-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, true, false, true, false, 10, RoundRobin})
})
t.Run("N-loop", func(t *testing.T) {
runClient(t, "unix", testUnixAddr(t), &testConf{true, 0, true, true, true, false, 10, LeastConnections})
})
})
})
}
type testClient struct {
*BuiltinEventEngine
client *Client
tester *testing.T
eng Engine
network string
addr string
multicore bool
async bool
nclients int
started int32
connected int32
clientActive int32
disconnected int32
udpReadHeader int32
}
func (s *testClient) OnBoot(eng Engine) (action Action) {
s.eng = eng
return
}
func (s *testClient) OnOpen(c Conn) (out []byte, action Action) {
c.SetContext(&sync.Once{})
atomic.AddInt32(&s.connected, 1)
assert.NotNil(s.tester, c.LocalAddr(), "nil local addr")
assert.NotNil(s.tester, c.RemoteAddr(), "nil remote addr")
return
}
func (s *testClient) OnClose(c Conn, err error) (action Action) {
if err != nil {
logging.Debugf("error occurred on closed, %v\n", err)
}
if s.network != "udp" {
assert.IsType(s.tester, c.Context(), new(sync.Once), "invalid context")
}
atomic.AddInt32(&s.disconnected, 1)
if atomic.LoadInt32(&s.connected) == atomic.LoadInt32(&s.disconnected) &&
atomic.LoadInt32(&s.disconnected) == int32(s.nclients) {
action = Shutdown
}
return
}
func (s *testClient) OnShutdown(Engine) {
if s.network == "udp" {
assert.EqualValues(s.tester, int32(s.nclients), atomic.LoadInt32(&s.udpReadHeader))
}
}
func (s *testClient) OnTraffic(c Conn) (action Action) {
readHeader := func() {
ping := make([]byte, len(pingMsg))
n, err := io.ReadFull(c, ping)
assert.NoError(s.tester, err)
assert.EqualValues(s.tester, len(pingMsg), n)
assert.Equal(s.tester, string(pingMsg), string(ping), "bad header")
}
v := c.Context()
if v != nil {
v.(*sync.Once).Do(readHeader)
}
if s.async {
buf := bbPool.Get()
_, err := c.WriteTo(buf)
assert.NoError(s.tester, err, "WriteTo error")
if s.network == "tcp" || s.network == "unix" {
// just for test
_ = c.InboundBuffered()
_ = c.OutboundBuffered()
_, _ = c.Discard(1)
}
if v == nil && bytes.Equal(buf.Bytes(), pingMsg) {
atomic.AddInt32(&s.udpReadHeader, 1)
buf.Reset()
}
err = goPool.DefaultWorkerPool.Submit(
func() {
if buf.Len() > 0 {
err := c.AsyncWrite(buf.Bytes(), nil)
assert.NoError(s.tester, err)
}
})
assert.NoError(s.tester, err)
return
}
buf, err := c.Next(-1)
assert.NoError(s.tester, err, "Reading data error")
if v == nil && bytes.Equal(buf, pingMsg) {
atomic.AddInt32(&s.udpReadHeader, 1)
buf = nil
}
if len(buf) > 0 {
n, err := c.Write(buf)
assert.NoError(s.tester, err)
assert.EqualValues(s.tester, len(buf), n)
}
return
}
func (s *testClient) OnTick() (delay time.Duration, action Action) {
delay = 100 * time.Millisecond
if atomic.CompareAndSwapInt32(&s.started, 0, 1) {
for i := 0; i < s.nclients; i++ {
atomic.AddInt32(&s.clientActive, 1)
var netConn bool
if i%2 == 0 {
netConn = true
}
err := goPool.DefaultWorkerPool.Submit(
func() {
startGnetClient(s.tester, s.client, s.network, s.addr, s.multicore, s.async, netConn)
})
assert.NoError(s.tester, err)
}
}
if s.network == "udp" && atomic.LoadInt32(&s.clientActive) == 0 {
action = Shutdown
return
}
return
}
func runClient(t *testing.T, network, addr string, conf *testConf) {
ts := &testClient{
tester: t,
network: network,
addr: addr,
multicore: conf.multicore,
async: conf.async,
nclients: conf.clients,
}
var err error
clientEV := &clientEvents{tester: t, svr: ts}
ts.client, err = NewClient(
clientEV,
WithMulticore(conf.multicore),
WithEdgeTriggeredIO(conf.et),
WithEdgeTriggeredIOChunk(conf.etChunk),
WithTCPNoDelay(TCPNoDelay),
WithLockOSThread(true),
WithTicker(true),
)
assert.NoError(t, err)
err = ts.client.Start()
assert.NoError(t, err)
defer ts.client.Stop() //nolint:errcheck
err = Run(ts,
network+"://"+addr,
WithEdgeTriggeredIO(conf.et),
WithEdgeTriggeredIOChunk(conf.etChunk),
WithLockOSThread(conf.async),
WithMulticore(conf.multicore),
WithReusePort(conf.reuseport),
WithTicker(true),
WithTCPKeepAlive(time.Minute),
WithTCPKeepInterval(time.Second*10),
WithTCPKeepCount(10),
WithLoadBalancing(conf.lb))
assert.NoError(t, err)
}
func startGnetClient(t *testing.T, cli *Client, network, addr string, multicore, async, netDial bool) {
var (
c Conn
err error
)
handler := &connHandler{
network: network,
rspCh: make(chan []byte, 1),
}
if netDial {
var netConn net.Conn
netConn, err = net.Dial(network, addr)
assert.NoError(t, err)
c, err = cli.EnrollContext(netConn, handler)
} else {
c, err = cli.DialContext(network, addr, handler)
}
assert.NoError(t, err)
defer c.Close() //nolint:errcheck
err = c.Wake(nil)
assert.NoError(t, err)
rspCh := handler.rspCh
duration := time.Duration((rand.Float64()*2+1)*float64(time.Second)) / 2
logging.Debugf("test duration: %v", duration)
start := time.Now()
for time.Since(start) < duration {
reqData := make([]byte, streamLen)
if network == "udp" {
reqData = reqData[:datagramLen]
}
_, err = crand.Read(reqData)
assert.NoError(t, err)
err = c.AsyncWrite(reqData, nil)
assert.NoError(t, err)
respData := <-rspCh
assert.NoError(t, err)
if !async {
// assert.Equalf(t, reqData, respData, "response mismatch with protocol:%s, multi-core:%t, content of bytes: %d vs %d", network, multicore, string(reqData), string(respData))
assert.Equalf(
t,
reqData,
respData,
"response mismatch with protocol:%s, multi-core:%t, length of bytes: %d vs %d",
network,
multicore,
len(reqData),
len(respData),
)
}
}
}
type clientEventsForWake struct {
BuiltinEventEngine
tester *testing.T
ch chan struct{}
}
func (ev *clientEventsForWake) OnBoot(_ Engine) Action {
ev.ch = make(chan struct{})
return None
}
func (ev *clientEventsForWake) OnTraffic(c Conn) (action Action) {
n, err := c.Read(nil)
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, n)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
buf := make([]byte, 10)
n, err = c.Read(buf)
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, n)
assert.ErrorIsf(ev.tester, err, io.ErrShortBuffer, "expected error: %v, but got: %v", io.ErrShortBuffer, err)
buf, err = c.Next(10)
assert.Nilf(ev.tester, buf, "expected: %v, but got: %v", nil, buf)
assert.ErrorIsf(ev.tester, err, io.ErrShortBuffer, "expected error: %v, but got: %v", io.ErrShortBuffer, err)
buf, err = c.Next(-1)
assert.Emptyf(ev.tester, buf, "expected an empty slice, but got: %v", buf)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
buf, err = c.Peek(10)
assert.Nilf(ev.tester, buf, "expected: %v, but got: %v", nil, buf)
assert.ErrorIsf(ev.tester, err, io.ErrShortBuffer, "expected error: %v, but got: %v", io.ErrShortBuffer, err)
buf, err = c.Peek(-1)
assert.Emptyf(ev.tester, buf, "expected an empty slice, but got: %v", buf)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
n, err = c.Discard(10)
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, n)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
n, err = c.Discard(-1)
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, n)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
m, err := c.WriteTo(io.Discard)
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, m)
assert.NoErrorf(ev.tester, err, "expected: %v, but got: %v", nil, err)
n = c.InboundBuffered()
assert.Zerof(ev.tester, n, "expected: %v, but got: %v", 0, m)
<-ev.ch
return None
}
type serverEventsForWake struct {
BuiltinEventEngine
network, addr string
client *Client
clientEV *clientEventsForWake
tester *testing.T
clients int32
started int32
}
func (ev *serverEventsForWake) OnOpen(_ Conn) ([]byte, Action) {
atomic.AddInt32(&ev.clients, 1)
return nil, None
}
func (ev *serverEventsForWake) OnClose(_ Conn, _ error) Action {
if atomic.AddInt32(&ev.clients, -1) == 0 {
return Shutdown
}
return None
}
func (ev *serverEventsForWake) OnTick() (time.Duration, Action) {
if atomic.CompareAndSwapInt32(&ev.started, 0, 1) {
err := goPool.DefaultWorkerPool.Submit(func() {
testConnWakeImmediately(ev.tester, ev.client, ev.clientEV, ev.network, ev.addr)
})
assert.NoError(ev.tester, err)
}
return 100 * time.Millisecond, None
}
func testConnWakeImmediately(t *testing.T, client *Client, clientEV *clientEventsForWake, network, addr string) {
c, err := client.Dial(network, addr)
assert.NoErrorf(t, err, "failed to dial: %v", err)
err = c.Wake(nil)
assert.NoError(t, err)
err = c.Close()
assert.NoError(t, err)
clientEV.ch <- struct{}{}
}
func TestWakeConnImmediately(t *testing.T) {
currentLogger, currentFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
t.Cleanup(func() {
logging.SetDefaultLoggerAndFlusher(currentLogger, currentFlusher) // restore
})
clientEV := &clientEventsForWake{tester: t}
logPath := filepath.Join(t.TempDir(), "gnet-test-wake-conn-immediately.log")
client, err := NewClient(clientEV,
WithSocketRecvBuffer(4*1024),
WithSocketSendBuffer(4*1024),
WithLogPath(logPath),
WithLogLevel(logging.WarnLevel),
WithReadBufferCap(512),
WithWriteBufferCap(512))
assert.NoError(t, err)
logging.Cleanup()
err = client.Start()
assert.NoError(t, err)
defer client.Stop() //nolint:errcheck
serverEV := &serverEventsForWake{tester: t, network: "tcp", addr: ":18888", client: client, clientEV: clientEV}
err = Run(serverEV, serverEV.network+"://"+serverEV.addr, WithTicker(true))
assert.NoError(t, err)
}
func TestClientReadOnEOF(t *testing.T) {
currentLogger, currentFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
t.Cleanup(func() {
logging.SetDefaultLoggerAndFlusher(currentLogger, currentFlusher) // restore
})
ln, err := net.Listen("tcp", "127.0.0.1:9999")
assert.NoError(t, err)
defer ln.Close() //nolint:errcheck
err = goPool.DefaultWorkerPool.Submit(func() {
for {
conn, err := ln.Accept()
if err != nil {
break
}
err = goPool.DefaultWorkerPool.Submit(func() {
process(conn)
})
assert.NoError(t, err)
}
})
assert.NoError(t, err)
ev := &clientReadOnEOF{
result: make(chan struct {
data []byte
err error
}, 1),
data: []byte("test"),
}
cli, err := NewClient(ev,
WithSocketRecvBuffer(4*1024),
WithSocketSendBuffer(4*1024),
WithTCPKeepAlive(time.Minute),
WithLogger(zap.NewExample().Sugar()),
WithReadBufferCap(32*1024),
WithWriteBufferCap(32*1024))
assert.NoError(t, err)
defer cli.Stop() //nolint:errcheck
err = cli.Start()
assert.NoError(t, err)
_, err = cli.Dial("tcp", "127.0.0.1:9999")
assert.NoError(t, err)
select {
case res := <-ev.result:
assert.NoError(t, res.err)
assert.EqualValuesf(t, ev.data, res.data, "expected: %v, but got: %v", ev.data, res.data)
case <-time.After(5 * time.Second):
t.Errorf("timeout waiting for the result")
}
}
func process(conn net.Conn) {
defer conn.Close() //nolint:errcheck
buf := make([]byte, 8)
n, err := conn.Read(buf)
if err != nil {
return
}
_, _ = conn.Write(buf[:n])
_ = conn.Close()
}
type clientReadOnEOF struct {
BuiltinEventEngine
data []byte
result chan struct {
data []byte
err error
}
}
func (clientReadOnEOF) OnBoot(Engine) (action Action) {
return None
}
func (cli clientReadOnEOF) OnOpen(Conn) (out []byte, action Action) {
return cli.data, None
}
func (clientReadOnEOF) OnClose(Conn, error) (action Action) {
return Close
}
func (cli clientReadOnEOF) OnTraffic(c Conn) (action Action) {
data, err := c.Next(-1)
cli.result <- struct {
data []byte
err error
}{data: data, err: err}
return None
}
================================================
FILE: client_unix.go
================================================
// Copyright (c) 2021 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"context"
"errors"
"net"
"strconv"
"syscall"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/pkg/buffer/ring"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
"github.com/panjf2000/gnet/v2/pkg/math"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
"github.com/panjf2000/gnet/v2/pkg/queue"
"github.com/panjf2000/gnet/v2/pkg/socket"
)
// Client of gnet.
type Client struct {
opts *Options
eng *engine
}
// NewClient creates an instance of Client.
func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
options := loadOptions(opts...)
cli = new(Client)
cli.opts = options
logger, logFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
if options.Logger == nil {
if options.LogPath != "" {
logger, logFlusher, _ = logging.CreateLoggerAsLocalFile(options.LogPath, options.LogLevel)
}
options.Logger = logger
} else {
logger = options.Logger
logFlusher = nil
}
logging.SetDefaultLoggerAndFlusher(logger, logFlusher)
rootCtx, shutdown := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(rootCtx)
eng := engine{
listeners: make(map[int]*listener),
opts: options,
turnOff: shutdown,
eventHandler: eh,
eventLoops: new(leastConnectionsLoadBalancer),
concurrency: struct {
*errgroup.Group
ctx context.Context
}{eg, ctx},
}
if options.EdgeTriggeredIOChunk > 0 {
options.EdgeTriggeredIO = true
options.EdgeTriggeredIOChunk = math.CeilToPowerOfTwo(options.EdgeTriggeredIOChunk)
} else if options.EdgeTriggeredIO {
options.EdgeTriggeredIOChunk = 1 << 20 // 1MB
}
rbc := options.ReadBufferCap
switch {
case rbc <= 0:
options.ReadBufferCap = MaxStreamBufferCap
case rbc <= ring.DefaultBufferSize:
options.ReadBufferCap = ring.DefaultBufferSize
default:
options.ReadBufferCap = math.CeilToPowerOfTwo(rbc)
}
wbc := options.WriteBufferCap
switch {
case wbc <= 0:
options.WriteBufferCap = MaxStreamBufferCap
case wbc <= ring.DefaultBufferSize:
options.WriteBufferCap = ring.DefaultBufferSize
default:
options.WriteBufferCap = math.CeilToPowerOfTwo(wbc)
}
cli.eng = &eng
return
}
// Start starts the client event-loop, handing IO events.
func (cli *Client) Start() error {
numEventLoop := determineEventLoops(cli.opts)
logging.Infof("Starting gnet client with %d event loops", numEventLoop)
cli.eng.eventHandler.OnBoot(Engine{cli.eng})
var el0 *eventloop
for i := 0; i < numEventLoop; i++ {
p, err := netpoll.OpenPoller()
if err != nil {
cli.eng.closeEventLoops()
return err
}
el := eventloop{
listeners: cli.eng.listeners,
engine: cli.eng,
poller: p,
buffer: make([]byte, cli.opts.ReadBufferCap),
eventHandler: cli.eng.eventHandler,
}
el.connections.init()
cli.eng.eventLoops.register(&el)
if cli.opts.Ticker && el.idx == 0 {
el0 = &el
}
}
cli.eng.eventLoops.iterate(func(_ int, el *eventloop) bool {
cli.eng.concurrency.Go(el.run)
return true
})
// Start the ticker.
if el0 != nil {
ctx := cli.eng.concurrency.ctx
cli.eng.concurrency.Go(func() error {
el0.ticker(ctx)
return nil
})
}
logging.Debugf("default logging level is %s", logging.LogLevel())
return nil
}
// Stop stops the client event-loop.
func (cli *Client) Stop() error {
cli.eng.shutdown(nil)
cli.eng.eventHandler.OnShutdown(Engine{cli.eng})
// Notify all event-loops to exit.
cli.eng.eventLoops.iterate(func(_ int, el *eventloop) bool {
logging.Error(el.poller.Trigger(queue.HighPriority,
func(_ any) error { return errorx.ErrEngineShutdown }, nil))
return true
})
// Wait for all event-loops to exit.
err := cli.eng.concurrency.Wait()
cli.eng.closeEventLoops()
// Put the engine into the shutdown state.
cli.eng.inShutdown.Store(true)
// Flush the logger.
logging.Cleanup()
return err
}
// Dial is like net.Dial().
func (cli *Client) Dial(network, address string) (Conn, error) {
return cli.DialContext(network, address, nil)
}
// DialContext is like Dial but also accepts an empty interface ctx that can be obtained later via Conn.Context.
func (cli *Client) DialContext(network, address string, ctx any) (Conn, error) {
c, err := net.Dial(network, address)
if err != nil {
return nil, err
}
return cli.EnrollContext(c, ctx)
}
// Enroll converts a net.Conn to gnet.Conn and then adds it into the Client.
func (cli *Client) Enroll(c net.Conn) (Conn, error) {
return cli.EnrollContext(c, nil)
}
// EnrollContext is like Enroll but also accepts an empty interface ctx that can be obtained later via Conn.Context.
func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
defer c.Close() //nolint:errcheck
sc, ok := c.(syscall.Conn)
if !ok {
return nil, errors.New("failed to convert net.Conn to syscall.Conn")
}
rc, err := sc.SyscallConn()
if err != nil {
return nil, errors.New("failed to get syscall.RawConn from net.Conn")
}
var dupFD int
e := rc.Control(func(fd uintptr) {
dupFD, err = socket.Dup(int(fd))
})
if err != nil {
return nil, err
}
if e != nil {
return nil, e
}
if cli.opts.SocketSendBuffer > 0 {
if err = socket.SetSendBuffer(dupFD, cli.opts.SocketSendBuffer); err != nil {
return nil, err
}
}
if cli.opts.SocketRecvBuffer > 0 {
if err = socket.SetRecvBuffer(dupFD, cli.opts.SocketRecvBuffer); err != nil {
return nil, err
}
}
el := cli.eng.eventLoops.next(nil)
var (
sockAddr unix.Sockaddr
gc *conn
)
switch c.(type) {
case *net.UnixConn:
sockAddr, _, _, err = socket.GetUnixSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
return nil, err
}
ua := c.LocalAddr().(*net.UnixAddr)
ua.Name = c.RemoteAddr().String() + "." + strconv.Itoa(dupFD)
gc = newStreamConn("unix", dupFD, el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.TCPConn:
if cli.opts.TCPNoDelay == TCPNoDelay {
if err = socket.SetNoDelay(dupFD, 1); err != nil {
return nil, err
}
}
if cli.opts.TCPKeepAlive > 0 {
if err = setKeepAlive(
dupFD,
true,
cli.opts.TCPKeepAlive,
cli.opts.TCPKeepInterval,
cli.opts.TCPKeepCount); err != nil {
return nil, err
}
}
sockAddr, _, _, _, err = socket.GetTCPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
return nil, err
}
gc = newStreamConn("tcp", dupFD, el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.UDPConn:
sockAddr, _, _, _, err = socket.GetUDPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
return nil, err
}
gc = newUDPConn(dupFD, el, c.LocalAddr(), sockAddr, true)
default:
return nil, errorx.ErrUnsupportedProtocol
}
gc.ctx = ctx
connOpened := make(chan struct{})
ccb := &connWithCallback{c: gc, cb: func() {
close(connOpened)
}}
err = el.poller.Trigger(queue.HighPriority, el.register, ccb)
if err != nil {
gc.Close() //nolint:errcheck
return nil, err
}
<-connOpened
return gc, nil
}
================================================
FILE: client_windows.go
================================================
// Copyright (c) 2023 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 gnet
import (
"context"
"net"
"golang.org/x/sync/errgroup"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
type Client struct {
opts *Options
eng *engine
}
func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
options := loadOptions(opts...)
cli = &Client{opts: options}
logger, logFlusher := logging.GetDefaultLogger(), logging.GetDefaultFlusher()
if options.Logger == nil {
if options.LogPath != "" {
logger, logFlusher, _ = logging.CreateLoggerAsLocalFile(options.LogPath, options.LogLevel)
}
options.Logger = logger
} else {
logger = options.Logger
logFlusher = nil
}
logging.SetDefaultLoggerAndFlusher(logger, logFlusher)
rootCtx, shutdown := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(rootCtx)
eng := engine{
listeners: []*listener{},
opts: options,
turnOff: shutdown,
eventHandler: eh,
eventLoops: new(leastConnectionsLoadBalancer),
concurrency: struct {
*errgroup.Group
ctx context.Context
}{eg, ctx},
}
cli.eng = &eng
return
}
func (cli *Client) Start() error {
numEventLoop := determineEventLoops(cli.opts)
logging.Infof("Starting gnet client with %d event loops", numEventLoop)
cli.eng.eventHandler.OnBoot(Engine{cli.eng})
var el0 *eventloop
for i := 0; i < numEventLoop; i++ {
el := eventloop{
ch: make(chan any, 1024),
eng: cli.eng,
connections: make(map[*conn]struct{}),
eventHandler: cli.eng.eventHandler,
}
cli.eng.eventLoops.register(&el)
cli.eng.concurrency.Go(el.run)
if cli.opts.Ticker && el.idx == 0 {
el0 = &el
}
}
if el0 != nil {
ctx := cli.eng.concurrency.ctx
cli.eng.concurrency.Go(func() error {
el0.ticker(ctx)
return nil
})
}
logging.Debugf("default logging level is %s", logging.LogLevel())
return nil
}
func (cli *Client) Stop() error {
cli.eng.shutdown(nil)
cli.eng.eventHandler.OnShutdown(Engine{cli.eng})
// Notify all event-loops to exit.
cli.eng.closeEventLoops()
// Wait for all event-loops to exit.
err := cli.eng.concurrency.Wait()
// Put the engine into the shutdown state.
cli.eng.inShutdown.Store(true)
// Flush the logger.
logging.Cleanup()
return err
}
func (cli *Client) Dial(network, addr string) (Conn, error) {
return cli.DialContext(network, addr, nil)
}
func (cli *Client) DialContext(network, addr string, ctx any) (Conn, error) {
var (
c net.Conn
err error
)
c, err = net.Dial(network, addr)
if err != nil {
return nil, err
}
return cli.EnrollContext(c, ctx)
}
func (cli *Client) Enroll(nc net.Conn) (gc Conn, err error) {
return cli.EnrollContext(nc, nil)
}
func (cli *Client) EnrollContext(nc net.Conn, ctx any) (gc Conn, err error) {
el := cli.eng.eventLoops.next(nil)
connOpened := make(chan struct{})
switch v := nc.(type) {
case *net.TCPConn:
if cli.opts.TCPNoDelay == TCPNoDelay {
if err = v.SetNoDelay(true); err != nil {
return
}
}
c := newStreamConn(el, nc, ctx)
if opts := cli.opts; opts.TCPKeepAlive > 0 {
idle := opts.TCPKeepAlive
intvl := opts.TCPKeepInterval
if intvl == 0 {
intvl = opts.TCPKeepAlive / 5
}
cnt := opts.TCPKeepCount
if opts.TCPKeepCount == 0 {
cnt = 5
}
if err = c.SetKeepAlive(true, idle, intvl, cnt); err != nil {
return
}
}
el.ch <- &openConn{c: c, cb: func() { close(connOpened) }}
goroutine.DefaultWorkerPool.Submit(func() {
var buffer [0x10000]byte
for {
n, err := nc.Read(buffer[:])
if err != nil {
el.ch <- &netErr{c, err}
return
}
el.ch <- packTCPConn(c, buffer[:n])
}
})
gc = c
case *net.UnixConn:
c := newStreamConn(el, nc, ctx)
el.ch <- &openConn{c: c, cb: func() { close(connOpened) }}
goroutine.DefaultWorkerPool.Submit(func() {
var buffer [0x10000]byte
for {
n, err := nc.Read(buffer[:])
if err != nil {
el.ch <- &netErr{c, err}
return
}
el.ch <- packTCPConn(c, buffer[:n])
}
})
gc = c
case *net.UDPConn:
c := newUDPConn(el, nil, nc, nc.LocalAddr(), nc.RemoteAddr(), ctx)
el.ch <- &openConn{c: c, cb: func() { close(connOpened) }}
goroutine.DefaultWorkerPool.Submit(func() {
var buffer [0x10000]byte
for {
n, err := nc.Read(buffer[:])
if err != nil {
el.ch <- &netErr{c, err}
return
}
c := newUDPConn(el, nil, nc, nc.LocalAddr(), nc.RemoteAddr(), ctx)
el.ch <- packUDPConn(c, buffer[:n])
}
})
gc = c
default:
return nil, errorx.ErrUnsupportedProtocol
}
<-connOpened
return
}
================================================
FILE: conn_map.go
================================================
// Copyright (c) 2023 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.
//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd) && !gc_opt
package gnet
import (
"sync/atomic"
"github.com/panjf2000/gnet/v2/internal/gfd"
)
type connMatrix struct {
connCount int32
connMap map[int]*conn
}
func (cm *connMatrix) init() {
cm.connMap = make(map[int]*conn)
}
func (cm *connMatrix) iterate(f func(*conn) bool) {
for _, c := range cm.connMap {
if c != nil {
if !f(c) {
return
}
}
}
}
func (cm *connMatrix) incCount(_ int, delta int32) {
atomic.AddInt32(&cm.connCount, delta)
}
func (cm *connMatrix) loadCount() (n int32) {
return atomic.LoadInt32(&cm.connCount)
}
func (cm *connMatrix) addConn(c *conn, index int) {
c.gfd = gfd.NewGFD(c.fd, index, 0, 0)
cm.connMap[c.fd] = c
cm.incCount(0, 1)
}
func (cm *connMatrix) delConn(c *conn) {
delete(cm.connMap, c.fd)
cm.incCount(0, -1)
}
func (cm *connMatrix) getConn(fd int) *conn {
return cm.connMap[fd]
}
/*
func (cm *connMatrix) getConnByGFD(fd gfd.GFD) *conn {
return cm.connMap[fd.Fd()]
}
*/
================================================
FILE: conn_matrix.go
================================================
// Copyright (c) 2023 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.
//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd) && gc_opt
package gnet
import (
"sync/atomic"
"github.com/panjf2000/gnet/v2/internal/gfd"
)
type connMatrix struct {
disableCompact bool // disable compaction when it is true
connCounts [gfd.ConnMatrixRowMax]int32 // number of active connections in event-loop
row int // next available row index
column int // next available column index
table [gfd.ConnMatrixRowMax][]*conn // connection matrix of *conn, multiple slices
fd2gfd map[int]gfd.GFD // fd -> gfd.GFD
}
func (cm *connMatrix) init() {
cm.fd2gfd = make(map[int]gfd.GFD)
}
func (cm *connMatrix) iterate(f func(*conn) bool) {
cm.disableCompact = true
defer func() { cm.disableCompact = false }()
for _, conns := range cm.table {
for _, c := range conns {
if c != nil {
if !f(c) {
return
}
}
}
}
}
func (cm *connMatrix) incCount(row int, delta int32) {
atomic.AddInt32(&cm.connCounts[row], delta)
}
func (cm *connMatrix) loadCount() (n int32) {
for i := 0; i < len(cm.connCounts); i++ {
n += atomic.LoadInt32(&cm.connCounts[i])
}
return
}
func (cm *connMatrix) addConn(c *conn, index int) {
if cm.row >= gfd.ConnMatrixRowMax {
return
}
if cm.table[cm.row] == nil {
cm.table[cm.row] = make([]*conn, gfd.ConnMatrixColumnMax)
}
c.gfd = gfd.NewGFD(c.fd, index, cm.row, cm.column)
cm.fd2gfd[c.fd] = c.gfd
cm.table[cm.row][cm.column] = c
cm.incCount(cm.row, 1)
if cm.column++; cm.column == gfd.ConnMatrixColumnMax {
cm.row++
cm.column = 0
}
}
func (cm *connMatrix) delConn(c *conn) {
cfd, cgfd := c.fd, c.gfd
delete(cm.fd2gfd, cfd)
cm.incCount(cgfd.ConnMatrixRow(), -1)
if cm.connCounts[cgfd.ConnMatrixRow()] == 0 {
cm.table[cgfd.ConnMatrixRow()] = nil
} else {
cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = nil
}
if cm.row > cgfd.ConnMatrixRow() || cm.column > cgfd.ConnMatrixColumn() {
cm.row, cm.column = cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn()
}
// Locate the last *conn in table and move it to the deleted location.
if cm.disableCompact || cm.table[cgfd.ConnMatrixRow()] == nil { // the deleted *conn is the last one, do nothing here.
return
}
// Traverse backward to find the first non-empty point in the matrix until we reach the deleted position.
for row := gfd.ConnMatrixRowMax - 1; row >= cgfd.ConnMatrixRow(); row-- {
if cm.connCounts[row] == 0 {
continue
}
columnMin := -1
if row == cgfd.ConnMatrixRow() {
columnMin = cgfd.ConnMatrixColumn()
}
for column := gfd.ConnMatrixColumnMax - 1; column > columnMin; column-- {
if cm.table[row][column] == nil {
continue
}
gFd := cm.table[row][column].gfd
gFd.UpdateIndexes(cgfd.ConnMatrixRow(), cgfd.ConnMatrixColumn())
cm.table[row][column].gfd = gFd
cm.fd2gfd[gFd.Fd()] = gFd
cm.table[cgfd.ConnMatrixRow()][cgfd.ConnMatrixColumn()] = cm.table[row][column]
cm.incCount(row, -1)
cm.incCount(cgfd.ConnMatrixRow(), 1)
if cm.connCounts[row] == 0 {
cm.table[row] = nil
} else {
cm.table[row][column] = nil
}
cm.row, cm.column = row, column
return
}
}
}
func (cm *connMatrix) getConn(fd int) *conn {
gFD, ok := cm.fd2gfd[fd]
if !ok {
return nil
}
if cm.table[gFD.ConnMatrixRow()] == nil {
return nil
}
return cm.table[gFD.ConnMatrixRow()][gFD.ConnMatrixColumn()]
}
/*
func (cm *connMatrix) getConnByGFD(fd gfd.GFD) *conn {
if cm.table[fd.ConnMatrixRow()] == nil {
return nil
}
return cm.table[fd.ConnMatrixRow()][fd.ConnMatrixColumn()]
}
*/
================================================
FILE: conn_matrix_test.go
================================================
//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd) && gc_opt
package gnet
import (
"net"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
var testVastConns = false
func TestConnMatrix(t *testing.T) {
t.Run("1k-connections", func(t *testing.T) {
testConnMatrix(t, 1000)
})
t.Run("10k-connections", func(t *testing.T) {
testConnMatrix(t, 10000)
})
t.Run("100k-connections", func(t *testing.T) {
if !testVastConns {
t.Skip("skipped because testVastConns is set to false")
}
testConnMatrix(t, 100000)
})
t.Run("1m-connections", func(t *testing.T) {
if !testVastConns {
t.Skip("skipped because testVastConns is set to false")
}
testConnMatrix(t, 1000000)
})
}
const (
actionAdd = iota + 1
actionDel
)
type handleConn struct {
c *conn
action int
}
func testConnMatrix(t *testing.T, n int) {
handleConns := make(chan *handleConn, 1024)
connections := connMatrix{}
connections.init()
el := eventloop{engine: &engine{opts: &Options{}}}
done := make(chan struct{})
err := goPool.DefaultWorkerPool.Submit(func() {
for i := 0; i < n+n/2; i++ {
v := <-handleConns
switch v.action {
case actionAdd:
connections.addConn(v.c, 0)
case actionDel:
connections.delConn(v.c)
}
}
close(done)
})
require.NoError(t, err)
for i := 0; i < n; i++ {
c := newStreamConn("tcp", i, &el, &unix.SockaddrInet4{}, &net.TCPAddr{}, &net.TCPAddr{})
handleConns <- &handleConn{c, actionAdd}
if i%2 == 0 {
_ = goPool.DefaultWorkerPool.Submit(func() {
handleConns <- &handleConn{c, actionDel}
})
}
}
m := n / 2
<-done
if count := connections.loadCount(); count != int32(n)/2 {
t.Fatalf("unexpected conn count %d, expected %d", count, int32(n)/2)
}
for i := 0; i < len(connections.table); i++ {
if connections.connCounts[i] == 0 {
continue
}
for j := 0; j < len(connections.table[i]) && m > 0; j++ {
m--
c := connections.table[i][j]
if c == nil {
t.Fatalf("unexpected nil connection at row %d, column %d", i, j)
}
if c.fd != c.gfd.Fd() {
t.Fatalf("unexpected fd %d, expected fd %d", c.gfd.Fd(), c.fd)
}
if i != c.gfd.ConnMatrixRow() || j != c.gfd.ConnMatrixColumn() {
t.Fatalf("unexpected row %d, column %d, expected row %d, column %d",
c.gfd.ConnMatrixRow(), c.gfd.ConnMatrixColumn(), i, j)
}
gfd, ok := connections.fd2gfd[c.fd]
if !ok {
t.Fatalf("missing gfd for fd %d", c.fd)
}
if gfd != c.gfd {
t.Fatalf("expected gfd: %v, but got gfd: %v", c.gfd, gfd)
}
}
}
t.Log("connMatrix remains compact after many additions and deletions, test done!")
}
================================================
FILE: connection_bsd.go
================================================
// Copyright (c) 2021 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.
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
package gnet
import (
"io"
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
)
func (c *conn) processIO(_ int, filter netpoll.IOEvent, flags netpoll.IOFlags) (err error) {
el := c.loop
switch filter {
case unix.EVFILT_READ:
err = el.read(c)
case unix.EVFILT_WRITE:
err = el.write(c)
}
// EV_EOF indicates that the remote has closed the connection.
// We check for EV_EOF after processing the read/write event
// to ensure that nothing is left out on this event filter.
if flags&unix.EV_EOF != 0 && c.opened && err == nil {
switch filter {
case unix.EVFILT_READ:
// Received the event of EVFILT_READ|EV_EOF, but the previous eventloop.read
// failed to drain the socket buffer, so we make sure we get it done this time.
c.isEOF = true
err = el.read(c)
case unix.EVFILT_WRITE:
// On macOS, the kqueue in either LT or ET mode will notify with one event for the
// EOF of the TCP remote: EVFILT_READ|EV_ADD|EV_CLEAR|EV_EOF. But for some reason,
// two events will be issued in ET mode for the EOF of the Unix remote in this order:
// 1) EVFILT_WRITE|EV_ADD|EV_CLEAR|EV_EOF, 2) EVFILT_READ|EV_ADD|EV_CLEAR|EV_EOF.
err = el.write(c)
default:
c.outboundBuffer.Release() // don't bother to write to a connection that is already broken
err = el.close(c, io.EOF)
}
}
return
}
================================================
FILE: connection_linux.go
================================================
/*
* Copyright (c) 2021 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 gnet
import (
"io"
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
)
func (c *conn) processIO(_ int, ev netpoll.IOEvent, _ netpoll.IOFlags) error {
el := c.loop
// First check for any unexpected non-IO events.
// For these events we just close the connection directly.
if ev&(netpoll.ErrEvents|unix.EPOLLRDHUP) != 0 && ev&netpoll.ReadWriteEvents == 0 {
c.outboundBuffer.Release() // don't bother to write to a connection that is already broken
return el.close(c, io.EOF)
}
// Secondly, check for EPOLLOUT before EPOLLIN, the former has a higher priority
// than the latter regardless of the aliveness of the current connection:
//
// 1. When the connection is alive and the system is overloaded, we want to
// offload the incoming traffic by writing all pending data back to the remotes
// before continuing to read and handle requests.
// 2. When the connection is dead, we need to try writing any pending data back
// to the remote first and then close the connection.
//
// We perform eventloop.write for EPOLLOUT because it can take good care of either case.
if ev&(netpoll.WriteEvents|netpoll.ErrEvents) != 0 {
if err := el.write(c); err != nil {
return err
}
}
// Check for EPOLLIN before EPOLLRDHUP in case that there are pending data in
// the socket buffer.
if ev&(netpoll.ReadEvents|netpoll.ErrEvents) != 0 {
if err := el.read(c); err != nil {
return err
}
}
// Ultimately, check for EPOLLRDHUP, this event indicates that the remote has
// either closed connection or shut down the writing half of the connection.
if ev&unix.EPOLLRDHUP != 0 && c.opened {
if ev&unix.EPOLLIN == 0 { // unreadable EPOLLRDHUP, close the connection directly
return el.close(c, io.EOF)
}
// Received the event of EPOLLIN|EPOLLRDHUP, but the previous eventloop.read
// failed to drain the socket buffer, so we ensure to get it done this time.
c.isEOF = true
return el.read(c)
}
return nil
}
================================================
FILE: connection_unix.go
================================================
// Copyright (c) 2019 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"io"
"net"
"os"
"time"
"golang.org/x/sys/unix"
"github.com/panjf2000/gnet/v2/internal/gfd"
"github.com/panjf2000/gnet/v2/pkg/bs"
"github.com/panjf2000/gnet/v2/pkg/buffer/elastic"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
gio "github.com/panjf2000/gnet/v2/pkg/io"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
bsPool "github.com/panjf2000/gnet/v2/pkg/pool/byteslice"
"github.com/panjf2000/gnet/v2/pkg/queue"
"github.com/panjf2000/gnet/v2/pkg/socket"
)
type conn struct {
fd int // file descriptor
gfd gfd.GFD // gnet file descriptor
ctx any // user-defined context
remote unix.Sockaddr // remote socket address
proto string // protocol name: "tcp", "udp", or "unix".
localAddr net.Addr // local addr
remoteAddr net.Addr // remote addr
loop *eventloop // connected event-loop
outboundBuffer elastic.Buffer // buffer for data that is eligible to be sent to the remote
pollAttachment netpoll.PollAttachment // connection attachment for poller
inboundBuffer elastic.RingBuffer // buffer for leftover data from the remote
buffer []byte // buffer for the latest bytes
cache []byte // temporary cache for the inbound data
isDatagram bool // UDP protocol
opened bool // connection opened event fired
isEOF bool // whether the connection has reached EOF
}
func newStreamConn(proto string, fd int, el *eventloop, sa unix.Sockaddr, localAddr, remoteAddr net.Addr) (c *conn) {
c = &conn{
fd: fd,
proto: proto,
remote: sa,
loop: el,
localAddr: localAddr,
remoteAddr: remoteAddr,
pollAttachment: netpoll.PollAttachment{FD: fd},
}
c.pollAttachment.Callback = c.processIO
c.outboundBuffer.Reset(el.engine.opts.WriteBufferCap)
return
}
func newUDPConn(fd int, el *eventloop, localAddr net.Addr, sa unix.Sockaddr, connected bool) (c *conn) {
c = &conn{
fd: fd,
proto: "udp",
gfd: gfd.NewGFD(fd, el.idx, 0, 0),
remote: sa,
loop: el,
localAddr: localAddr,
remoteAddr: socket.SockaddrToUDPAddr(sa),
isDatagram: true,
pollAttachment: netpoll.PollAttachment{FD: fd, Callback: el.readUDP},
}
if connected {
c.remote = nil
}
return
}
func (c *conn) release() {
c.opened = false
c.isEOF = false
c.ctx = nil
c.buffer = nil
if addr, ok := c.localAddr.(*net.TCPAddr); ok && len(c.loop.listeners) == 0 && len(addr.Zone) > 0 {
bsPool.Put(bs.StringToBytes(addr.Zone))
}
if addr, ok := c.remoteAddr.(*net.TCPAddr); ok && len(addr.Zone) > 0 {
bsPool.Put(bs.StringToBytes(addr.Zone))
}
if addr, ok := c.localAddr.(*net.UDPAddr); ok && len(c.loop.listeners) == 0 && len(addr.Zone) > 0 {
bsPool.Put(bs.StringToBytes(addr.Zone))
}
if addr, ok := c.remoteAddr.(*net.UDPAddr); ok && len(addr.Zone) > 0 {
bsPool.Put(bs.StringToBytes(addr.Zone))
}
c.localAddr = nil
c.remoteAddr = nil
if !c.isDatagram {
c.remote = nil
c.inboundBuffer.Done()
c.outboundBuffer.Release()
}
}
func (c *conn) open(buf []byte) error {
if c.isDatagram && c.remote == nil {
return unix.Send(c.fd, buf, 0)
}
for {
n, err := unix.Write(c.fd, buf)
if err != nil {
if err == unix.EAGAIN {
_, _ = c.outboundBuffer.Write(buf)
break
}
return err
}
buf = buf[n:]
if len(buf) == 0 {
break
}
}
return nil
}
func (c *conn) write(data []byte) (n int, err error) {
isET := c.loop.engine.opts.EdgeTriggeredIO
n = len(data)
// If there is pending data in outbound buffer,
// the current data ought to be appended to the
// outbound buffer for maintaining the sequence
// of network packets.
if !c.outboundBuffer.IsEmpty() {
_, _ = c.outboundBuffer.Write(data)
return
}
defer func() {
if err != nil {
_ = c.loop.close(c, os.NewSyscallError("write", err))
}
}()
var sent int
loop:
if sent, err = unix.Write(c.fd, data); err != nil {
// A temporary error occurs, append the data to outbound buffer,
// writing it back to the remote in the next round for LT mode.
if err == unix.EAGAIN {
_, err = c.outboundBuffer.Write(data)
if !isET {
err = c.loop.poller.ModReadWrite(&c.pollAttachment, isET)
}
return
}
return 0, err
}
data = data[sent:]
if isET && len(data) > 0 {
goto loop
}
// Failed to send all data back to the remote, buffer the leftover data for the next round.
if len(data) > 0 {
_, _ = c.outboundBuffer.Write(data)
err = c.loop.poller.ModReadWrite(&c.pollAttachment, isET)
}
return
}
func (c *conn) writev(bs [][]byte) (n int, err error) {
isET := c.loop.engine.opts.EdgeTriggeredIO
for _, b := range bs {
n += len(b)
}
// If there is pending data in outbound buffer,
// the current data ought to be appended to the
// outbound buffer for maintaining the sequence
// of network packets.
if !c.outboundBuffer.IsEmpty() {
_, _ = c.outboundBuffer.Writev(bs)
return
}
defer func() {
if err != nil {
_ = c.loop.close(c, os.NewSyscallError("writev", err))
}
}()
remaining := n
var sent int
loop:
if sent, err = gio.Writev(c.fd, bs); err != nil {
// A temporary error occurs, append the data to outbound buffer,
// writing it back to the remote in the next round for LT mode.
if err == unix.EAGAIN {
_, err = c.outboundBuffer.Writev(bs)
if !isET {
err = c.loop.poller.ModReadWrite(&c.pollAttachment, isET)
}
return
}
return 0, err
}
pos := len(bs)
if remaining -= sent; remaining > 0 {
for i := range bs {
bn := len(bs[i])
if sent < bn {
bs[i] = bs[i][sent:]
pos = i
break
}
sent -= bn
}
}
bs = bs[pos:]
if isET && remaining > 0 {
goto loop
}
// Failed to send all data back to the remote, buffer the leftover data for the next round.
if remaining > 0 {
_, _ = c.outboundBuffer.Writev(bs)
err = c.loop.poller.ModReadWrite(&c.pollAttachment, isET)
}
return
}
type asyncWriteHook struct {
callback AsyncCallback
data []byte
}
func (c *conn) asyncWrite(a any) (err error) {
hook := a.(*asyncWriteHook)
defer func() {
if hook.callback != nil {
_ = hook.callback(c, err)
}
}()
if !c.opened {
return net.ErrClosed
}
_, err = c.write(hook.data)
return
}
type asyncWritevHook struct {
callback AsyncCallback
data [][]byte
}
func (c *conn) asyncWritev(a any) (err error) {
hook := a.(*asyncWritevHook)
defer func() {
if hook.callback != nil {
_ = hook.callback(c, err)
}
}()
if !c.opened {
return net.ErrClosed
}
_, err = c.writev(hook.data)
return
}
func (c *conn) sendTo(buf []byte, addr unix.Sockaddr) (n int, err error) {
defer func() {
if err != nil {
n = 0
}
}()
if addr != nil {
return len(buf), unix.Sendto(c.fd, buf, 0, addr)
}
if c.remote == nil { // connected UDP socket of client
return len(buf), unix.Send(c.fd, buf, 0)
}
return len(buf), unix.Sendto(c.fd, buf, 0, c.remote) // unconnected UDP socket of server
}
func (c *conn) resetBuffer() {
c.buffer = c.buffer[:0]
c.inboundBuffer.Reset()
c.inboundBuffer.Done()
}
func (c *conn) Read(p []byte) (n int, err error) {
if c.inboundBuffer.IsEmpty() {
n = copy(p, c.buffer)
c.buffer = c.buffer[n:]
if n == 0 && len(p) > 0 {
err = io.ErrShortBuffer
}
return
}
n, _ = c.inboundBuffer.Read(p)
if n == len(p) {
return
}
m := copy(p[n:], c.buffer)
n += m
c.buffer = c.buffer[m:]
return
}
func (c *conn) Next(n int) (buf []byte, err error) {
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + len(c.buffer); n > totalLen {
return nil, io.ErrShortBuffer
} else if n <= 0 {
n = totalLen
}
if c.inboundBuffer.IsEmpty() {
buf = c.buffer[:n]
c.buffer = c.buffer[n:]
return
}
buf = bsPool.Get(n)
_, err = c.Read(buf)
return
}
func (c *conn) Peek(n int) (buf []byte, err error) {
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + len(c.buffer); n > totalLen {
return nil, io.ErrShortBuffer
} else if n <= 0 {
n = totalLen
}
if c.inboundBuffer.IsEmpty() {
return c.buffer[:n], err
}
head, tail := c.inboundBuffer.Peek(n)
if len(head) == n {
return head, err
}
buf = bsPool.Get(n)[:0]
buf = append(buf, head...)
buf = append(buf, tail...)
if inBufferLen >= n {
return
}
remaining := n - inBufferLen
buf = append(buf, c.buffer[:remaining]...)
c.cache = buf
return
}
func (c *conn) Discard(n int) (int, error) {
if len(c.cache) > 0 {
bsPool.Put(c.cache)
c.cache = nil
}
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + len(c.buffer); n >= totalLen || n <= 0 {
c.resetBuffer()
return totalLen, nil
}
if c.inboundBuffer.IsEmpty() {
c.buffer = c.buffer[n:]
return n, nil
}
discarded, _ := c.inboundBuffer.Discard(n)
if discarded < inBufferLen {
return discarded, nil
}
remaining := n - inBufferLen
c.buffer = c.buffer[remaining:]
return n, nil
}
func (c *conn) Write(p []byte) (int, error) {
if c.isDatagram {
return c.sendTo(p, nil)
}
return c.write(p)
}
func (c *conn) SendTo(p []byte, addr net.Addr) (int, error) {
if !c.isDatagram {
return 0, errorx.ErrUnsupportedOp
}
sa := socket.NetAddrToSockaddr(addr)
if sa == nil {
return 0, errorx.ErrInvalidNetworkAddress
}
return c.sendTo(p, sa)
}
func (c *conn) Writev(bs [][]byte) (int, error) {
if c.isDatagram {
return 0, errorx.ErrUnsupportedOp
}
return c.writev(bs)
}
func (c *conn) ReadFrom(r io.Reader) (int64, error) {
return c.outboundBuffer.ReadFrom(r)
}
func (c *conn) WriteTo(w io.Writer) (n int64, err error) {
if !c.inboundBuffer.IsEmpty() {
if n, err = c.inboundBuffer.WriteTo(w); err != nil {
return
}
}
var m int
m, err = w.Write(c.buffer)
n += int64(m)
c.buffer = c.buffer[m:]
return
}
func (c *conn) Flush() error {
return c.loop.write(c)
}
func (c *conn) InboundBuffered() int {
return c.inboundBuffer.Buffered() + len(c.buffer)
}
func (c *conn) OutboundBuffered() int {
return c.outboundBuffer.Buffered()
}
func (c *conn) Context() any { return c.ctx }
func (c *conn) SetContext(ctx any) { c.ctx = ctx }
func (c *conn) LocalAddr() net.Addr { return c.localAddr }
func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
// Implementation of Socket interface
// func (c *conn) Gfd() gfd.GFD { return c.gfd }
func (c *conn) Fd() int { return c.fd }
func (c *conn) Dup() (fd int, err error) { return socket.Dup(c.fd) }
func (c *conn) SetReadBuffer(bytes int) error { return socket.SetRecvBuffer(c.fd, bytes) }
func (c *conn) SetWriteBuffer(bytes int) error { return socket.SetSendBuffer(c.fd, bytes) }
func (c *conn) SetLinger(sec int) error { return socket.SetLinger(c.fd, sec) }
func (c *conn) SetNoDelay(noDelay bool) error {
return socket.SetNoDelay(c.fd, func(b bool) int {
if b {
return 1
}
return 0
}(noDelay))
}
func (c *conn) SetKeepAlivePeriod(d time.Duration) error {
if c.proto != "tcp" {
return errorx.ErrUnsupportedOp
}
return socket.SetKeepAlivePeriod(c.fd, int(d.Seconds()))
}
func (c *conn) SetKeepAlive(enabled bool, idle, intvl time.Duration, cnt int) error {
if c.proto != "tcp" {
return errorx.ErrUnsupportedOp
}
return socket.SetKeepAlive(c.fd, enabled, int(idle.Seconds()), int(intvl.Seconds()), cnt)
}
func (c *conn) AsyncWrite(buf []byte, callback AsyncCallback) error {
if c.isDatagram {
_, err := c.sendTo(buf, nil)
// TODO: it will not go asynchronously with UDP, so calling a callback is needless,
// we may remove this branch in the future, please don't rely on the callback
// to do something important under UDP, if you're working with UDP, just call Conn.Write
// to send back your data.
if callback != nil {
_ = callback(nil, nil)
}
return err
}
return c.loop.poller.Trigger(queue.HighPriority, c.asyncWrite, &asyncWriteHook{callback, buf})
}
func (c *conn) AsyncWritev(bs [][]byte, callback AsyncCallback) error {
if c.isDatagram {
return errorx.ErrUnsupportedOp
}
return c.loop.poller.Trigger(queue.HighPriority, c.asyncWritev, &asyncWritevHook{callback, bs})
}
func (c *conn) Wake(callback AsyncCallback) error {
return c.loop.poller.Trigger(queue.LowPriority, func(_ any) (err error) {
err = c.loop.wake(c)
if callback != nil {
_ = callback(c, err)
}
return
}, nil)
}
func (c *conn) CloseWithCallback(callback AsyncCallback) error {
return c.loop.poller.Trigger(queue.LowPriority, func(_ any) (err error) {
err = c.loop.close(c, nil)
if callback != nil {
_ = callback(c, err)
}
return
}, nil)
}
func (c *conn) Close() error {
return c.loop.poller.Trigger(queue.LowPriority, func(_ any) (err error) {
err = c.loop.close(c, nil)
return
}, nil)
}
func (c *conn) EventLoop() EventLoop {
return c.loop
}
func (*conn) SetDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
func (*conn) SetReadDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
func (*conn) SetWriteDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
================================================
FILE: connection_windows.go
================================================
// Copyright (c) 2023 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 gnet
import (
"errors"
"io"
"net"
"os"
"syscall"
"time"
"golang.org/x/sys/windows"
"github.com/panjf2000/gnet/v2/pkg/buffer/elastic"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
bbPool "github.com/panjf2000/gnet/v2/pkg/pool/bytebuffer"
bsPool "github.com/panjf2000/gnet/v2/pkg/pool/byteslice"
"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
type netErr struct {
c *conn
err error
}
type tcpConn struct {
c *conn
b *bbPool.ByteBuffer
}
type udpConn struct {
c *conn
}
type openConn struct {
c *conn
cb func()
}
type conn struct {
pc net.PacketConn
ctx any // user-defined context
loop *eventloop // owner event-loop
buffer *bbPool.ByteBuffer // reuse memory of inbound data as a temporary buffer
cache []byte // temporary cache for the inbound data
rawConn net.Conn // original connection
localAddr net.Addr // local server addr
remoteAddr net.Addr // remote addr
inboundBuffer elastic.RingBuffer // buffer for data from the remote
}
func packTCPConn(c *conn, buf []byte) *tcpConn {
b := bbPool.Get()
_, _ = b.Write(buf)
return &tcpConn{c: c, b: b}
}
func unpackTCPConn(tc *tcpConn) *conn {
if tc.c.buffer == nil { // the connection has been closed
return nil
}
_, _ = tc.c.buffer.Write(tc.b.B)
bbPool.Put(tc.b)
tc.b = nil
return tc.c
}
func packUDPConn(c *conn, buf []byte) *udpConn {
_, _ = c.buffer.Write(buf)
return &udpConn{c}
}
func newStreamConn(el *eventloop, nc net.Conn, ctx any) (c *conn) {
return &conn{
ctx: ctx,
loop: el,
buffer: bbPool.Get(),
rawConn: nc,
localAddr: nc.LocalAddr(),
remoteAddr: nc.RemoteAddr(),
}
}
func (c *conn) release() {
c.ctx = nil
c.localAddr = nil
if c.rawConn != nil {
c.rawConn = nil
c.remoteAddr = nil
}
c.inboundBuffer.Done()
bbPool.Put(c.buffer)
c.buffer = nil
}
func newUDPConn(el *eventloop, pc net.PacketConn, rc net.Conn, localAddr, remoteAddr net.Addr, ctx any) *conn {
return &conn{
ctx: ctx,
pc: pc,
rawConn: rc,
loop: el,
buffer: bbPool.Get(),
localAddr: localAddr,
remoteAddr: remoteAddr,
}
}
func (c *conn) resetBuffer() {
c.buffer.Reset()
c.inboundBuffer.Reset()
c.inboundBuffer.Done()
}
func (c *conn) Read(p []byte) (n int, err error) {
if c.inboundBuffer.IsEmpty() {
n = copy(p, c.buffer.B)
c.buffer.B = c.buffer.B[n:]
if n == 0 && len(p) > 0 {
err = io.ErrShortBuffer
}
return
}
n, _ = c.inboundBuffer.Read(p)
if n == len(p) {
return
}
m := copy(p[n:], c.buffer.B)
n += m
c.buffer.B = c.buffer.B[m:]
return
}
func (c *conn) Next(n int) (buf []byte, err error) {
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + c.buffer.Len(); n > totalLen {
return nil, io.ErrShortBuffer
} else if n <= 0 {
n = totalLen
}
if c.inboundBuffer.IsEmpty() {
buf = c.buffer.B[:n]
c.buffer.B = c.buffer.B[n:]
return
}
buf = bsPool.Get(n)
_, err = c.Read(buf)
return
}
func (c *conn) Peek(n int) (buf []byte, err error) {
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + c.buffer.Len(); n > totalLen {
return nil, io.ErrShortBuffer
} else if n <= 0 {
n = totalLen
}
if c.inboundBuffer.IsEmpty() {
return c.buffer.B[:n], err
}
head, tail := c.inboundBuffer.Peek(n)
if len(head) == n {
return head, err
}
buf = bsPool.Get(n)[:0]
buf = append(buf, head...)
buf = append(buf, tail...)
if inBufferLen >= n {
return
}
remaining := n - inBufferLen
buf = append(buf, c.buffer.B[:remaining]...)
c.cache = buf
return
}
func (c *conn) Discard(n int) (int, error) {
if len(c.cache) > 0 {
bsPool.Put(c.cache)
c.cache = nil
}
inBufferLen := c.inboundBuffer.Buffered()
if totalLen := inBufferLen + c.buffer.Len(); n >= totalLen || n <= 0 {
c.resetBuffer()
return totalLen, nil
}
if c.inboundBuffer.IsEmpty() {
c.buffer.B = c.buffer.B[n:]
return n, nil
}
discarded, _ := c.inboundBuffer.Discard(n)
if discarded < inBufferLen {
return discarded, nil
}
remaining := n - inBufferLen
c.buffer.B = c.buffer.B[remaining:]
return n, nil
}
func (c *conn) Write(p []byte) (int, error) {
if c.rawConn == nil && c.pc == nil {
return 0, net.ErrClosed
}
if c.rawConn != nil {
return c.rawConn.Write(p)
}
return c.pc.WriteTo(p, c.remoteAddr)
}
func (c *conn) SendTo(p []byte, addr net.Addr) (int, error) {
if c.pc == nil {
return 0, errorx.ErrUnsupportedOp
}
if addr == nil {
return 0, errorx.ErrInvalidNetworkAddress
}
return c.pc.WriteTo(p, addr)
}
func (c *conn) Writev(bs [][]byte) (int, error) {
if c.pc != nil { // not available for UDP
return 0, errorx.ErrUnsupportedOp
}
if c.rawConn != nil {
bb := bbPool.Get()
defer bbPool.Put(bb)
for i := range bs {
_, _ = bb.Write(bs[i])
}
return c.rawConn.Write(bb.Bytes())
}
return 0, net.ErrClosed
}
func (c *conn) ReadFrom(r io.Reader) (int64, error) {
if c.rawConn != nil {
return io.Copy(c.rawConn, r)
}
return 0, net.ErrClosed
}
func (c *conn) WriteTo(w io.Writer) (n int64, err error) {
if !c.inboundBuffer.IsEmpty() {
if n, err = c.inboundBuffer.WriteTo(w); err != nil {
return
}
}
if c.buffer == nil {
return 0, nil
}
defer c.buffer.Reset()
return c.buffer.WriteTo(w)
}
func (c *conn) Flush() error {
return nil
}
func (c *conn) InboundBuffered() int {
if c.buffer == nil {
return 0
}
return c.inboundBuffer.Buffered() + c.buffer.Len()
}
func (c *conn) OutboundBuffered() int {
return 0
}
func (c *conn) Context() any { return c.ctx }
func (c *conn) SetContext(ctx any) { c.ctx = ctx }
func (c *conn) LocalAddr() net.Addr { return c.localAddr }
func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
func (c *conn) Fd() (fd int) {
if c.rawConn == nil {
return -1
}
rc, err := c.rawConn.(syscall.Conn).SyscallConn()
if err != nil {
return -1
}
if err := rc.Control(func(i uintptr) {
fd = int(i)
}); err != nil {
return -1
}
return
}
func (c *conn) Dup() (fd int, err error) {
if c.rawConn == nil && c.pc == nil {
return -1, net.ErrClosed
}
var (
sc syscall.Conn
ok bool
)
if c.rawConn != nil {
sc, ok = c.rawConn.(syscall.Conn)
} else {
sc, ok = c.pc.(syscall.Conn)
}
if !ok {
return -1, errors.New("failed to convert net.Conn to syscall.Conn")
}
rc, err := sc.SyscallConn()
if err != nil {
return -1, errors.New("failed to get syscall.RawConn from net.Conn")
}
var dupHandle windows.Handle
e := rc.Control(func(fd uintptr) {
process := windows.CurrentProcess()
err = windows.DuplicateHandle(
process,
windows.Handle(fd),
process,
&dupHandle,
0,
true,
windows.DUPLICATE_SAME_ACCESS,
)
})
if err != nil {
return -1, err
}
if e != nil {
return -1, e
}
return int(dupHandle), nil
}
func (c *conn) SetReadBuffer(bytes int) error {
if c.rawConn == nil && c.pc == nil {
return net.ErrClosed
}
if c.rawConn != nil {
return c.rawConn.(interface{ SetReadBuffer(int) error }).SetReadBuffer(bytes)
}
return c.pc.(interface{ SetReadBuffer(int) error }).SetReadBuffer(bytes)
}
func (c *conn) SetWriteBuffer(bytes int) error {
if c.rawConn == nil && c.pc == nil {
return net.ErrClosed
}
if c.rawConn != nil {
return c.rawConn.(interface{ SetWriteBuffer(int) error }).SetWriteBuffer(bytes)
}
return c.pc.(interface{ SetWriteBuffer(int) error }).SetWriteBuffer(bytes)
}
func (c *conn) SetLinger(sec int) error {
if c.rawConn == nil {
return net.ErrClosed
}
tc, ok := c.rawConn.(*net.TCPConn)
if !ok {
return errorx.ErrUnsupportedOp
}
return tc.SetLinger(sec)
}
func (c *conn) SetNoDelay(noDelay bool) error {
if c.rawConn == nil {
return net.ErrClosed
}
tc, ok := c.rawConn.(*net.TCPConn)
if !ok {
return errorx.ErrUnsupportedOp
}
return tc.SetNoDelay(noDelay)
}
func (c *conn) SetKeepAlivePeriod(d time.Duration) error {
return c.SetKeepAlive(d > 0, d, d/5, 5)
}
func (c *conn) SetKeepAlive(enabled bool, idle, intvl time.Duration, cnt int) error {
if c.rawConn == nil && c.pc == nil {
return net.ErrClosed
}
if c.pc != nil {
return errorx.ErrUnsupportedOp
}
tc, ok := c.rawConn.(*net.TCPConn)
if !ok {
return errorx.ErrUnsupportedOp
}
if enabled && (idle <= 0 || intvl <= 0 || cnt <= 0) {
return errors.New("invalid time duration")
}
if err := tc.SetKeepAlive(enabled); err != nil {
return err
}
if !enabled {
return nil
}
if err := tc.SetKeepAlivePeriod(idle); err != nil {
return err
}
if err := windows.SetsockoptInt(
windows.Handle(c.Fd()),
windows.IPPROTO_TCP,
windows.TCP_KEEPINTVL,
int(intvl.Seconds())); err != nil {
return os.NewSyscallError("setsockopt", err)
}
if err := windows.SetsockoptInt(
windows.Handle(c.Fd()),
windows.IPPROTO_TCP,
windows.TCP_KEEPCNT,
cnt); err != nil {
return os.NewSyscallError("setsockopt", err)
}
return nil
}
// Gfd return an uninitialized GFD which is not valid,
// this method is only implemented for compatibility, don't use it on Windows.
// func (c *conn) Gfd() gfd.GFD { return gfd.GFD{} }
func (c *conn) AsyncWrite(buf []byte, cb AsyncCallback) error {
fn := func() error {
_, err := c.Write(buf)
if cb != nil {
_ = cb(c, err)
}
return err
}
var err error
select {
case c.loop.ch <- fn:
default:
// If the event-loop channel is full, asynchronize this operation to avoid blocking the eventloop.
err = goroutine.DefaultWorkerPool.Submit(func() {
c.loop.ch <- fn
})
}
return err
}
func (c *conn) AsyncWritev(bs [][]byte, cb AsyncCallback) error {
if c.pc != nil {
return errorx.ErrUnsupportedOp
}
buf := bbPool.Get()
for _, b := range bs {
_, _ = buf.Write(b)
}
return c.AsyncWrite(buf.Bytes(), func(c Conn, err error) error {
defer bbPool.Put(buf)
if cb == nil {
return err
}
return cb(c, err)
})
}
func (c *conn) Wake(cb AsyncCallback) (err error) {
wakeFn := func() (err error) {
err = c.loop.wake(c)
if cb != nil {
_ = cb(c, err)
}
return
}
select {
case c.loop.ch <- wakeFn:
default:
// If the event-loop channel is full, asynchronize this operation to avoid blocking the eventloop.
err = goroutine.DefaultWorkerPool.Submit(func() {
c.loop.ch <- wakeFn
})
}
return
}
func (c *conn) Close() (err error) {
closeFn := func() error {
return c.loop.close(c, nil)
}
select {
case c.loop.ch <- closeFn:
default:
// If the event-loop channel is full, asynchronize this operation to avoid blocking the eventloop.
err = goroutine.DefaultWorkerPool.Submit(func() {
c.loop.ch <- closeFn
})
}
return
}
func (c *conn) CloseWithCallback(cb AsyncCallback) (err error) {
closeFn := func() (err error) {
err = c.loop.close(c, nil)
if cb != nil {
_ = cb(c, err)
}
return
}
select {
case c.loop.ch <- closeFn:
default:
// If the event-loop channel is full, asynchronize this operation to avoid blocking the eventloop.
err = goroutine.DefaultWorkerPool.Submit(func() {
c.loop.ch <- closeFn
})
}
return
}
func (c *conn) EventLoop() EventLoop {
return c.loop
}
func (*conn) SetDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
func (*conn) SetReadDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
func (*conn) SetWriteDeadline(_ time.Time) error {
return errorx.ErrUnsupportedOp
}
================================================
FILE: context.go
================================================
/*
* 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 gnet
import (
"context"
"net"
)
// contextKey is a key for Conn values in context.Context.
type contextKey struct{}
// NewContext returns a new context.Context that carries the value
// that will be attached to the Conn.
func NewContext(ctx context.Context, v any) context.Context {
return context.WithValue(ctx, contextKey{}, v)
}
// FromContext retrieves context value of the Conn stored in ctx, if any.
func FromContext(ctx context.Context) any {
return ctx.Value(contextKey{})
}
// connContextKey is a key for net.Conn values in context.Context.
type connContextKey struct{}
// NewNetConnContext returns a new context.Context that carries the net.Conn value.
func NewNetConnContext(ctx context.Context, c net.Conn) context.Context {
return context.WithValue(ctx, connContextKey{}, c)
}
// FromNetConnContext retrieves the net.Conn value from ctx, if any.
func FromNetConnContext(ctx context.Context) (net.Conn, bool) {
c, ok := ctx.Value(connContextKey{}).(net.Conn)
return c, ok
}
// netAddrContextKey is a key for net.Addr values in context.Context.
type netAddrContextKey struct{}
// NewNetAddrContext returns a new context.Context that carries the net.Addr value.
func NewNetAddrContext(ctx context.Context, a net.Addr) context.Context {
return context.WithValue(ctx, netAddrContextKey{}, a)
}
// FromNetAddrContext retrieves the net.Addr value from ctx, if any.
func FromNetAddrContext(ctx context.Context) (net.Addr, bool) {
a, ok := ctx.Value(netAddrContextKey{}).(net.Addr)
return a, ok
}
================================================
FILE: engine_unix.go
================================================
// Copyright (c) 2019 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"context"
"errors"
"strings"
"sync/atomic"
"time"
"golang.org/x/sync/errgroup"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
"github.com/panjf2000/gnet/v2/pkg/queue"
"github.com/panjf2000/gnet/v2/pkg/socket"
)
type engine struct {
listeners map[int]*listener // listeners for accepting incoming connections
opts *Options // options with engine
ingress *eventloop // main event-loop that monitors all listeners
eventLoops loadBalancer // event-loops for handling events
inShutdown atomic.Bool // whether the engine is in shutdown
turnOff context.CancelFunc
eventHandler EventHandler // user eventHandler
concurrency struct {
*errgroup.Group
ctx context.Context
}
}
func (eng *engine) isShutdown() bool {
return eng.inShutdown.Load()
}
// shutdown signals the engine to shut down.
func (eng *engine) shutdown(err error) {
if err != nil && !errors.Is(err, errorx.ErrEngineShutdown) {
eng.opts.Logger.Errorf("engine is being shutdown with error: %v", err)
}
// Cancel the context to stop the engine.
eng.turnOff()
}
func (eng *engine) closeEventLoops() {
eng.eventLoops.iterate(func(_ int, el *eventloop) bool {
for _, ln := range el.listeners {
ln.close()
}
_ = el.poller.Close()
return true
})
if eng.ingress != nil {
for _, ln := range eng.listeners {
ln.close()
}
err := eng.ingress.poller.Close()
if err != nil {
eng.opts.Logger.Errorf("failed to close poller when stopping engine: %v", err)
}
}
}
func (eng *engine) runEventLoops(ctx context.Context, numEventLoop int) error {
var el0 *eventloop
lns := eng.listeners
// Create loops locally and bind the listeners.
for i := 0; i < numEventLoop; i++ {
if i > 0 {
lns = make(map[int]*listener, len(eng.listeners))
for _, l := range eng.listeners {
ln, err := initListener(l.network, l.address, eng.opts)
if err != nil {
return err
}
lns[ln.fd] = ln
}
}
p, err := netpoll.OpenPoller()
if err != nil {
return err
}
el := new(eventloop)
el.listeners = lns
el.engine = eng
el.poller = p
el.buffer = make([]byte, eng.opts.ReadBufferCap)
el.connections.init()
el.eventHandler = eng.eventHandler
for _, ln := range lns {
if err = el.poller.AddRead(ln.packPollAttachment(el.accept), false); err != nil {
return err
}
}
eng.eventLoops.register(el)
// Start the ticker.
if eng.opts.Ticker && el.idx == 0 {
el0 = el
}
}
// Start event-loops in the background.
eng.eventLoops.iterate(func(_ int, el *eventloop) bool {
eng.concurrency.Go(el.run)
return true
})
if el0 != nil {
eng.concurrency.Go(func() error {
el0.ticker(ctx)
return nil
})
}
return nil
}
func (eng *engine) activateReactors(ctx context.Context, numEventLoop int) error {
for i := 0; i < numEventLoop; i++ {
p, err := netpoll.OpenPoller()
if err != nil {
return err
}
el := new(eventloop)
el.listeners = eng.listeners
el.engine = eng
el.poller = p
el.buffer = make([]byte, eng.opts.ReadBufferCap)
el.connections.init()
el.eventHandler = eng.eventHandler
eng.eventLoops.register(el)
}
// Start sub reactors in the background.
eng.eventLoops.iterate(func(_ int, el *eventloop) bool {
eng.concurrency.Go(el.orbit)
return true
})
p, err := netpoll.OpenPoller()
if err != nil {
return err
}
el := new(eventloop)
el.listeners = eng.listeners
el.idx = -1
el.engine = eng
el.poller = p
el.eventHandler = eng.eventHandler
for _, ln := range eng.listeners {
if err = el.poller.AddRead(ln.packPollAttachment(el.accept0), true); err != nil {
return err
}
}
eng.ingress = el
// Start the main reactor in the background.
eng.concurrency.Go(el.rotate)
// Start the ticker.
if eng.opts.Ticker {
eng.concurrency.Go(func() error {
eng.ingress.ticker(ctx)
return nil
})
}
return nil
}
func (eng *engine) start(ctx context.Context, numEventLoop int) error {
if eng.opts.ReusePort {
return eng.runEventLoops(ctx, numEventLoop)
}
return eng.activateReactors(ctx, numEventLoop)
}
func (eng *engine) stop(ctx context.Context, s Engine) {
// Wait on a signal for shutdown
<-ctx.Done()
eng.eventHandler.OnShutdown(s)
// Notify all event-loops to exit.
eng.eventLoops.iterate(func(i int, el *eventloop) bool {
err := el.poller.Trigger(queue.HighPriority, func(_ any) error { return errorx.ErrEngineShutdown }, nil)
if err != nil {
eng.opts.Logger.Errorf("failed to enqueue shutdown signal of high-priority for event-loop(%d): %v", i, err)
}
return true
})
if eng.ingress != nil {
err := eng.ingress.poller.Trigger(queue.HighPriority, func(_ any) error { return errorx.ErrEngineShutdown }, nil)
if err != nil {
eng.opts.Logger.Errorf("failed to enqueue shutdown signal of high-priority for main event-loop: %v", err)
}
}
if err := eng.concurrency.Wait(); err != nil {
eng.opts.Logger.Errorf("engine shutdown error: %v", err)
}
// Close all listeners and pollers of event-loops.
eng.closeEventLoops()
// Put the engine into the shutdown state.
eng.inShutdown.Store(true)
}
func run(eventHandler EventHandler, listeners []*listener, options *Options, addrs []string) error {
numEventLoop := determineEventLoops(options)
logging.Infof("Launching gnet with %d event-loops, listening on: %s",
numEventLoop, strings.Join(addrs, " | "))
lns := make(map[int]*listener, len(listeners))
for _, ln := range listeners {
lns[ln.fd] = ln
}
rootCtx, shutdown := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(rootCtx)
eng := engine{
listeners: lns,
opts: options,
turnOff: shutdown,
eventHandler: eventHandler,
concurrency: struct {
*errgroup.Group
ctx context.Context
}{eg, ctx},
}
switch options.LB {
case RoundRobin:
eng.eventLoops = new(roundRobinLoadBalancer)
case LeastConnections:
eng.eventLoops = new(leastConnectionsLoadBalancer)
case SourceAddrHash:
eng.eventLoops = new(sourceAddrHashLoadBalancer)
}
e := Engine{&eng}
switch eng.eventHandler.OnBoot(e) {
case None, Close:
case Shutdown:
return nil
}
if err := eng.start(ctx, numEventLoop); err != nil {
eng.closeEventLoops()
eng.opts.Logger.Errorf("gnet engine is stopping with error: %v", err)
return err
}
defer eng.stop(rootCtx, e)
for _, addr := range addrs {
allEngines.Store(addr, &eng)
}
return nil
}
func setKeepAlive(fd int, enabled bool, idle, intvl time.Duration, cnt int) error {
if intvl == 0 {
intvl = idle / 5
}
if cnt == 0 {
cnt = 5
}
return socket.SetKeepAlive(fd, enabled, int(idle.Seconds()), int(intvl.Seconds()), cnt)
}
/*
func (eng *engine) sendCmd(cmd *asyncCmd, urgent bool) error {
if !gfd.Validate(cmd.fd) {
return errors.ErrInvalidConn
}
el := eng.eventLoops.index(cmd.fd.EventLoopIndex())
if el == nil {
return errors.ErrInvalidConn
}
if urgent {
return el.poller.Trigger(queue.LowPriority, el.execCmd, cmd)
}
return el.poller.Trigger(el.execCmd, cmd)
}
*/
================================================
FILE: engine_windows.go
================================================
// Copyright (c) 2023 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 gnet
import (
"context"
"errors"
"strings"
"sync/atomic"
"golang.org/x/sync/errgroup"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
)
type engine struct {
listeners []*listener
opts *Options // options with engine
eventLoops loadBalancer // event-loops for handling events
inShutdown atomic.Bool // whether the engine is in shutdown
beingShutdown atomic.Bool // whether the engine is being shutdown
turnOff context.CancelFunc
eventHandler EventHandler // user eventHandler
concurrency struct {
*errgroup.Group
ctx context.Context
}
}
func (eng *engine) isShutdown() bool {
return eng.inShutdown.Load()
}
// shutdown signals the engine to shut down.
func (eng *engine) shutdown(err error) {
if err != nil && !errors.Is(err, errorx.ErrEngineShutdown) {
eng.opts.Logger.Errorf("engine is being shutdown with error: %v", err)
}
eng.turnOff()
eng.beingShutdown.Store(true)
}
func (eng *engine) closeEventLoops() {
eng.eventLoops.iterate(func(i int, el *eventloop) bool {
el.ch <- errorx.ErrEngineShutdown
return true
})
for _, ln := range eng.listeners {
ln.close()
}
}
func (eng *engine) start(ctx context.Context, numEventLoop int) error {
var el0 *eventloop
for i := 0; i < numEventLoop; i++ {
el := eventloop{
ch: make(chan any, 1024),
eng: eng,
connections: make(map[*conn]struct{}),
eventHandler: eng.eventHandler,
}
eng.eventLoops.register(&el)
eng.concurrency.Go(el.run)
if i == 0 && eng.opts.Ticker {
el0 = &el
}
}
if el0 != nil {
eng.concurrency.Go(func() error {
el0.ticker(ctx)
return nil
})
}
for _, ln := range eng.listeners {
l := ln
if l.pc != nil {
eng.concurrency.Go(func() error {
return eng.ListenUDP(l.pc)
})
} else {
eng.concurrency.Go(func() error {
return eng.listenStream(l.ln)
})
}
}
return nil
}
func (eng *engine) stop(ctx context.Context, engine Engine) {
<-ctx.Done()
eng.eventHandler.OnShutdown(engine)
eng.closeEventLoops()
if err := eng.concurrency.Wait(); err != nil && !errors.Is(err, errorx.ErrEngineShutdown) {
eng.opts.Logger.Errorf("engine shutdown error: %v", err)
}
eng.inShutdown.Store(true)
}
func run(eventHandler EventHandler, listeners []*listener, options *Options, addrs []string) error {
numEventLoop := determineEventLoops(options)
logging.Infof("Launching gnet with %d event-loops, listening on: %s",
numEventLoop, strings.Join(addrs, " | "))
rootCtx, shutdown := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(rootCtx)
eng := engine{
opts: options,
listeners: listeners,
turnOff: shutdown,
eventHandler: eventHandler,
concurrency: struct {
*errgroup.Group
ctx context.Context
}{eg, ctx},
}
switch options.LB {
case RoundRobin:
eng.eventLoops = new(roundRobinLoadBalancer)
// If there are more than one listener, we can't use roundRobinLoadBalancer because
// it's not concurrency-safe, replace it with leastConnectionsLoadBalancer.
if len(listeners) > 1 {
eng.eventLoops = new(leastConnectionsLoadBalancer)
}
case LeastConnections:
eng.eventLoops = new(leastConnectionsLoadBalancer)
case SourceAddrHash:
eng.eventLoops = new(sourceAddrHashLoadBalancer)
}
engine := Engine{eng: &eng}
switch eventHandler.OnBoot(engine) {
case None, Close:
case Shutdown:
return nil
}
if err := eng.start(ctx, numEventLoop); err != nil {
eng.opts.Logger.Errorf("gnet engine is stopping with error: %v", err)
return err
}
defer eng.stop(rootCtx, engine)
for _, addr := range addrs {
allEngines.Store(addr, &eng)
}
return nil
}
/*
func (eng *engine) sendCmd(_ *asyncCmd, _ bool) error {
return errorx.ErrUnsupportedOp
}
*/
================================================
FILE: eventloop_unix.go
================================================
// Copyright (c) 2019 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.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
gio "github.com/panjf2000/gnet/v2/pkg/io"
"github.com/panjf2000/gnet/v2/pkg/logging"
"github.com/panjf2000/gnet/v2/pkg/netpoll"
"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
"github.com/panjf2000/gnet/v2/pkg/queue"
"github.com/panjf2000/gnet/v2/pkg/socket"
)
type eventloop struct {
listeners map[int]*listener // listeners
idx int // loop index in the engine loops list
engine *engine // engine in loop
poller *netpoll.Poller // epoll or kqueue
buffer []byte // read packet buffer whose capacity is set by user, default value is 64KB
connections connMatrix // loop connections storage
eventHandler EventHandler // user eventHandler
}
func (el *eventloop) Register(ctx context.Context, addr net.Addr) (<-chan RegisteredResult, error) {
if el.engine.isShutdown() {
return nil, errorx.ErrEngineInShutdown
}
if addr == nil {
return nil, errorx.ErrInvalidNetworkAddress
}
return el.enroll(nil, addr, FromContext(ctx))
}
func (el *eventloop) Enroll(ctx context.Context, c net.Conn) (<-chan RegisteredResult, error) {
if el.engine.isShutdown() {
return nil, errorx.ErrEngineInShutdown
}
if c == nil {
return nil, errorx.ErrInvalidNetConn
}
return el.enroll(c, c.RemoteAddr(), FromContext(ctx))
}
func (el *eventloop) Execute(ctx context.Context, runnable Runnable) error {
if el.engine.isShutdown() {
return errorx.ErrEngineInShutdown
}
if runnable == nil {
return errorx.ErrNilRunnable
}
return el.poller.Trigger(queue.LowPriority, func(any) error {
return runnable.Run(ctx)
}, nil)
}
func (el *eventloop) Schedule(context.Context, Runnable, time.Duration) error {
return errorx.ErrUnsupportedOp
}
func (el *eventloop) Close(c Conn) error {
return el.close(c.(*conn), nil)
}
func (el *eventloop) getLogger() logging.Logger {
return el.engine.opts.Logger
}
func (el *eventloop) countConn() int32 {
return el.connections.loadCount()
}
func (el *eventloop) closeConns() {
// Close loops and all outstanding connections
el.connections.iterate(func(c *conn) bool {
_ = el.close(c, nil)
return true
})
}
type connWithCallback struct {
c *conn
cb func()
}
func (el *eventloop) enroll(c net.Conn, addr net.Addr, ctx any) (resCh chan RegisteredResult, err error) {
resCh = make(chan RegisteredResult, 1)
err = goroutine.DefaultWorkerPool.Submit(func() {
defer close(resCh)
var err error
if c == nil {
if c, err = net.Dial(addr.Network(), addr.String()); err != nil {
resCh <- RegisteredResult{Err: err}
return
}
}
defer c.Close() //nolint:errcheck
sc, ok := c.(syscall.Conn)
if !ok {
resCh <- RegisteredResult{
Err: fmt.Errorf("failed to assert syscall.Conn from net.Conn: %s", addr.String()),
}
return
}
rc, err := sc.SyscallConn()
if err != nil {
resCh <- RegisteredResult{Err: err}
return
}
var dupFD int
err1 := rc.Control(func(fd uintptr) {
dupFD, err = socket.Dup(int(fd))
})
if err != nil {
resCh <- RegisteredResult{Err: err}
return
}
if err1 != nil {
resCh <- RegisteredResult{Err: err1}
return
}
var (
sockAddr unix.Sockaddr
gc *conn
)
switch c.(type) {
case *net.UnixConn:
sockAddr, _, _, err = socket.GetUnixSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
resCh <- RegisteredResult{Err: err}
return
}
ua := c.LocalAddr().(*net.UnixAddr)
ua.Name = c.RemoteAddr().String() + "." + strconv.Itoa(dupFD)
gc = newStreamConn("unix", dupFD, el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.TCPConn:
sockAddr, _, _, _, err = socket.GetTCPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
resCh <- RegisteredResult{Err: err}
return
}
gc = newStreamConn("tcp", dupFD, el, sockAddr, c.LocalAddr(), c.RemoteAddr())
case *net.UDPConn:
sockAddr, _, _, _, err = socket.GetUDPSockAddr(c.RemoteAddr().Network(), c.RemoteAddr().String())
if err != nil {
resCh <- RegisteredResult{Err: err}
return
}
gc = newUDPConn(dupFD, el, c.LocalAddr(), sockAddr, true)
default:
resCh <- RegisteredResult{Err: fmt.Errorf("unknown type of conn: %T", c)}
return
}
gc.ctx = ctx
connOpened := make(chan struct{})
ccb := &connWithCallback{c: gc, cb: func() {
close(connOpened)
}}
if err := el.poller.Trigger(queue.LowPriority, el.register, ccb); err != nil {
gc.Close() //nolint:errcheck
resCh <- RegisteredResult{Err: err}
return
}
<-connOpened
resCh <- RegisteredResult{Conn: gc}
})
return
}
func (el *eventloop) register(a any) error {
c, ok := a.(*conn)
if !ok {
ccb := a.(*connWithCallback)
c = ccb.c
defer ccb.cb()
}
return el.register0(c)
}
func (el *eventloop) register0(c *conn) error {
addEvents := el.poller.AddRead
if el.engine.opts.EdgeTriggeredIO {
addEvents = el.poller.AddReadWrite
}
if err := addEvents(&c.pollAttachment, el.engine.opts.EdgeTriggeredIO); err != nil {
_ = unix.Close(c.fd)
c.release()
return err
}
el.connections.addConn(c, el.idx)
if c.isDatagram && c.remote != nil {
return nil
}
return el.open(c)
}
func (el *eventloop) open(c *conn) error {
c.opened = true
out, action := el.eventHandler.OnOpen(c)
if out != nil {
if err := c.open(out); err != nil {
return err
}
}
if !c.outboundBuffer.IsEmpty() && !el.engine.opts.EdgeTriggeredIO {
if err := el.poller.ModReadWrite(&c.pollAttachment, false); err != nil {
return err
}
}
return el.handleAction(c, action)
}
func (el *eventloop) read0(a any) error {
return el.read(a.(*conn))
}
func (el *eventloop) read(c *conn) error {
if !c.opened {
return nil
}
var recv int
isET := el.engine.opts.EdgeTriggeredIO
chunk := el.engine.opts.EdgeTriggeredIOChunk
loop:
n, err := unix.Read(c.fd, el.buffer)
if err != nil || n == 0 {
if err == unix.EAGAIN {
return nil
}
if n == 0 {
err = io.EOF
}
return el.close(c, os.NewSyscallError("read", err))
}
recv += n
c.buffer = el.buffer[:n]
action := el.eventHandler.OnTraffic(c)
switch action {
case None:
case Close:
return el.close(c, nil)
case Shutdown:
return errorx.ErrEngineShutdown
}
_, _ = c.inboundBuffer.Write(c.buffer)
c.buffer = c.buffer[:0]
if c.isEOF || (isET && recv < chunk) {
goto loop
}
// To prevent infinite reading in ET mode and starving other events,
// we need to set up threshold for the maximum read bytes per connection
// on each event-loop. If the threshold is reached and there are still
// unread data in the socket buffer, we must issue another read event manually.
if isET && n == len(el.buffer) {
return el.poller.Trigger(queue.LowPriority, el.read0, c)
}
return nil
}
func (el *eventloop) write0(a any) error {
return el.write(a.(*conn))
}
// The default value of UIO_MAXIOV/IOV_MAX is 1024 on Linux and most BSD-like OSs.
const iovMax = 1024
func (el *eventloop) write(c *conn) error {
if c.outboundBuffer.IsEmpty() {
return nil
}
isET := el.engine.opts.EdgeTriggeredIO
chunk := el.engine.opts.EdgeTriggeredIOChunk
var (
n int
sent int
err error
)
loop:
iov, _ := c.outboundBuffer.Peek(-1)
if len(iov) > 1 {
if len(iov) > iovMax {
iov = iov[:iovMax]
}
n, err = gio.Writev(c.fd, iov)
} else {
n, err = unix.Write(c.fd, iov[0])
}
_, _ = c.outboundBuffer.Discard(n)
switch err {
case nil:
case unix.EAGAIN:
return nil
default:
return el.close(c, os.NewSyscallError("write", err))
}
sent += n
if isET && !c.outboundBuffer.IsEmpty() && sent < chunk {
goto loop
}
// All data have been sent, it's no need to monitor the writable events for LT mode,
// remove the writable event from poller to help the future event-loops if necessary.
if !isET && c.outboundBuffer.IsEmpty() {
return el.poller.ModRead(&c.pollAttachment, false)
}
// To prevent infinite writing in ET mode and starving other events,
// we need to set up threshold for the maximum write bytes per connection
// on each event-loop. If the threshold is reached and there are still
// pending data to write, we must issue another write event manually.
if isET && !c.outboundBuffer.IsEmpty() {
return el.poller.Trigger(queue.HighPriority, el.write0, c)
}
return nil
}
func (el *eventloop) close(c *conn, err error) error {
if !c.opened || el.connections.getConn(c.fd) == nil {
return nil // ignore stale connections
}
el.connections.delConn(c)
action := el.eventHandler.OnClose(c, err)
// Send residual data in buffer back to the remote before actually closing the connection.
for !c.outboundBuffer.IsEmpty() {
iov, _ := c.outboundBuffer.Peek(0)
if len(iov) > iovMax {
iov = iov[:iovMax]
}
n, err := gio.Writev(c.fd, iov)
if err != nil {
break
}
_, _ = c.outboundBuffer.Discard(n)
}
c.release()
var errStr strings.Builder
err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
if err0 != nil {
err0 = fmt.Errorf("failed to delete fd=%d from poller in event-loop(%d): %v",
c.fd, el.idx, os.NewSyscallError("delete", err0))
errStr.WriteString(err0.Error())
errStr.WriteString(" | ")
}
if err1 != nil {
err1 = fmt.Errorf("failed to close fd=%d in event-loop(%d): %v",
c.fd, el.idx, os.NewSyscallError("close", err1))
errStr.WriteString(err1.Error())
}
if errStr.Len() > 0 {
return errors.New(strings.TrimSuffix(errStr.String(), " | "))
}
return el.handleAction(c, action)
}
func (el *eventloop) wake(c *conn) error {
if !c.opened || el.connections.getConn(c.fd) == nil {
return nil // ignore stale connections
}
action := el.eventHandler.OnTraffic(c)
return el.handleAction(c, action)
}
func (el *eventloop) ticker(ctx context.Context) {
var (
action Action
delay time.Duration
timer *time.Timer
)
defer func() {
if timer != nil {
timer.Stop()
}
}()
for {
delay, action = el.eventHandler.OnTick()
switch action {
case None, Close:
case Shutdown:
// It seems reasonable to mark this as low-priority, waiting for some tasks like asynchronous writes
// to finish up before shutting down the service.
err := el.poller.Trigger(queue.LowPriority, func(_ any) error { return errorx.ErrEngineShutdown }, nil)
el.getLogger().Debugf("failed to enqueue shutdown signal of high-priority for event-loop(%d): %v", el.idx, err)
}
if timer == nil {
timer = time.NewTimer(delay)
} else {
timer.Reset(delay)
}
select {
case <-ctx.Done():
el.getLogger().Debugf("stopping ticker in event-loop(%d) from Engine, error:%v", el.idx, ctx.Err())
return
case <-timer.C:
}
}
}
func (el *eventloop) readUDP(fd int, _ netpoll.IOEvent, _ netpoll.IOFlags) error {
n, sa, err := unix.Recvfrom(fd, el.buffer, 0)
if err != nil {
if err == unix.EAGAIN {
return nil
}
return fmt.Errorf("failed to read UDP packet from fd=%d in event-loop(%d), %v",
fd, el.idx, os.NewSyscallError("recvfrom", err))
}
var c *conn
if ln, ok := el.listeners[fd]; ok {
c = newUDPConn(fd, el, ln.addr, sa, false)
} else {
c = el.connections.getConn(fd)
}
c.buffer = el.buffer[:n]
action := el.eventHandler.OnTraffic(c)
if c.remote != nil {
c.release()
}
if action == Shutdown {
return errorx.ErrEngineShutdown
}
return nil
}
func (el *eventloop) handleAction(c *conn, action Action) error {
switch action {
case None:
return nil
case Close:
return el.close(c, nil)
case Shutdown:
return errorx.ErrEngineShutdown
default:
return nil
}
}
/*
func (el *eventloop) execCmd(a any) (err error) {
cmd := a.(*asyncCmd)
c := el.connections.getConnByGFD(cmd.fd)
if c == nil || c.gfd != cmd.fd {
return errorx.ErrInvalidConn
}
defer func() {
if cmd.cb != nil {
_ = cmd.cb(c, err)
}
}()
switch cmd.typ {
case asyncCmdClose:
return el.close(c, nil)
case asyncCmdWake:
return el.wake(c)
case asyncCmdWrite:
_, err = c.Write(cmd.param.([]byte))
case asyncCmdWritev:
_, err = c.Writev(cmd.param.([][]byte))
default:
return errorx.ErrUnsupportedOp
}
return
}
*/
================================================
FILE: eventloop_unix_test.go
================================================
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package gnet
import (
"context"
"net"
"runtime"
"runtime/debug"
"sync/atomic"
"testing"
"time"
"golang.org/x/sys/unix"
"github.com/stretchr/testify/assert"
goPool "github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
func (lb *roundRobinLoadBalancer) register(el *eventloop) {
lb.baseLoadBalancer.register(el)
registerInitConn(el)
}
func (lb *leastConnectionsLoadBalancer) register(el *eventloop) {
lb.baseLoadBalancer.register(el)
registerInitConn(el)
}
func (lb *sourceAddrHashLoadBalancer) register(el *eventloop) {
lb.baseLoadBalancer.register(el)
registerInitConn(el)
}
func registerInitConn(el *eventloop) {
for i := 0; i < int(atomic.LoadInt32(&nowEventLoopInitConn)); i++ {
c := newStreamConn("tcp", i, el, &unix.SockaddrInet4{}, &net.TCPAddr{}, &net.TCPAddr{})
el.connections.addConn(c, el.idx)
}
}
// nowEventLoopInitConn initializes the number of conn fake data, must be set to 0 after use.
var (
nowEventLoopInitConn int32
testBigGC = false
)
func BenchmarkGC4El100k(b *testing.B) {
oldGc := debug.SetGCPercent(-1)
ts1 := benchServeGC(b, "tcp", ":0", true, 4, 100000)
b.Run("Run-4-eventloop-100000", func(b *testing.B) {
for i := 0; i < b.N; i++ {
runtime.GC()
}
})
_ = ts1.eng.Stop(context.Background())
debug.SetGCPercent(oldGc)
}
func BenchmarkGC4El200k(b *testing.B) {
oldGc := debug.SetGCPercent(-1)
ts1 := benchServeGC(b, "tcp", ":0", true, 4, 200000)
b.Run("Run-4-eventloop-200000", func(b *testing.B) {
for i := 0; i < b.N; i++ {
runtime.GC()
}
})
_ = ts1.eng.Stop(context.Background())
debug.SetGCPercent(oldGc)
}
func BenchmarkGC4El500k(b *testing.B) {
oldGc := debug.SetGCPercent(-1)
ts1 := benchServeGC(b, "tcp", ":0", true, 4, 500000)
b.Run("Run-4-eventloop-500000", func(b *testing.B) {
for i := 0; i < b.N; i++ {
runtime.GC()
}
})
_ = ts1.eng.Stop(context.Background())
debug.SetGCPercent(oldGc)
}
func benchServeGC(b *testing.B, network, addr string, async bool, elNum int, initConnCount int32) *benchmarkServerGC {
ts := &benchmarkServerGC{
tester: b,
network: network,
addr: addr,
async: async,
elNum: elNum,
initOk: make(chan struct{}),
initConnCount: initConnCount,
}
nowEventLoopInitConn = initConnCount
_ = goPool.DefaultWorkerPool.Submit(func() {
err := Run(ts,
network+"://"+addr,
WithLockOSThread(async),
WithNumEventLoop(elNum),
WithTCPKeepAlive(time.Minute),
WithTCPNoDelay(TCPDelay))
assert.NoError(b, err)
nowEventLoopInitConn = 0
})
<-ts.initOk
return ts
}
type benchmarkServerGC struct {
*BuiltinEventEngine
tester *testing.B
eng Engine
network string
addr string
async bool
elNum int
initConnCount int32
initOk chan struct{}
}
func (s *benchmarkServerGC) OnBoot(eng Engine) (action Action) {
s.eng = eng
_ = goPool.DefaultWorkerPool.Submit(func() {
for s.eng.eng.eventLoops.len() != s.elNum || s.eng.CountConnections() != s.elNum*int(s.initConnCount) {
time.Sleep(time.Millisecond)
}
close(s.initOk)
})
return
}
// TestServeGC generate fake data asynchronously, if you need to test, manually open the comment.
func TestServeGC(t *testing.T) {
t.Run("gc-loop", func(t *testing.T) {
t.Run("1-loop-10000", func(t *testing.T) {
if testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 1, 10000)
})
t.Run("1-loop-100000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 1, 100000)
})
t.Run("1-loop-1000000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 1, 1000000)
})
t.Run("2-loop-10000", func(t *testing.T) {
if testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 2, 10000)
})
t.Run("2-loop-100000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 2, 100000)
})
t.Run("2-loop-1000000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 2, 1000000)
})
t.Run("4-loop-10000", func(t *testing.T) {
if testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 4, 10000)
})
t.Run("4-loop-100000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 4, 100000)
})
t.Run("4-loop-1000000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 4, 1000000)
})
t.Run("16-loop-10000", func(t *testing.T) {
if testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 16, 10000)
})
t.Run("16-loop-100000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 16, 100000)
})
t.Run("16-loop-1000000", func(t *testing.T) {
if !testBigGC {
t.Skipf("Skip when testBigGC=%t", testBigGC)
}
testServeGC(t, "tcp", ":0", true, true, 16, 1000000)
})
})
}
func testServeGC(t *testing.T, network, addr string, multicore, async bool, elNum int, initConnCount int32) {
ts := &testServerGC{
tester: t,
network: network,
addr: addr,
multicore: multicore,
async: async,
elNum: elNum,
}
nowEventLoopInitConn = initConnCount
err := Run(ts,
network+"://"+addr,
WithLockOSThread(async),
WithMulticore(multicore),
WithNumEventLoop(elNum),
WithTCPKeepAlive(time.Minute),
WithTCPNoDelay(TCPDelay))
assert.NoError(t, err)
nowEventLoopInitConn = 0
}
type testServerGC struct {
*BuiltinEventEngine
tester *testing.T
eng Engine
network string
addr string
multicore bool
async bool
elNum int
}
func (s *testServerGC) OnBoot(eng Engine) (action Action) {
s.eng = eng
gcSecs := 5
if testBigGC {
gcSecs = 10
}
err := goPool.DefaultWorkerPool.Submit(func() {
s.GC(gcSecs)
})
assert.NoError(s.tester, err)
return
}
func (s *testServerGC) GC(secs int) {
defer func() {
_ = s.eng.Stop(context.Background())
runtime.GC()
}()
var gcAllTime, gcAllCount time.Duration
gcStart := time.Now()
for range time.Tick(time.Second) {
gcAllCount++
now := time.Now()
runtime.GC()
gcTime := time.Since(now)
gcAllTime += gcTime
s.tester.Log(s.tester.Name(), s.network, "server gc:", gcTime, "average gc time:", gcAllTime/gcAllCount)
if time.Since(gcStart) >= time.Second*time.Duration(secs) {
break
}
}
}
================================================
FILE: eventloop_windows.go
================================================
// Copyright (c) 2023 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 gnet
import (
"context"
"errors"
"fmt"
"net"
"runtime"
"sync/atomic"
"time"
errorx "github.com/panjf2000/gnet/v2/pkg/errors"
"github.com/panjf2000/gnet/v2/pkg/logging"
"github.com/panjf2000/gnet/v2/pkg/pool/goroutine"
)
type eventloop struct {
ch chan any // channel for event-loop
idx int // index of event-loop in event-loops
eng *engine // engine in loop
connCount int32 // number of active connections in event-loop
connections map[*conn]struct{} // TCP connection map: fd -> conn
eventHandler EventHandler // user eventHandler
}
func (el *eventloop) Register(ctx context.Context, addr net.Addr) (<-chan RegisteredResult, error) {
if el.eng.isShutdown() {
return nil, errorx.ErrEngineInShutdown
}
if addr == nil {
return nil, errorx.ErrInvalidNetworkAddress
}
return el.enroll(nil, addr, FromContext(ctx))
}
func (el *eventloop) Enroll(ctx context.Context, c net.Conn) (<-chan RegisteredResult, error) {
if el.eng.isShutdown() {
return nil, errorx.ErrEngineInShutdown
}
if c == nil {
return nil, errorx.ErrInvalidNetConn
}
return el.enroll(c, c.RemoteAddr(), FromContext(ctx))
}
func (el *eventloop) Execute(ctx context.Context, runnable Runnable) error {
if el.eng.isShutdown() {
return errorx.ErrEngineInShutdown
}
if runnable == nil {
return errorx.ErrNilRunnable
}
return goroutine.DefaultWorkerPool.Submit(func() {
el.ch <- func() error {
return runnable.Run(ctx)
}
})
}
func (el *eventloop) Schedule(context.Context, Runnable, time.Duration) error {
return errorx.ErrUnsupportedOp
}
func (el *eventloop) Close(c Conn) error {
return el.close(c.(*conn), nil)
}
func (el *eventloop) getLogger() logging.Logger {
return el.eng.opts.Logger
}
func (el *eventloop) enroll(c net.Conn, addr net.Addr, ctx any) (resCh chan RegisteredResult, err error) {
resCh = make(chan RegisteredResult, 1)
err = goroutine.DefaultWorkerPool.Submit(func() {
defer close(resCh)
var err error
if c == nil {
if c, err = net.Dial(addr.Network(), addr.String()); err != nil {
resCh <- RegisteredResult{Err: err}
return
}
}
connOpened := make(chan struct{})
var gc *conn
switch addr.Network() {
case "tcp", "tcp4", "tcp6", "unix":
gc = newStreamConn(el, c, ctx)
el.ch <- &openConn{c: gc, cb: func() { close(connOpened) }}
goroutine.DefaultWorkerPool.Submit(func() {
var buffer [0x10000]b
gitextract_0awrewzb/ ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yaml │ │ ├── feature-request.yaml │ │ └── question.yaml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── release-drafter.yml │ └── workflows/ │ ├── codeql.yml │ ├── cross-compile-bsd.yml │ ├── gh-translator.yml │ ├── pull-request.yml │ ├── release-drafter.yml │ ├── stale-bot.yml │ ├── test.yml │ ├── test_gc_opt.yml │ ├── test_poll_opt.yml │ └── test_poll_opt_gc_opt.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_ZH.md ├── acceptor_unix.go ├── acceptor_windows.go ├── client_test.go ├── client_unix.go ├── client_windows.go ├── conn_map.go ├── conn_matrix.go ├── conn_matrix_test.go ├── connection_bsd.go ├── connection_linux.go ├── connection_unix.go ├── connection_windows.go ├── context.go ├── engine_unix.go ├── engine_windows.go ├── eventloop_unix.go ├── eventloop_unix_test.go ├── eventloop_windows.go ├── gnet.go ├── gnet_test.go ├── go.mod ├── go.sum ├── internal/ │ └── gfd/ │ └── gfd.go ├── listener_unix.go ├── listener_windows.go ├── load_balancer.go ├── options.go ├── os_unix_test.go ├── os_windows_test.go ├── pkg/ │ ├── bs/ │ │ └── bs.go │ ├── buffer/ │ │ ├── elastic/ │ │ │ ├── elastic_buffer_test.go │ │ │ ├── elastic_ring_buffer.go │ │ │ └── elastic_ring_list_buffer.go │ │ ├── linkedlist/ │ │ │ ├── linked_list_buffer.go │ │ │ └── llbuffer_test.go │ │ └── ring/ │ │ ├── ring_buffer.go │ │ └── ring_buffer_test.go │ ├── errors/ │ │ └── errors.go │ ├── io/ │ │ ├── io.go │ │ ├── io_bsd.go │ │ └── io_linux.go │ ├── logging/ │ │ └── logger.go │ ├── math/ │ │ ├── math.go │ │ └── math_test.go │ ├── netpoll/ │ │ ├── defs_bsd_32bit.go │ │ ├── defs_bsd_64bit.go │ │ ├── defs_linux.go │ │ ├── defs_linux_386.go │ │ ├── defs_linux_amd64.go │ │ ├── defs_linux_arm.go │ │ ├── defs_linux_arm64.go │ │ ├── defs_linux_mips64x.go │ │ ├── defs_linux_mipsx.go │ │ ├── defs_linux_ppc64.go │ │ ├── defs_linux_ppc64le.go │ │ ├── defs_linux_riscv64.go │ │ ├── defs_linux_s390x.go │ │ ├── defs_poller.go │ │ ├── defs_poller_bsd.go │ │ ├── defs_poller_epoll.go │ │ ├── defs_poller_kqueue.go │ │ ├── defs_poller_netbsd.go │ │ ├── example_test.go │ │ ├── netpoll.go │ │ ├── poller_epoll_default.go │ │ ├── poller_epoll_ultimate.go │ │ ├── poller_kqueue_default.go │ │ ├── poller_kqueue_ultimate.go │ │ ├── poller_kqueue_wakeup.go │ │ ├── poller_kqueue_wakeup1.go │ │ ├── poller_unix_ultimate.go │ │ ├── syscall_epoll_generic_linux.go │ │ ├── syscall_epoll_linux.go │ │ ├── syscall_epoll_riscv64_arm64_linux.go │ │ └── syscall_errors_linux.go │ ├── pool/ │ │ ├── bytebuffer/ │ │ │ └── bytebuffer.go │ │ ├── byteslice/ │ │ │ ├── byteslice.go │ │ │ └── byteslice_test.go │ │ ├── goroutine/ │ │ │ └── goroutine.go │ │ └── ringbuffer/ │ │ └── ringbuffer.go │ ├── queue/ │ │ ├── lock_free_queue.go │ │ ├── queue.go │ │ └── queue_test.go │ └── socket/ │ ├── fd_unix.go │ ├── sock_bsd.go │ ├── sock_cloexec.go │ ├── sock_linux.go │ ├── sock_posix.go │ ├── sockaddr.go │ ├── socket.go │ ├── sockopts_bsd.go │ ├── sockopts_darwin.go │ ├── sockopts_freebsd.go │ ├── sockopts_linux.go │ ├── sockopts_openbsd.go │ ├── sockopts_posix.go │ ├── sockopts_unix.go │ ├── sockopts_unix1.go │ ├── sys_cloexec.go │ ├── tcp_socket.go │ ├── udp_socket.go │ └── unix_socket.go ├── reactor_default.go └── reactor_ultimate.go
SYMBOL INDEX (865 symbols across 93 files)
FILE: acceptor_unix.go
method accept0 (line 30) | func (el *eventloop) accept0(fd int, _ netpoll.IOEvent, _ netpoll.IOFlag...
method accept (line 77) | func (el *eventloop) accept(fd int, ev netpoll.IOEvent, flags netpoll.IO...
FILE: acceptor_windows.go
method listenStream (line 26) | func (eng *engine) listenStream(ln net.Listener) (err error) {
method ListenUDP (line 63) | func (eng *engine) ListenUDP(pc net.PacketConn) (err error) {
FILE: client_test.go
type connHandler (line 26) | type connHandler struct
type clientEvents (line 32) | type clientEvents struct
method OnBoot (line 38) | func (ev *clientEvents) OnBoot(e Engine) Action {
method OnOpen (line 53) | func (ev *clientEvents) OnOpen(Conn) (out []byte, action Action) {
method OnClose (line 58) | func (ev *clientEvents) OnClose(Conn, error) Action {
method OnTraffic (line 67) | func (ev *clientEvents) OnTraffic(c Conn) (action Action) {
method OnTick (line 84) | func (ev *clientEvents) OnTick() (delay time.Duration, action Action) {
method OnShutdown (line 89) | func (ev *clientEvents) OnShutdown(e Engine) {
function TestClient (line 101) | func TestClient(t *testing.T) {
type testClient (line 364) | type testClient struct
method OnBoot (line 381) | func (s *testClient) OnBoot(eng Engine) (action Action) {
method OnOpen (line 386) | func (s *testClient) OnOpen(c Conn) (out []byte, action Action) {
method OnClose (line 394) | func (s *testClient) OnClose(c Conn, err error) (action Action) {
method OnShutdown (line 411) | func (s *testClient) OnShutdown(Engine) {
method OnTraffic (line 417) | func (s *testClient) OnTraffic(c Conn) (action Action) {
method OnTick (line 470) | func (s *testClient) OnTick() (delay time.Duration, action Action) {
function runClient (line 494) | func runClient(t *testing.T, network, addr string, conf *testConf) {
function startGnetClient (line 535) | func startGnetClient(t *testing.T, cli *Client, network, addr string, mu...
type clientEventsForWake (line 587) | type clientEventsForWake struct
method OnBoot (line 593) | func (ev *clientEventsForWake) OnBoot(_ Engine) Action {
method OnTraffic (line 598) | func (ev *clientEventsForWake) OnTraffic(c Conn) (action Action) {
type serverEventsForWake (line 633) | type serverEventsForWake struct
method OnOpen (line 643) | func (ev *serverEventsForWake) OnOpen(_ Conn) ([]byte, Action) {
method OnClose (line 648) | func (ev *serverEventsForWake) OnClose(_ Conn, _ error) Action {
method OnTick (line 655) | func (ev *serverEventsForWake) OnTick() (time.Duration, Action) {
function testConnWakeImmediately (line 665) | func testConnWakeImmediately(t *testing.T, client *Client, clientEV *cli...
function TestWakeConnImmediately (line 675) | func TestWakeConnImmediately(t *testing.T) {
function TestClientReadOnEOF (line 703) | func TestClientReadOnEOF(t *testing.T) {
function process (line 759) | func process(conn net.Conn) {
type clientReadOnEOF (line 770) | type clientReadOnEOF struct
method OnBoot (line 779) | func (clientReadOnEOF) OnBoot(Engine) (action Action) {
method OnOpen (line 783) | func (cli clientReadOnEOF) OnOpen(Conn) (out []byte, action Action) {
method OnClose (line 787) | func (clientReadOnEOF) OnClose(Conn, error) (action Action) {
method OnTraffic (line 791) | func (cli clientReadOnEOF) OnTraffic(c Conn) (action Action) {
FILE: client_unix.go
type Client (line 39) | type Client struct
method Start (line 106) | func (cli *Client) Start() error {
method Stop (line 153) | func (cli *Client) Stop() error {
method Dial (line 180) | func (cli *Client) Dial(network, address string) (Conn, error) {
method DialContext (line 185) | func (cli *Client) DialContext(network, address string, ctx any) (Conn...
method Enroll (line 194) | func (cli *Client) Enroll(c net.Conn) (Conn, error) {
method EnrollContext (line 199) | func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
function NewClient (line 45) | func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
FILE: client_windows.go
type Client (line 28) | type Client struct
method Start (line 66) | func (cli *Client) Start() error {
method Stop (line 100) | func (cli *Client) Stop() error {
method Dial (line 120) | func (cli *Client) Dial(network, addr string) (Conn, error) {
method DialContext (line 124) | func (cli *Client) DialContext(network, addr string, ctx any) (Conn, e...
method Enroll (line 136) | func (cli *Client) Enroll(nc net.Conn) (gc Conn, err error) {
method EnrollContext (line 140) | func (cli *Client) EnrollContext(nc net.Conn, ctx any) (gc Conn, err e...
function NewClient (line 33) | func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
FILE: conn_map.go
type connMatrix (line 25) | type connMatrix struct
method init (line 30) | func (cm *connMatrix) init() {
method iterate (line 34) | func (cm *connMatrix) iterate(f func(*conn) bool) {
method incCount (line 44) | func (cm *connMatrix) incCount(_ int, delta int32) {
method loadCount (line 48) | func (cm *connMatrix) loadCount() (n int32) {
method addConn (line 52) | func (cm *connMatrix) addConn(c *conn, index int) {
method delConn (line 58) | func (cm *connMatrix) delConn(c *conn) {
method getConn (line 63) | func (cm *connMatrix) getConn(fd int) *conn {
FILE: conn_matrix.go
type connMatrix (line 25) | type connMatrix struct
method init (line 34) | func (cm *connMatrix) init() {
method iterate (line 38) | func (cm *connMatrix) iterate(f func(*conn) bool) {
method incCount (line 52) | func (cm *connMatrix) incCount(row int, delta int32) {
method loadCount (line 56) | func (cm *connMatrix) loadCount() (n int32) {
method addConn (line 63) | func (cm *connMatrix) addConn(c *conn, index int) {
method delConn (line 83) | func (cm *connMatrix) delConn(c *conn) {
method getConn (line 139) | func (cm *connMatrix) getConn(fd int) *conn {
FILE: conn_matrix_test.go
function TestConnMatrix (line 17) | func TestConnMatrix(t *testing.T) {
constant actionAdd (line 39) | actionAdd = iota + 1
constant actionDel (line 40) | actionDel
type handleConn (line 43) | type handleConn struct
function testConnMatrix (line 48) | func testConnMatrix(t *testing.T, n int) {
FILE: connection_bsd.go
method processIO (line 27) | func (c *conn) processIO(_ int, filter netpoll.IOEvent, flags netpoll.IO...
FILE: connection_linux.go
method processIO (line 28) | func (c *conn) processIO(_ int, ev netpoll.IOEvent, _ netpoll.IOFlags) e...
FILE: connection_unix.go
type conn (line 38) | type conn struct
method release (line 90) | func (c *conn) release() {
method open (line 116) | func (c *conn) open(buf []byte) error {
method write (line 139) | func (c *conn) write(data []byte) (n int, err error) {
method writev (line 184) | func (c *conn) writev(bs [][]byte) (n int, err error) {
method asyncWrite (line 252) | func (c *conn) asyncWrite(a any) (err error) {
method asyncWritev (line 273) | func (c *conn) asyncWritev(a any) (err error) {
method sendTo (line 289) | func (c *conn) sendTo(buf []byte, addr unix.Sockaddr) (n int, err erro...
method resetBuffer (line 305) | func (c *conn) resetBuffer() {
method Read (line 311) | func (c *conn) Read(p []byte) (n int, err error) {
method Next (line 330) | func (c *conn) Next(n int) (buf []byte, err error) {
method Peek (line 349) | func (c *conn) Peek(n int) (buf []byte, err error) {
method Discard (line 378) | func (c *conn) Discard(n int) (int, error) {
method Write (line 405) | func (c *conn) Write(p []byte) (int, error) {
method SendTo (line 412) | func (c *conn) SendTo(p []byte, addr net.Addr) (int, error) {
method Writev (line 425) | func (c *conn) Writev(bs [][]byte) (int, error) {
method ReadFrom (line 432) | func (c *conn) ReadFrom(r io.Reader) (int64, error) {
method WriteTo (line 436) | func (c *conn) WriteTo(w io.Writer) (n int64, err error) {
method Flush (line 449) | func (c *conn) Flush() error {
method InboundBuffered (line 453) | func (c *conn) InboundBuffered() int {
method OutboundBuffered (line 457) | func (c *conn) OutboundBuffered() int {
method Context (line 461) | func (c *conn) Context() any { return c.ctx }
method SetContext (line 462) | func (c *conn) SetContext(ctx any) { c.ctx = ctx }
method LocalAddr (line 463) | func (c *conn) LocalAddr() net.Addr { return c.localAddr }
method RemoteAddr (line 464) | func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
method Fd (line 470) | func (c *conn) Fd() int { return c.fd }
method Dup (line 471) | func (c *conn) Dup() (fd int, err error) { return socket.Dup(c.f...
method SetReadBuffer (line 472) | func (c *conn) SetReadBuffer(bytes int) error { return socket.SetRecv...
method SetWriteBuffer (line 473) | func (c *conn) SetWriteBuffer(bytes int) error { return socket.SetSend...
method SetLinger (line 474) | func (c *conn) SetLinger(sec int) error { return socket.SetLing...
method SetNoDelay (line 475) | func (c *conn) SetNoDelay(noDelay bool) error {
method SetKeepAlivePeriod (line 484) | func (c *conn) SetKeepAlivePeriod(d time.Duration) error {
method SetKeepAlive (line 491) | func (c *conn) SetKeepAlive(enabled bool, idle, intvl time.Duration, c...
method AsyncWrite (line 498) | func (c *conn) AsyncWrite(buf []byte, callback AsyncCallback) error {
method AsyncWritev (line 513) | func (c *conn) AsyncWritev(bs [][]byte, callback AsyncCallback) error {
method Wake (line 520) | func (c *conn) Wake(callback AsyncCallback) error {
method CloseWithCallback (line 530) | func (c *conn) CloseWithCallback(callback AsyncCallback) error {
method Close (line 540) | func (c *conn) Close() error {
method EventLoop (line 547) | func (c *conn) EventLoop() EventLoop {
method SetDeadline (line 551) | func (*conn) SetDeadline(_ time.Time) error {
method SetReadDeadline (line 555) | func (*conn) SetReadDeadline(_ time.Time) error {
method SetWriteDeadline (line 559) | func (*conn) SetWriteDeadline(_ time.Time) error {
function newStreamConn (line 57) | func newStreamConn(proto string, fd int, el *eventloop, sa unix.Sockaddr...
function newUDPConn (line 72) | func newUDPConn(fd int, el *eventloop, localAddr net.Addr, sa unix.Socka...
type asyncWriteHook (line 247) | type asyncWriteHook struct
type asyncWritevHook (line 268) | type asyncWritevHook struct
FILE: connection_windows.go
type netErr (line 34) | type netErr struct
type tcpConn (line 39) | type tcpConn struct
type udpConn (line 44) | type udpConn struct
type openConn (line 48) | type openConn struct
type conn (line 53) | type conn struct
method release (line 97) | func (c *conn) release() {
method resetBuffer (line 121) | func (c *conn) resetBuffer() {
method Read (line 127) | func (c *conn) Read(p []byte) (n int, err error) {
method Next (line 146) | func (c *conn) Next(n int) (buf []byte, err error) {
method Peek (line 164) | func (c *conn) Peek(n int) (buf []byte, err error) {
method Discard (line 191) | func (c *conn) Discard(n int) (int, error) {
method Write (line 218) | func (c *conn) Write(p []byte) (int, error) {
method SendTo (line 228) | func (c *conn) SendTo(p []byte, addr net.Addr) (int, error) {
method Writev (line 240) | func (c *conn) Writev(bs [][]byte) (int, error) {
method ReadFrom (line 256) | func (c *conn) ReadFrom(r io.Reader) (int64, error) {
method WriteTo (line 263) | func (c *conn) WriteTo(w io.Writer) (n int64, err error) {
method Flush (line 277) | func (c *conn) Flush() error {
method InboundBuffered (line 281) | func (c *conn) InboundBuffered() int {
method OutboundBuffered (line 288) | func (c *conn) OutboundBuffered() int {
method Context (line 292) | func (c *conn) Context() any { return c.ctx }
method SetContext (line 293) | func (c *conn) SetContext(ctx any) { c.ctx = ctx }
method LocalAddr (line 294) | func (c *conn) LocalAddr() net.Addr { return c.localAddr }
method RemoteAddr (line 295) | func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
method Fd (line 297) | func (c *conn) Fd() (fd int) {
method Dup (line 314) | func (c *conn) Dup() (fd int, err error) {
method SetReadBuffer (line 360) | func (c *conn) SetReadBuffer(bytes int) error {
method SetWriteBuffer (line 371) | func (c *conn) SetWriteBuffer(bytes int) error {
method SetLinger (line 381) | func (c *conn) SetLinger(sec int) error {
method SetNoDelay (line 393) | func (c *conn) SetNoDelay(noDelay bool) error {
method SetKeepAlivePeriod (line 405) | func (c *conn) SetKeepAlivePeriod(d time.Duration) error {
method SetKeepAlive (line 409) | func (c *conn) SetKeepAlive(enabled bool, idle, intvl time.Duration, c...
method AsyncWrite (line 462) | func (c *conn) AsyncWrite(buf []byte, cb AsyncCallback) error {
method AsyncWritev (line 484) | func (c *conn) AsyncWritev(bs [][]byte, cb AsyncCallback) error {
method Wake (line 502) | func (c *conn) Wake(cb AsyncCallback) (err error) {
method Close (line 523) | func (c *conn) Close() (err error) {
method CloseWithCallback (line 540) | func (c *conn) CloseWithCallback(cb AsyncCallback) (err error) {
method EventLoop (line 561) | func (c *conn) EventLoop() EventLoop {
method SetDeadline (line 565) | func (*conn) SetDeadline(_ time.Time) error {
method SetReadDeadline (line 569) | func (*conn) SetReadDeadline(_ time.Time) error {
method SetWriteDeadline (line 573) | func (*conn) SetWriteDeadline(_ time.Time) error {
function packTCPConn (line 65) | func packTCPConn(c *conn, buf []byte) *tcpConn {
function unpackTCPConn (line 71) | func unpackTCPConn(tc *tcpConn) *conn {
function packUDPConn (line 81) | func packUDPConn(c *conn, buf []byte) *udpConn {
function newStreamConn (line 86) | func newStreamConn(el *eventloop, nc net.Conn, ctx any) (c *conn) {
function newUDPConn (line 109) | func newUDPConn(el *eventloop, pc net.PacketConn, rc net.Conn, localAddr...
FILE: context.go
type contextKey (line 25) | type contextKey struct
function NewContext (line 29) | func NewContext(ctx context.Context, v any) context.Context {
function FromContext (line 34) | func FromContext(ctx context.Context) any {
type connContextKey (line 39) | type connContextKey struct
function NewNetConnContext (line 42) | func NewNetConnContext(ctx context.Context, c net.Conn) context.Context {
function FromNetConnContext (line 47) | func FromNetConnContext(ctx context.Context) (net.Conn, bool) {
type netAddrContextKey (line 53) | type netAddrContextKey struct
function NewNetAddrContext (line 56) | func NewNetAddrContext(ctx context.Context, a net.Addr) context.Context {
function FromNetAddrContext (line 61) | func FromNetAddrContext(ctx context.Context) (net.Addr, bool) {
FILE: engine_unix.go
type engine (line 35) | type engine struct
method isShutdown (line 50) | func (eng *engine) isShutdown() bool {
method shutdown (line 55) | func (eng *engine) shutdown(err error) {
method closeEventLoops (line 63) | func (eng *engine) closeEventLoops() {
method runEventLoops (line 82) | func (eng *engine) runEventLoops(ctx context.Context, numEventLoop int...
method activateReactors (line 137) | func (eng *engine) activateReactors(ctx context.Context, numEventLoop ...
method start (line 190) | func (eng *engine) start(ctx context.Context, numEventLoop int) error {
method stop (line 198) | func (eng *engine) stop(ctx context.Context, s Engine) {
function run (line 230) | func run(eventHandler EventHandler, listeners []*listener, options *Opti...
function setKeepAlive (line 281) | func setKeepAlive(fd int, enabled bool, idle, intvl time.Duration, cnt i...
FILE: engine_windows.go
type engine (line 29) | type engine struct
method isShutdown (line 44) | func (eng *engine) isShutdown() bool {
method shutdown (line 49) | func (eng *engine) shutdown(err error) {
method closeEventLoops (line 57) | func (eng *engine) closeEventLoops() {
method start (line 67) | func (eng *engine) start(ctx context.Context, numEventLoop int) error {
method stop (line 106) | func (eng *engine) stop(ctx context.Context, engine Engine) {
function run (line 120) | func run(eventHandler EventHandler, listeners []*listener, options *Opti...
FILE: eventloop_unix.go
type eventloop (line 42) | type eventloop struct
method Register (line 52) | func (el *eventloop) Register(ctx context.Context, addr net.Addr) (<-c...
method Enroll (line 62) | func (el *eventloop) Enroll(ctx context.Context, c net.Conn) (<-chan R...
method Execute (line 72) | func (el *eventloop) Execute(ctx context.Context, runnable Runnable) e...
method Schedule (line 84) | func (el *eventloop) Schedule(context.Context, Runnable, time.Duration...
method Close (line 88) | func (el *eventloop) Close(c Conn) error {
method getLogger (line 92) | func (el *eventloop) getLogger() logging.Logger {
method countConn (line 96) | func (el *eventloop) countConn() int32 {
method closeConns (line 100) | func (el *eventloop) closeConns() {
method enroll (line 113) | func (el *eventloop) enroll(c net.Conn, addr net.Addr, ctx any) (resCh...
method register (line 204) | func (el *eventloop) register(a any) error {
method register0 (line 214) | func (el *eventloop) register0(c *conn) error {
method open (line 231) | func (el *eventloop) open(c *conn) error {
method read0 (line 250) | func (el *eventloop) read0(a any) error {
method read (line 254) | func (el *eventloop) read(c *conn) error {
method write0 (line 302) | func (el *eventloop) write0(a any) error {
method write (line 309) | func (el *eventloop) write(c *conn) error {
method close (line 362) | func (el *eventloop) close(c *conn, err error) error {
method wake (line 405) | func (el *eventloop) wake(c *conn) error {
method ticker (line 415) | func (el *eventloop) ticker(ctx context.Context) {
method readUDP (line 450) | func (el *eventloop) readUDP(fd int, _ netpoll.IOEvent, _ netpoll.IOFl...
method handleAction (line 476) | func (el *eventloop) handleAction(c *conn, action Action) error {
type connWithCallback (line 108) | type connWithCallback struct
constant iovMax (line 307) | iovMax = 1024
FILE: eventloop_unix_test.go
method register (line 21) | func (lb *roundRobinLoadBalancer) register(el *eventloop) {
method register (line 26) | func (lb *leastConnectionsLoadBalancer) register(el *eventloop) {
method register (line 31) | func (lb *sourceAddrHashLoadBalancer) register(el *eventloop) {
function registerInitConn (line 36) | func registerInitConn(el *eventloop) {
function BenchmarkGC4El100k (line 49) | func BenchmarkGC4El100k(b *testing.B) {
function BenchmarkGC4El200k (line 63) | func BenchmarkGC4El200k(b *testing.B) {
function BenchmarkGC4El500k (line 77) | func BenchmarkGC4El500k(b *testing.B) {
function benchServeGC (line 91) | func benchServeGC(b *testing.B, network, addr string, async bool, elNum ...
type benchmarkServerGC (line 117) | type benchmarkServerGC struct
method OnBoot (line 129) | func (s *benchmarkServerGC) OnBoot(eng Engine) (action Action) {
function TestServeGC (line 141) | func TestServeGC(t *testing.T) {
function testServeGC (line 218) | func testServeGC(t *testing.T, network, addr string, multicore, async bo...
type testServerGC (line 241) | type testServerGC struct
method OnBoot (line 252) | func (s *testServerGC) OnBoot(eng Engine) (action Action) {
method GC (line 266) | func (s *testServerGC) GC(secs int) {
FILE: eventloop_windows.go
type eventloop (line 31) | type eventloop struct
method Register (line 40) | func (el *eventloop) Register(ctx context.Context, addr net.Addr) (<-c...
method Enroll (line 50) | func (el *eventloop) Enroll(ctx context.Context, c net.Conn) (<-chan R...
method Execute (line 60) | func (el *eventloop) Execute(ctx context.Context, runnable Runnable) e...
method Schedule (line 74) | func (el *eventloop) Schedule(context.Context, Runnable, time.Duration...
method Close (line 78) | func (el *eventloop) Close(c Conn) error {
method getLogger (line 82) | func (el *eventloop) getLogger() logging.Logger {
method enroll (line 86) | func (el *eventloop) enroll(c net.Conn, addr net.Addr, ctx any) (resCh...
method incConn (line 140) | func (el *eventloop) incConn(delta int32) {
method countConn (line 144) | func (el *eventloop) countConn() int32 {
method run (line 148) | func (el *eventloop) run() (err error) {
method open (line 188) | func (el *eventloop) open(oc *openConn) error {
method read (line 207) | func (el *eventloop) read(c *conn) error {
method readUDP (line 225) | func (el *eventloop) readUDP(c *conn) error {
method ticker (line 234) | func (el *eventloop) ticker(ctx context.Context) {
method wake (line 274) | func (el *eventloop) wake(c *conn) error {
method close (line 282) | func (el *eventloop) close(c *conn, err error) error {
method handleAction (line 299) | func (el *eventloop) handleAction(c *conn, action Action) error {
FILE: gnet.go
type Action (line 40) | type Action
constant None (line 44) | None Action = iota
constant Close (line 47) | Close
constant Shutdown (line 50) | Shutdown
type Engine (line 54) | type Engine struct
method Validate (line 60) | func (e Engine) Validate() error {
method CountConnections (line 71) | func (e Engine) CountConnections() (count int) {
method Register (line 92) | func (e Engine) Register(ctx context.Context) (<-chan RegisteredResult...
method Dup (line 119) | func (e Engine) Dup() (fd int, err error) {
method DupListener (line 137) | func (e Engine) DupListener(network, addr string) (int, error) {
method Stop (line 153) | func (e Engine) Stop(ctx context.Context) error {
type Reader (line 232) | type Reader interface
type Writer (line 260) | type Writer interface
type AsyncCallback (line 306) | type AsyncCallback
type Socket (line 312) | type Socket interface
type Runnable (line 381) | type Runnable interface
type RunnableFunc (line 387) | type RunnableFunc
method Run (line 390) | func (fn RunnableFunc) Run(ctx context.Context) error {
type RegisteredResult (line 395) | type RegisteredResult struct
type EventLoop (line 401) | type EventLoop interface
type Conn (line 424) | type Conn interface
type EventHandler (line 474) | type EventHandler interface
type BuiltinEventEngine (line 507) | type BuiltinEventEngine struct
method OnBoot (line 512) | func (*BuiltinEventEngine) OnBoot(_ Engine) (action Action) {
method OnShutdown (line 518) | func (*BuiltinEventEngine) OnShutdown(_ Engine) {
method OnOpen (line 523) | func (*BuiltinEventEngine) OnOpen(_ Conn) (out []byte, action Action) {
method OnClose (line 529) | func (*BuiltinEventEngine) OnClose(_ Conn, _ error) (action Action) {
method OnTraffic (line 534) | func (*BuiltinEventEngine) OnTraffic(_ Conn) (action Action) {
method OnTick (line 540) | func (*BuiltinEventEngine) OnTick() (delay time.Duration, action Actio...
function createListeners (line 547) | func createListeners(addrs []string, opts ...Option) ([]*listener, *Opti...
function Run (line 678) | func Run(eventHandler EventHandler, protoAddr string, opts ...Option) er...
function Rotate (line 693) | func Rotate(eventHandler EventHandler, addrs []string, opts ...Option) e...
function Stop (line 722) | func Stop(ctx context.Context, protoAddr string) error {
function parseProtoAddr (line 750) | func parseProtoAddr(protoAddr string) (string, string, error) {
function determineEventLoops (line 789) | func determineEventLoops(opts *Options) int {
FILE: gnet_test.go
type testConf (line 39) | type testConf struct
function testUnixAddr (line 52) | func testUnixAddr(t testing.TB) string {
function TestServer (line 62) | func TestServer(t *testing.T) {
type testServer (line 545) | type testServer struct
method OnBoot (line 560) | func (s *testServer) OnBoot(eng Engine) (action Action) {
method OnOpen (line 587) | func (s *testServer) OnOpen(c Conn) (out []byte, action Action) {
method OnShutdown (line 596) | func (s *testServer) OnShutdown(_ Engine) {
method OnClose (line 621) | func (s *testServer) OnClose(c Conn, err error) (action Action) {
method OnTraffic (line 632) | func (s *testServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 752) | func (s *testServer) OnTick() (delay time.Duration, action Action) {
function runServer (line 785) | func runServer(t *testing.T, addrs []string, conf *testConf) {
function startClient (line 827) | func startClient(t *testing.T, network, addr string, multicore, async bo...
function TestDefaultGnetServer (line 876) | func TestDefaultGnetServer(*testing.T) {
type testBadAddrServer (line 885) | type testBadAddrServer struct
method OnBoot (line 889) | func (t *testBadAddrServer) OnBoot(_ Engine) (action Action) {
function TestBadAddresses (line 893) | func TestBadAddresses(t *testing.T) {
function TestTick (line 911) | func TestTick(t *testing.T) {
type testTickServer (line 915) | type testTickServer struct
method OnTick (line 920) | func (t *testTickServer) OnTick() (delay time.Duration, action Action) {
function testTick (line 930) | func testTick(network, addr string, t *testing.T) {
function TestWakeConn (line 942) | func TestWakeConn(t *testing.T) {
type testWakeConnServer (line 946) | type testWakeConnServer struct
method OnOpen (line 956) | func (t *testWakeConnServer) OnOpen(c Conn) (out []byte, action Action) {
method OnClose (line 961) | func (t *testWakeConnServer) OnClose(Conn, error) (action Action) {
method OnTraffic (line 966) | func (t *testWakeConnServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 972) | func (t *testWakeConnServer) OnTick() (delay time.Duration, action Act...
function testWakeConn (line 996) | func testWakeConn(t *testing.T, network, addr string) {
function TestShutdown (line 1016) | func TestShutdown(t *testing.T) {
type testShutdownServer (line 1020) | type testShutdownServer struct
method OnBoot (line 1031) | func (t *testShutdownServer) OnBoot(eng Engine) (action Action) {
method OnOpen (line 1036) | func (t *testShutdownServer) OnOpen(Conn) (out []byte, action Action) {
method OnClose (line 1041) | func (t *testShutdownServer) OnClose(Conn, error) (action Action) {
method OnTick (line 1046) | func (t *testShutdownServer) OnTick() (delay time.Duration, action Act...
function testShutdown (line 1067) | func testShutdown(t *testing.T, network, addr string) {
function TestCloseActionError (line 1085) | func TestCloseActionError(t *testing.T) {
type testCloseActionErrorServer (line 1089) | type testCloseActionErrorServer struct
method OnClose (line 1096) | func (t *testCloseActionErrorServer) OnClose(Conn, error) (action Acti...
method OnTraffic (line 1101) | func (t *testCloseActionErrorServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 1114) | func (t *testCloseActionErrorServer) OnTick() (delay time.Duration, ac...
function testCloseActionError (line 1134) | func testCloseActionError(t *testing.T, network, addr string) {
function TestShutdownActionError (line 1140) | func TestShutdownActionError(t *testing.T) {
type testShutdownActionErrorServer (line 1144) | type testShutdownActionErrorServer struct
method OnTraffic (line 1151) | func (t *testShutdownActionErrorServer) OnTraffic(c Conn) (action Acti...
method OnTick (line 1159) | func (t *testShutdownActionErrorServer) OnTick() (delay time.Duration,...
function testShutdownActionError (line 1179) | func testShutdownActionError(t *testing.T, network, addr string) {
function TestCloseActionOnOpen (line 1185) | func TestCloseActionOnOpen(t *testing.T) {
type testCloseActionOnOpenServer (line 1189) | type testCloseActionOnOpenServer struct
method OnOpen (line 1196) | func (t *testCloseActionOnOpenServer) OnOpen(Conn) (out []byte, action...
method OnClose (line 1201) | func (t *testCloseActionOnOpenServer) OnClose(Conn, error) (action Act...
method OnTick (line 1206) | func (t *testCloseActionOnOpenServer) OnTick() (delay time.Duration, a...
function testCloseActionOnOpen (line 1222) | func testCloseActionOnOpen(t *testing.T, network, addr string) {
function TestShutdownActionOnOpen (line 1228) | func TestShutdownActionOnOpen(t *testing.T) {
type testShutdownActionOnOpenServer (line 1232) | type testShutdownActionOnOpenServer struct
method OnOpen (line 1240) | func (t *testShutdownActionOnOpenServer) OnOpen(Conn) (out []byte, act...
method OnShutdown (line 1245) | func (t *testShutdownActionOnOpenServer) OnShutdown(e Engine) {
method OnTick (line 1254) | func (t *testShutdownActionOnOpenServer) OnTick() (delay time.Duration...
function testShutdownActionOnOpen (line 1270) | func testShutdownActionOnOpen(t *testing.T, network, addr string) {
function TestUDPShutdown (line 1279) | func TestUDPShutdown(t *testing.T) {
type testUDPShutdownServer (line 1283) | type testUDPShutdownServer struct
method OnTraffic (line 1291) | func (t *testUDPShutdownServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 1299) | func (t *testUDPShutdownServer) OnTick() (delay time.Duration, action ...
function testUDPShutdown (line 1320) | func testUDPShutdown(t *testing.T, network, addr string) {
function TestCloseConnection (line 1326) | func TestCloseConnection(t *testing.T) {
type testCloseConnectionServer (line 1330) | type testCloseConnectionServer struct
method OnClose (line 1337) | func (t *testCloseConnectionServer) OnClose(Conn, error) (action Actio...
method OnTraffic (line 1342) | func (t *testCloseConnectionServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 1358) | func (t *testCloseConnectionServer) OnTick() (delay time.Duration, act...
function testCloseConnection (line 1380) | func testCloseConnection(t *testing.T, network, addr string) {
function TestServerOptionsCheck (line 1386) | func TestServerOptionsCheck(t *testing.T) {
function TestStopServer (line 1391) | func TestStopServer(t *testing.T) {
type testStopServer (line 1395) | type testStopServer struct
method OnBoot (line 1403) | func (t *testStopServer) OnBoot(eng Engine) (action Action) {
method OnClose (line 1408) | func (t *testStopServer) OnClose(Conn, error) (action Action) {
method OnTraffic (line 1413) | func (t *testStopServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 1420) | func (t *testStopServer) OnTick() (delay time.Duration, action Action) {
function testStop (line 1450) | func testStop(t *testing.T, network, addr string) {
function TestEngineStop (line 1456) | func TestEngineStop(t *testing.T) {
type testStopEngine (line 1460) | type testStopEngine struct
method OnBoot (line 1470) | func (t *testStopEngine) OnBoot(eng Engine) (action Action) {
method OnClose (line 1475) | func (t *testStopEngine) OnClose(Conn, error) (action Action) {
method OnTraffic (line 1480) | func (t *testStopEngine) OnTraffic(c Conn) (action Action) {
method OnTick (line 1488) | func (t *testStopEngine) OnTick() (delay time.Duration, action Action) {
function testEngineStop (line 1514) | func testEngineStop(t *testing.T, network, addr string) {
function TestClosedWakeUp (line 1546) | func TestClosedWakeUp(t *testing.T) {
type testClosedWakeUpServer (line 1559) | type testClosedWakeUpServer struct
method OnBoot (line 1569) | func (s *testClosedWakeUpServer) OnBoot(eng Engine) (action Action) {
method OnTraffic (line 1591) | func (s *testClosedWakeUpServer) OnTraffic(c Conn) Action {
method OnClose (line 1611) | func (s *testClosedWakeUpServer) OnClose(Conn, error) (action Action) {
type testMultiInstLoggerRaceServer (line 1620) | type testMultiInstLoggerRaceServer struct
method OnBoot (line 1624) | func (t *testMultiInstLoggerRaceServer) OnBoot(_ Engine) (action Actio...
function TestMultiInstLoggerRace (line 1628) | func TestMultiInstLoggerRace(t *testing.T) {
type testDisconnectedAsyncWriteServer (line 1652) | type testDisconnectedAsyncWriteServer struct
method OnTraffic (line 1660) | func (t *testDisconnectedAsyncWriteServer) OnTraffic(c Conn) Action {
method OnTick (line 1703) | func (t *testDisconnectedAsyncWriteServer) OnTick() (delay time.Durati...
function TestDisconnectedAsyncWrite (line 1725) | func TestDisconnectedAsyncWrite(t *testing.T) {
type simServer (line 1740) | type simServer struct
method OnBoot (line 1756) | func (s *simServer) OnBoot(eng Engine) (action Action) {
method OnOpen (line 1761) | func (s *simServer) OnOpen(c Conn) (out []byte, action Action) {
method OnClose (line 1770) | func (s *simServer) OnClose(_ Conn, err error) (action Action) {
method OnTraffic (line 1784) | func (s *simServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 1811) | func (s *simServer) OnTick() (delay time.Duration, action Action) {
constant magicNumber (line 1826) | magicNumber = 1314
constant magicNumberSize (line 1827) | magicNumberSize = 2
constant bodySize (line 1828) | bodySize = 4
function init (line 1833) | func init() {
type testCodec (line 1850) | type testCodec struct
method Encode (line 1852) | func (codec testCodec) Encode(buf []byte) ([]byte, error) {
method Decode (line 1864) | func (codec testCodec) Decode(c Conn) ([]byte, error) {
method Unpack (line 1894) | func (codec testCodec) Unpack(buf []byte) ([]byte, error) {
function TestSimServer (line 1913) | func TestSimServer(t *testing.T) {
function runSimServer (line 1943) | func runSimServer(t *testing.T, addr string, et bool, nclients, packetSi...
function runSimClient (line 1969) | func runSimClient(t *testing.T, network, addr string, packetSize, batch ...
function batchSendAndRecv (line 1996) | func batchSendAndRecv(t *testing.T, c net.Conn, rd *bufio.Reader, packet...
type testUDPSendtoServer (line 2025) | type testUDPSendtoServer struct
method OnBoot (line 2039) | func (t *testUDPSendtoServer) OnBoot(_ Engine) (action Action) {
method OnTraffic (line 2045) | func (t *testUDPSendtoServer) OnTraffic(c Conn) Action {
method OnTick (line 2065) | func (t *testUDPSendtoServer) OnTick() (delay time.Duration, action Ac...
function TestUDPSendtoServer (line 2098) | func TestUDPSendtoServer(t *testing.T) {
function startUDPEchoServer (line 2110) | func startUDPEchoServer(t *testing.T, c *net.UDPConn) {
function startStreamEchoServer (line 2128) | func startStreamEchoServer(t *testing.T, ln net.Listener) {
type streamProxyServer (line 2165) | type streamProxyServer struct
method OnShutdown (line 2190) | func (p *streamProxyServer) OnShutdown(eng Engine) {
method OnOpen (line 2195) | func (p *streamProxyServer) OnOpen(c Conn) (out []byte, action Action) {
method OnClose (line 2264) | func (p *streamProxyServer) OnClose(c Conn, err error) (action Action) {
method OnTraffic (line 2292) | func (p *streamProxyServer) OnTraffic(c Conn) Action {
method OnTick (line 2311) | func (p *streamProxyServer) OnTick() (time.Duration, Action) {
function TestStreamProxyServer (line 2324) | func TestStreamProxyServer(t *testing.T) {
function testStreamProxyServer (line 2410) | func testStreamProxyServer(t *testing.T, addr string, backendServers []s...
type udpProxyServer (line 2485) | type udpProxyServer struct
method OnBoot (line 2512) | func (p *udpProxyServer) OnBoot(eng Engine) (action Action) {
method OnShutdown (line 2520) | func (p *udpProxyServer) OnShutdown(Engine) {
method OnOpen (line 2525) | func (p *udpProxyServer) OnOpen(c Conn) (out []byte, action Action) {
method OnTraffic (line 2536) | func (p *udpProxyServer) OnTraffic(c Conn) Action {
method OnTick (line 2561) | func (p *udpProxyServer) OnTick() (delay time.Duration, action Action) {
function TestUDPProxyServer (line 2619) | func TestUDPProxyServer(t *testing.T) {
function testUDPProxyServer (line 2677) | func testUDPProxyServer(t *testing.T, addr string, backendServers []stri...
FILE: internal/gfd/gfd.go
constant ConnMatrixColumnOffset (line 33) | ConnMatrixColumnOffset = 2
constant SequenceOffset (line 34) | SequenceOffset = 4
constant FdOffset (line 35) | FdOffset = 8
constant EventLoopIndexMax (line 36) | EventLoopIndexMax = math.MaxUint8 + 1
constant ConnMatrixRowMax (line 37) | ConnMatrixRowMax = math.MaxUint8 + 1
constant ConnMatrixColumnMax (line 38) | ConnMatrixColumnMax = math.MaxUint16 + 1
type monotoneSeq (line 41) | type monotoneSeq
method Inc (line 43) | func (seq *monotoneSeq) Inc() uint32 {
type GFD (line 50) | type GFD
method Fd (line 53) | func (gfd GFD) Fd() int {
method EventLoopIndex (line 58) | func (gfd GFD) EventLoopIndex() int {
method ConnMatrixRow (line 63) | func (gfd GFD) ConnMatrixRow() int {
method ConnMatrixColumn (line 68) | func (gfd GFD) ConnMatrixColumn() int {
method Sequence (line 73) | func (gfd GFD) Sequence() uint32 {
method UpdateIndexes (line 78) | func (gfd *GFD) UpdateIndexes(row, column int) {
method Validate (line 84) | func (gfd GFD) Validate() bool {
function NewGFD (line 93) | func NewGFD(fd, elIndex, row, column int) (gfd GFD) {
FILE: listener_unix.go
type listener (line 34) | type listener struct
method packPollAttachment (line 44) | func (ln *listener) packPollAttachment(handler netpoll.PollEventHandle...
method dup (line 49) | func (ln *listener) dup() (int, error) {
method open (line 53) | func (ln *listener) open() (err error) {
method close (line 72) | func (ln *listener) close() {
function initListener (line 84) | func initListener(network, addr string, options *Options) (ln *listener,...
FILE: listener_windows.go
type listener (line 31) | type listener struct
method dup (line 41) | func (l *listener) dup() (int, error) {
method open (line 87) | func (l *listener) open() (err error) {
method close (line 108) | func (l *listener) close() {
function initListener (line 119) | func initListener(network, addr string, options *Options) (*listener, er...
FILE: load_balancer.go
type LoadBalancing (line 25) | type LoadBalancing
constant RoundRobin (line 29) | RoundRobin LoadBalancing = iota
constant LeastConnections (line 33) | LeastConnections
constant SourceAddrHash (line 36) | SourceAddrHash
type loadBalancer (line 41) | type loadBalancer interface
type baseLoadBalancer (line 50) | type baseLoadBalancer struct
method register (line 75) | func (lb *baseLoadBalancer) register(el *eventloop) {
method index (line 82) | func (lb *baseLoadBalancer) index(i int) *eventloop {
method iterate (line 90) | func (lb *baseLoadBalancer) iterate(f func(int, *eventloop) bool) {
method len (line 99) | func (lb *baseLoadBalancer) len() int {
type roundRobinLoadBalancer (line 56) | type roundRobinLoadBalancer struct
method next (line 106) | func (lb *roundRobinLoadBalancer) next(_ net.Addr) (el *eventloop) {
type leastConnectionsLoadBalancer (line 62) | type leastConnectionsLoadBalancer struct
method next (line 114) | func (lb *leastConnectionsLoadBalancer) next(_ net.Addr) (el *eventloo...
type sourceAddrHashLoadBalancer (line 67) | type sourceAddrHashLoadBalancer struct
method hash (line 129) | func (*sourceAddrHashLoadBalancer) hash(s string) int {
method next (line 138) | func (lb *sourceAddrHashLoadBalancer) next(netAddr net.Addr) *eventloop {
FILE: options.go
type Option (line 24) | type Option
function loadOptions (line 26) | func loadOptions(options ...Option) *Options {
type TCPSocketOpt (line 35) | type TCPSocketOpt
constant TCPNoDelay (line 39) | TCPNoDelay TCPSocketOpt = iota
constant TCPDelay (line 40) | TCPDelay
type Options (line 44) | type Options struct
function WithOptions (line 163) | func WithOptions(options Options) Option {
function WithMulticore (line 170) | func WithMulticore(multicore bool) Option {
function WithLockOSThread (line 177) | func WithLockOSThread(lockOSThread bool) Option {
function WithReadBufferCap (line 184) | func WithReadBufferCap(readBufferCap int) Option {
function WithWriteBufferCap (line 191) | func WithWriteBufferCap(writeBufferCap int) Option {
function WithLoadBalancing (line 198) | func WithLoadBalancing(lb LoadBalancing) Option {
function WithNumEventLoop (line 205) | func WithNumEventLoop(numEventLoop int) Option {
function WithReusePort (line 212) | func WithReusePort(reusePort bool) Option {
function WithReuseAddr (line 219) | func WithReuseAddr(reuseAddr bool) Option {
function WithTCPKeepAlive (line 226) | func WithTCPKeepAlive(tcpKeepAlive time.Duration) Option {
function WithTCPKeepInterval (line 233) | func WithTCPKeepInterval(tcpKeepInterval time.Duration) Option {
function WithTCPKeepCount (line 241) | func WithTCPKeepCount(tcpKeepCount int) Option {
function WithTCPNoDelay (line 248) | func WithTCPNoDelay(tcpNoDelay TCPSocketOpt) Option {
function WithSocketRecvBuffer (line 255) | func WithSocketRecvBuffer(recvBuf int) Option {
function WithSocketSendBuffer (line 262) | func WithSocketSendBuffer(sendBuf int) Option {
function WithTicker (line 269) | func WithTicker(ticker bool) Option {
function WithLogPath (line 276) | func WithLogPath(fileName string) Option {
function WithLogLevel (line 283) | func WithLogLevel(lvl logging.Level) Option {
function WithLogger (line 290) | func WithLogger(logger logging.Logger) Option {
function WithMulticastInterfaceIndex (line 297) | func WithMulticastInterfaceIndex(idx int) Option {
function WithBindToDevice (line 307) | func WithBindToDevice(iface string) Option {
function WithEdgeTriggeredIO (line 314) | func WithEdgeTriggeredIO(et bool) Option {
function WithEdgeTriggeredIOChunk (line 322) | func WithEdgeTriggeredIOChunk(chunk int) Option {
FILE: os_unix_test.go
function TestServeMulticast (line 31) | func TestServeMulticast(t *testing.T) {
function findLoopbackInterface (line 65) | func findLoopbackInterface() (*net.Interface, error) {
function testMulticast (line 78) | func testMulticast(t *testing.T, addr string, reuseport, reuseaddr bool,...
type testMcastServer (line 97) | type testMcastServer struct
method startMcastClient (line 107) | func (s *testMcastServer) startMcastClient() {
method OnTraffic (line 137) | func (s *testMcastServer) OnTraffic(c Conn) (action Action) {
method OnTick (line 147) | func (s *testMcastServer) OnTick() (delay time.Duration, action Action) {
type testMulticastBindServer (line 166) | type testMulticastBindServer struct
method OnTick (line 170) | func (t *testMulticastBindServer) OnTick() (delay time.Duration, actio...
function TestMulticastBindIPv4 (line 175) | func TestMulticastBindIPv4(t *testing.T) {
function TestMulticastBindIPv6 (line 185) | func TestMulticastBindIPv6(t *testing.T) {
function detectLinuxEthernetInterfaceName (line 195) | func detectLinuxEthernetInterfaceName() (string, error) {
function getInterfaceIP (line 218) | func getInterfaceIP(ifname string, ipv4 bool) (net.IP, error) {
type testBindToDeviceServer (line 245) | type testBindToDeviceServer struct
function netDial (line 257) | func netDial[T *net.TCPAddr | *net.UDPAddr](network string, a T) (net.Co...
method OnTraffic (line 269) | func (s *testBindToDeviceServer[T]) OnTraffic(c Conn) (action Action) {
method OnShutdown (line 279) | func (s *testBindToDeviceServer[T]) OnShutdown(_ Engine) {
method OnTick (line 283) | func (s *testBindToDeviceServer[T]) OnTick() (delay time.Duration, actio...
function TestBindToDevice (line 319) | func TestBindToDevice(t *testing.T) {
FILE: os_windows_test.go
function SysClose (line 9) | func SysClose(fd int) error {
FILE: pkg/bs/bs.go
function BytesToString (line 23) | func BytesToString(b []byte) string {
function StringToBytes (line 28) | func StringToBytes(s string) []byte {
FILE: pkg/buffer/elastic/elastic_buffer_test.go
function TestMixedBuffer_Basic (line 13) | func TestMixedBuffer_Basic(t *testing.T) {
function TestMixedBuffer_ReadFrom (line 107) | func TestMixedBuffer_ReadFrom(t *testing.T) {
function TestMixedBuffer_WriteTo (line 150) | func TestMixedBuffer_WriteTo(t *testing.T) {
FILE: pkg/buffer/elastic/elastic_ring_buffer.go
type RingBuffer (line 26) | type RingBuffer struct
method instance (line 30) | func (b *RingBuffer) instance() *ring.Buffer {
method Done (line 39) | func (b *RingBuffer) Done() {
method done (line 46) | func (b *RingBuffer) done() {
method Peek (line 55) | func (b *RingBuffer) Peek(n int) (head []byte, tail []byte) {
method Discard (line 63) | func (b *RingBuffer) Discard(n int) (int, error) {
method Read (line 83) | func (b *RingBuffer) Read(p []byte) (int, error) {
method ReadByte (line 93) | func (b *RingBuffer) ReadByte() (byte, error) {
method Write (line 108) | func (b *RingBuffer) Write(p []byte) (int, error) {
method WriteByte (line 116) | func (b *RingBuffer) WriteByte(c byte) error {
method Buffered (line 121) | func (b *RingBuffer) Buffered() int {
method Len (line 129) | func (b *RingBuffer) Len() int {
method Cap (line 137) | func (b *RingBuffer) Cap() int {
method Available (line 145) | func (b *RingBuffer) Available() int {
method WriteString (line 153) | func (b *RingBuffer) WriteString(s string) (int, error) {
method Bytes (line 161) | func (b *RingBuffer) Bytes() []byte {
method ReadFrom (line 169) | func (b *RingBuffer) ReadFrom(r io.Reader) (int64, error) {
method WriteTo (line 174) | func (b *RingBuffer) WriteTo(w io.Writer) (int64, error) {
method IsFull (line 184) | func (b *RingBuffer) IsFull() bool {
method IsEmpty (line 192) | func (b *RingBuffer) IsEmpty() bool {
method Reset (line 200) | func (b *RingBuffer) Reset() {
FILE: pkg/buffer/elastic/elastic_ring_list_buffer.go
type Buffer (line 29) | type Buffer struct
method Read (line 44) | func (mb *Buffer) Read(p []byte) (n int, err error) {
method Peek (line 56) | func (mb *Buffer) Peek(n int) ([][]byte, error) {
method Discard (line 70) | func (mb *Buffer) Discard(n int) (discarded int, err error) {
method Write (line 84) | func (mb *Buffer) Write(p []byte) (n int, err error) {
method Writev (line 101) | func (mb *Buffer) Writev(bs [][]byte) (int, error) {
method ReadFrom (line 135) | func (mb *Buffer) ReadFrom(r io.Reader) (int64, error) {
method WriteTo (line 143) | func (mb *Buffer) WriteTo(w io.Writer) (n int64, err error) {
method Buffered (line 154) | func (mb *Buffer) Buffered() int {
method IsEmpty (line 159) | func (mb *Buffer) IsEmpty() bool {
method Reset (line 164) | func (mb *Buffer) Reset(maxStaticBytes int) {
method Release (line 173) | func (mb *Buffer) Release() {
function New (line 36) | func New(maxStaticBytes int) (*Buffer, error) {
FILE: pkg/buffer/linkedlist/linked_list_buffer.go
type node (line 25) | type node struct
method len (line 30) | func (b *node) len() int {
type Buffer (line 35) | type Buffer struct
method Read (line 43) | func (llb *Buffer) Read(p []byte) (n int, err error) {
method AllocNode (line 69) | func (llb *Buffer) AllocNode(n int) []byte {
method FreeNode (line 74) | func (llb *Buffer) FreeNode(p []byte) {
method Append (line 79) | func (llb *Buffer) Append(p []byte) {
method Pop (line 88) | func (llb *Buffer) Pop() []byte {
method PushFront (line 97) | func (llb *Buffer) PushFront(p []byte) {
method PushBack (line 108) | func (llb *Buffer) PushBack(p []byte) {
method Peek (line 120) | func (llb *Buffer) Peek(maxBytes int) ([][]byte, error) {
method PeekWithBytes (line 142) | func (llb *Buffer) PeekWithBytes(maxBytes int, bs ...[]byte) ([][]byte...
method Discard (line 176) | func (llb *Buffer) Discard(n int) (discarded int, err error) {
method ReadFrom (line 201) | func (llb *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
method WriteTo (line 224) | func (llb *Buffer) WriteTo(w io.Writer) (n int64, err error) {
method Len (line 246) | func (llb *Buffer) Len() int {
method Buffered (line 251) | func (llb *Buffer) Buffered() int {
method IsEmpty (line 256) | func (llb *Buffer) IsEmpty() bool {
method Reset (line 261) | func (llb *Buffer) Reset() {
method pop (line 272) | func (llb *Buffer) pop() *node {
method pushFront (line 288) | func (llb *Buffer) pushFront(b *node) {
method pushBack (line 304) | func (llb *Buffer) pushBack(b *node) {
constant minRead (line 198) | minRead = 512
FILE: pkg/buffer/linkedlist/llbuffer_test.go
function TestLinkedListBuffer_Basic (line 12) | func TestLinkedListBuffer_Basic(t *testing.T) {
function TestLinkedListBuffer_ReadFrom (line 70) | func TestLinkedListBuffer_ReadFrom(t *testing.T) {
function TestLinkedListBuffer_WriteTo (line 104) | func TestLinkedListBuffer_WriteTo(t *testing.T) {
FILE: pkg/buffer/ring/ring_buffer.go
constant MinRead (line 38) | MinRead = 512
constant DefaultBufferSize (line 40) | DefaultBufferSize = 1024
constant bufferGrowThreshold (line 41) | bufferGrowThreshold = 4 * 1024
type Buffer (line 48) | type Buffer struct
method Peek (line 71) | func (rb *Buffer) Peek(n int) (head []byte, tail []byte) {
method peekAll (line 107) | func (rb *Buffer) peekAll() (head []byte, tail []byte) {
method Discard (line 126) | func (rb *Buffer) Discard(n int) (discarded int, err error) {
method Read (line 151) | func (rb *Buffer) Read(p []byte) (n int, err error) {
method ReadByte (line 195) | func (rb *Buffer) ReadByte() (b byte, err error) {
method Write (line 217) | func (rb *Buffer) Write(p []byte) (n int, err error) {
method WriteByte (line 254) | func (rb *Buffer) WriteByte(c byte) error {
method Buffered (line 270) | func (rb *Buffer) Buffered() int {
method Len (line 286) | func (rb *Buffer) Len() int {
method Cap (line 291) | func (rb *Buffer) Cap() int {
method Available (line 296) | func (rb *Buffer) Available() int {
method WriteString (line 312) | func (rb *Buffer) WriteString(s string) (int, error) {
method Bytes (line 317) | func (rb *Buffer) Bytes() []byte {
method ReadFrom (line 343) | func (rb *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
method WriteTo (line 395) | func (rb *Buffer) WriteTo(w io.Writer) (int64, error) {
method IsFull (line 472) | func (rb *Buffer) IsFull() bool {
method IsEmpty (line 477) | func (rb *Buffer) IsEmpty() bool {
method Reset (line 482) | func (rb *Buffer) Reset() {
method grow (line 487) | func (rb *Buffer) grow(newCap int) {
function New (line 57) | func New(size int) *Buffer {
FILE: pkg/buffer/ring/ring_buffer_test.go
function TestRingBuffer_Write (line 13) | func TestRingBuffer_Write(t *testing.T) {
function TestZeroRingBuffer (line 101) | func TestZeroRingBuffer(t *testing.T) {
function TestRingBufferGrow (line 121) | func TestRingBufferGrow(t *testing.T) {
function TestRingBuffer_Read (line 180) | func TestRingBuffer_Read(t *testing.T) {
function TestRingBuffer_ByteInterface (line 246) | func TestRingBuffer_ByteInterface(t *testing.T) {
function TestRingBuffer_ReadFrom (line 305) | func TestRingBuffer_ReadFrom(t *testing.T) {
function TestRingBuffer_WriteTo (line 382) | func TestRingBuffer_WriteTo(t *testing.T) {
FILE: pkg/io/io_bsd.go
function Writev (line 30) | func Writev(fd int, bs [][]byte) (int, error) {
function Readv (line 47) | func Readv(fd int, bs [][]byte) (int, error) {
function bytes2iovec (line 62) | func bytes2iovec(bs [][]byte) []unix.Iovec {
FILE: pkg/io/io_linux.go
function Writev (line 23) | func Writev(fd int, iov [][]byte) (int, error) {
function Readv (line 31) | func Readv(fd int, iov [][]byte) (int, error) {
FILE: pkg/logging/logger.go
constant DebugLevel (line 79) | DebugLevel = zapcore.DebugLevel
constant InfoLevel (line 81) | InfoLevel = zapcore.InfoLevel
constant WarnLevel (line 84) | WarnLevel = zapcore.WarnLevel
constant ErrorLevel (line 87) | ErrorLevel = zapcore.ErrorLevel
constant DPanicLevel (line 90) | DPanicLevel = zapcore.DPanicLevel
constant PanicLevel (line 92) | PanicLevel = zapcore.PanicLevel
constant FatalLevel (line 94) | FatalLevel = zapcore.FatalLevel
function init (line 97) | func init() {
type prefixEncoder (line 126) | type prefixEncoder struct
method EncodeEntry (line 133) | func (e *prefixEncoder) EncodeEntry(entry zapcore.Entry, fields []zapc...
function getDevEncoder (line 152) | func getDevEncoder() zapcore.Encoder {
function getProdEncoder (line 163) | func getProdEncoder() zapcore.Encoder {
function GetDefaultLogger (line 175) | func GetDefaultLogger() Logger {
function GetDefaultFlusher (line 182) | func GetDefaultFlusher() Flusher {
function SetDefaultLoggerAndFlusher (line 189) | func SetDefaultLoggerAndFlusher(logger Logger, flusher Flusher) {
function LogLevel (line 196) | func LogLevel() string {
function CreateLoggerAsLocalFile (line 201) | func CreateLoggerAsLocalFile(localFilePath string, logLevel Level) (logg...
function Cleanup (line 229) | func Cleanup() {
function Error (line 238) | func Error(err error) {
function Debugf (line 247) | func Debugf(format string, args ...any) {
function Infof (line 254) | func Infof(format string, args ...any) {
function Warnf (line 261) | func Warnf(format string, args ...any) {
function Errorf (line 268) | func Errorf(format string, args ...any) {
function Fatalf (line 275) | func Fatalf(format string, args ...any) {
type Logger (line 282) | type Logger interface
FILE: pkg/math/math.go
constant bitSize (line 21) | bitSize = 32 << (^uint(0) >> 63)
constant maxintHeadBit (line 22) | maxintHeadBit = 1 << (bitSize - 2)
function IsPowerOfTwo (line 26) | func IsPowerOfTwo(n int) bool {
function CeilToPowerOfTwo (line 31) | func CeilToPowerOfTwo(n int) int {
function FloorToPowerOfTwo (line 43) | func FloorToPowerOfTwo(n int) int {
function ClosestPowerOfTwo (line 58) | func ClosestPowerOfTwo(n int) int {
FILE: pkg/math/math_test.go
function TestCeilToPowerOfTwo (line 5) | func TestCeilToPowerOfTwo(t *testing.T) {
FILE: pkg/netpoll/defs_linux_386.go
type epollevent (line 8) | type epollevent struct
FILE: pkg/netpoll/defs_linux_amd64.go
type epollevent (line 8) | type epollevent struct
FILE: pkg/netpoll/defs_linux_arm.go
type epollevent (line 9) | type epollevent struct
FILE: pkg/netpoll/defs_linux_arm64.go
type epollevent (line 8) | type epollevent struct
FILE: pkg/netpoll/defs_linux_mips64x.go
type epollevent (line 9) | type epollevent struct
FILE: pkg/netpoll/defs_linux_mipsx.go
type epollevent (line 9) | type epollevent struct
FILE: pkg/netpoll/defs_linux_ppc64.go
type epollevent (line 8) | type epollevent struct
FILE: pkg/netpoll/defs_linux_ppc64le.go
type epollevent (line 8) | type epollevent struct
FILE: pkg/netpoll/defs_linux_riscv64.go
type epollevent (line 9) | type epollevent struct
FILE: pkg/netpoll/defs_linux_s390x.go
type epollevent (line 9) | type epollevent struct
FILE: pkg/netpoll/defs_poller.go
type PollEventHandler (line 20) | type PollEventHandler
type PollAttachment (line 23) | type PollAttachment struct
FILE: pkg/netpoll/defs_poller_epoll.go
constant InitPollEventsCap (line 29) | InitPollEventsCap = 128
constant MaxPollEventsCap (line 31) | MaxPollEventsCap = 1024
constant MinPollEventsCap (line 33) | MinPollEventsCap = 32
constant MaxAsyncTasksAtOneTime (line 35) | MaxAsyncTasksAtOneTime = 256
constant ReadEvents (line 37) | ReadEvents = unix.EPOLLIN | unix.EPOLLPRI
constant WriteEvents (line 39) | WriteEvents = unix.EPOLLOUT
constant ReadWriteEvents (line 41) | ReadWriteEvents = ReadEvents | WriteEvents
constant ErrEvents (line 43) | ErrEvents = unix.EPOLLERR | unix.EPOLLHUP
function IsReadEvent (line 47) | func IsReadEvent(event IOEvent) bool {
function IsWriteEvent (line 52) | func IsWriteEvent(event IOEvent) bool {
function IsErrorEvent (line 57) | func IsErrorEvent(event IOEvent, _ IOFlags) bool {
type eventList (line 61) | type eventList struct
method expand (line 70) | func (el *eventList) expand() {
method shrink (line 77) | func (el *eventList) shrink() {
function newEventList (line 66) | func newEventList(size int) *eventList {
FILE: pkg/netpoll/defs_poller_kqueue.go
constant InitPollEventsCap (line 23) | InitPollEventsCap = 64
constant MaxPollEventsCap (line 25) | MaxPollEventsCap = 512
constant MinPollEventsCap (line 27) | MinPollEventsCap = 16
constant MaxAsyncTasksAtOneTime (line 29) | MaxAsyncTasksAtOneTime = 128
constant ReadEvents (line 31) | ReadEvents = unix.EVFILT_READ
constant WriteEvents (line 33) | WriteEvents = unix.EVFILT_WRITE
constant ReadWriteEvents (line 35) | ReadWriteEvents = ReadEvents | WriteEvents
constant ErrEvents (line 37) | ErrEvents = unix.EV_EOF | unix.EV_ERROR
function IsReadEvent (line 41) | func IsReadEvent(event IOEvent) bool {
function IsWriteEvent (line 46) | func IsWriteEvent(event IOEvent) bool {
function IsErrorEvent (line 51) | func IsErrorEvent(_ IOEvent, flags IOFlags) bool {
type eventList (line 55) | type eventList struct
method expand (line 64) | func (el *eventList) expand() {
method shrink (line 71) | func (el *eventList) shrink() {
function newEventList (line 60) | func newEventList(size int) *eventList {
FILE: pkg/netpoll/example_test.go
function Example (line 31) | func Example() {
FILE: pkg/netpoll/poller_epoll_default.go
type Poller (line 35) | type Poller struct
method Close (line 72) | func (p *Poller) Close() error {
method Trigger (line 90) | func (p *Poller) Trigger(priority queue.EventPriority, fn queue.Func, ...
method Polling (line 115) | func (p *Poller) Polling(callback PollEventHandler) error {
method AddReadWrite (line 189) | func (p *Poller) AddReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method AddRead (line 199) | func (p *Poller) AddRead(pa *PollAttachment, edgeTriggered bool) error {
method AddWrite (line 209) | func (p *Poller) AddWrite(pa *PollAttachment, edgeTriggered bool) error {
method ModRead (line 219) | func (p *Poller) ModRead(pa *PollAttachment, edgeTriggered bool) error {
method ModReadWrite (line 229) | func (p *Poller) ModReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method Delete (line 239) | func (p *Poller) Delete(fd int) error {
function OpenPoller (line 46) | func OpenPoller() (poller *Poller, err error) {
FILE: pkg/netpoll/poller_epoll_ultimate.go
type Poller (line 34) | type Poller struct
method Close (line 73) | func (p *Poller) Close() error {
method Trigger (line 91) | func (p *Poller) Trigger(priority queue.EventPriority, fn queue.Func, ...
method Polling (line 116) | func (p *Poller) Polling() error {
method AddReadWrite (line 191) | func (p *Poller) AddReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method AddRead (line 202) | func (p *Poller) AddRead(pa *PollAttachment, edgeTriggered bool) error {
method AddWrite (line 213) | func (p *Poller) AddWrite(pa *PollAttachment, edgeTriggered bool) error {
method ModRead (line 224) | func (p *Poller) ModRead(pa *PollAttachment, edgeTriggered bool) error {
method ModReadWrite (line 235) | func (p *Poller) ModReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method Delete (line 246) | func (p *Poller) Delete(fd int) error {
function OpenPoller (line 45) | func OpenPoller() (poller *Poller, err error) {
FILE: pkg/netpoll/poller_kqueue_default.go
type Poller (line 33) | type Poller struct
method Close (line 63) | func (p *Poller) Close() error {
method Trigger (line 77) | func (p *Poller) Trigger(priority queue.EventPriority, fn queue.Func, ...
method Polling (line 95) | func (p *Poller) Polling(callback PollEventHandler) error {
method AddReadWrite (line 165) | func (p *Poller) AddReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method AddRead (line 178) | func (p *Poller) AddRead(pa *PollAttachment, edgeTriggered bool) error {
method AddWrite (line 190) | func (p *Poller) AddWrite(pa *PollAttachment, edgeTriggered bool) error {
method ModRead (line 202) | func (p *Poller) ModRead(pa *PollAttachment, _ bool) error {
method ModReadWrite (line 210) | func (p *Poller) ModReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method Delete (line 222) | func (*Poller) Delete(_ int) error {
function OpenPoller (line 43) | func OpenPoller() (poller *Poller, err error) {
FILE: pkg/netpoll/poller_kqueue_ultimate.go
type Poller (line 34) | type Poller struct
method Close (line 64) | func (p *Poller) Close() error {
method Trigger (line 78) | func (p *Poller) Trigger(priority queue.EventPriority, fn queue.Func, ...
method Polling (line 96) | func (p *Poller) Polling() error {
method AddReadWrite (line 167) | func (p *Poller) AddReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method AddRead (line 183) | func (p *Poller) AddRead(pa *PollAttachment, edgeTriggered bool) error {
method AddWrite (line 197) | func (p *Poller) AddWrite(pa *PollAttachment, edgeTriggered bool) error {
method ModRead (line 211) | func (p *Poller) ModRead(pa *PollAttachment, _ bool) error {
method ModReadWrite (line 221) | func (p *Poller) ModReadWrite(pa *PollAttachment, edgeTriggered bool) ...
method Delete (line 235) | func (p *Poller) Delete(_ int) error {
function OpenPoller (line 44) | func OpenPoller() (poller *Poller, err error) {
FILE: pkg/netpoll/poller_kqueue_wakeup.go
method addWakeupEvent (line 25) | func (p *Poller) addWakeupEvent() error {
method wakePoller (line 34) | func (p *Poller) wakePoller() error {
method drainWakeupEvent (line 54) | func (p *Poller) drainWakeupEvent() {}
FILE: pkg/netpoll/poller_kqueue_wakeup1.go
method addWakeupEvent (line 32) | func (p *Poller) addWakeupEvent() error {
method wakePoller (line 45) | func (p *Poller) wakePoller() error {
method drainWakeupEvent (line 58) | func (p *Poller) drainWakeupEvent() {
FILE: pkg/netpoll/poller_unix_ultimate.go
function convertPollAttachment (line 21) | func convertPollAttachment(ptr unsafe.Pointer, attachment *PollAttachmen...
function restorePollAttachment (line 25) | func restorePollAttachment(ptr unsafe.Pointer) *PollAttachment {
FILE: pkg/netpoll/syscall_epoll_generic_linux.go
function epollWait (line 25) | func epollWait(epfd int, events []epollevent, msec int) (int, error) {
FILE: pkg/netpoll/syscall_epoll_linux.go
function epollCtl (line 25) | func epollCtl(epfd int, op int, fd int, event *epollevent) error {
FILE: pkg/netpoll/syscall_epoll_riscv64_arm64_linux.go
function epollWait (line 25) | func epollWait(epfd int, events []epollevent, msec int) (int, error) {
FILE: pkg/netpoll/syscall_errors_linux.go
function errnoErr (line 21) | func errnoErr(e unix.Errno) error {
FILE: pkg/pool/byteslice/byteslice.go
type Pool (line 29) | type Pool struct
method Get (line 44) | func (p *Pool) Get(size int) []byte {
method Put (line 60) | func (p *Pool) Put(buf []byte) {
function Get (line 34) | func Get(size int) []byte {
function Put (line 39) | func Put(buf []byte) {
function index (line 74) | func index(n uint32) uint32 {
FILE: pkg/pool/byteslice/byteslice_test.go
function TestByteSlice (line 8) | func TestByteSlice(t *testing.T) {
function BenchmarkByteSlice (line 32) | func BenchmarkByteSlice(b *testing.B) {
FILE: pkg/pool/goroutine/goroutine.go
constant DefaultAntsPoolSize (line 28) | DefaultAntsPoolSize = 1 << 18
constant ExpiryDuration (line 31) | ExpiryDuration = 10 * time.Second
constant Nonblocking (line 35) | Nonblocking = true
function init (line 38) | func init() {
type antsLogger (line 49) | type antsLogger struct
method Printf (line 54) | func (l antsLogger) Printf(format string, args ...any) {
function Default (line 59) | func Default() *Pool {
FILE: pkg/pool/ringbuffer/ringbuffer.go
constant minBitSize (line 32) | minBitSize = 6
constant steps (line 33) | steps = 20
constant minSize (line 35) | minSize = 1 << minBitSize
constant calibrateCallsThreshold (line 37) | calibrateCallsThreshold = 42000
constant maxPercentile (line 38) | maxPercentile = 0.95
type Pool (line 49) | type Pool struct
method Get (line 72) | func (p *Pool) Get() *RingBuffer {
method Put (line 89) | func (p *Pool) Put(b *RingBuffer) {
method calibrate (line 103) | func (p *Pool) calibrate() {
function Get (line 66) | func Get() *RingBuffer { return builtinPool.Get() }
function Put (line 84) | func Put(b *RingBuffer) { builtinPool.Put(b) }
type callSize (line 142) | type callSize struct
type callSizes (line 147) | type callSizes
method Len (line 149) | func (ci callSizes) Len() int {
method Less (line 153) | func (ci callSizes) Less(i, j int) bool {
method Swap (line 157) | func (ci callSizes) Swap(i, j int) {
function index (line 161) | func index(n int) int {
FILE: pkg/queue/lock_free_queue.go
type lockFreeQueue (line 86) | type lockFreeQueue struct
method Enqueue (line 104) | func (q *lockFreeQueue) Enqueue(task *Task) {
method Dequeue (line 129) | func (q *lockFreeQueue) Dequeue() *Task {
method IsEmpty (line 156) | func (q *lockFreeQueue) IsEmpty() bool {
method Length (line 161) | func (q *lockFreeQueue) Length() int32 {
type node (line 92) | type node struct
function NewLockFreeQueue (line 98) | func NewLockFreeQueue() AsyncTaskQueue {
function load (line 165) | func load(p *unsafe.Pointer) (n *node) {
function cas (line 169) | func cas(p *unsafe.Pointer, old, new *node) bool { //nolint:revive
FILE: pkg/queue/queue.go
type Func (line 21) | type Func
type Task (line 24) | type Task struct
function GetTask (line 32) | func GetTask() *Task {
function PutTask (line 37) | func PutTask(task *Task) {
type AsyncTaskQueue (line 43) | type AsyncTaskQueue interface
type EventPriority (line 51) | type EventPriority
constant HighPriority (line 56) | HighPriority EventPriority = iota
constant LowPriority (line 59) | LowPriority
FILE: pkg/queue/queue_test.go
function TestLockFreeQueue (line 11) | func TestLockFreeQueue(t *testing.T) {
FILE: pkg/socket/fd_unix.go
function Dup (line 27) | func Dup(fd int) (int, error) {
function init (line 35) | func init() {
function dupCloseOnExec (line 40) | func dupCloseOnExec(fd int) (int, error) {
function dupCloseOnExecOld (line 61) | func dupCloseOnExecOld(fd int) (int, error) {
FILE: pkg/socket/sock_bsd.go
function maxListenerBacklog (line 26) | func maxListenerBacklog() int {
FILE: pkg/socket/sock_cloexec.go
function sysSocket (line 21) | func sysSocket(family, sotype, proto int) (int, error) {
function sysAccept (line 25) | func sysAccept(fd int) (nfd int, sa unix.Sockaddr, err error) {
FILE: pkg/socket/sock_linux.go
function maxListenerBacklog (line 27) | func maxListenerBacklog() int {
FILE: pkg/socket/sock_posix.go
function ipToSockaddrInet4 (line 27) | func ipToSockaddrInet4(ip net.IP, port int) (unix.SockaddrInet4, error) {
function ipToSockaddrInet6 (line 40) | func ipToSockaddrInet6(ip net.IP, port int, zone string) (unix.SockaddrI...
function ipToSockaddr (line 72) | func ipToSockaddr(family int, ip net.IP, port int, zone string) (unix.So...
FILE: pkg/socket/sockaddr.go
function NetAddrToSockaddr (line 32) | func NetAddrToSockaddr(addr net.Addr) unix.Sockaddr {
function IPAddrToSockaddr (line 50) | func IPAddrToSockaddr(addr *net.IPAddr) unix.Sockaddr {
function TCPAddrToSockaddr (line 56) | func TCPAddrToSockaddr(addr *net.TCPAddr) unix.Sockaddr {
function UDPAddrToSockaddr (line 62) | func UDPAddrToSockaddr(addr *net.UDPAddr) unix.Sockaddr {
function IPToSockaddr (line 68) | func IPToSockaddr(ip net.IP, port int, zone string) unix.Sockaddr {
function UnixAddrToSockaddr (line 97) | func UnixAddrToSockaddr(addr *net.UnixAddr) (unix.Sockaddr, int) {
function SockaddrToTCPOrUnixAddr (line 114) | func SockaddrToTCPOrUnixAddr(sa unix.Sockaddr) net.Addr {
function SockaddrToUDPAddr (line 128) | func SockaddrToUDPAddr(sa unix.Sockaddr) net.Addr {
function ip6ZoneToInt (line 140) | func ip6ZoneToInt(zone string) int {
function ip6ZoneToString (line 153) | func ip6ZoneToString(zone uint32) string {
function itod (line 164) | func itod(v uint) string {
constant big (line 179) | big = 0xFFFFFF
function dtoi (line 183) | func dtoi(s string, i0 int) (n int, i int, ok bool) {
FILE: pkg/socket/socket.go
type Option (line 28) | type Option struct
function execSockOpts (line 33) | func execSockOpts[T int | string](fd int, opts []Option[T]) error {
function TCPSocket (line 44) | func TCPSocket(proto, addr string, passive bool, sockOptInts []Option[in...
function UDPSocket (line 50) | func UDPSocket(proto, addr string, connect bool, sockOptInts []Option[in...
function UnixSocket (line 56) | func UnixSocket(proto, addr string, passive bool, sockOptInts []Option[i...
function Accept (line 62) | func Accept(fd int) (int, unix.Sockaddr, error) {
FILE: pkg/socket/sockopts_bsd.go
function SetBindToDevice (line 23) | func SetBindToDevice(_ int, _ string) error {
FILE: pkg/socket/sockopts_darwin.go
function SetKeepAlivePeriod (line 29) | func SetKeepAlivePeriod(fd, secs int) error {
function SetKeepAlive (line 43) | func SetKeepAlive(fd int, enabled bool, idle, intvl, cnt int) error {
function SetBindToDevice (line 75) | func SetBindToDevice(_ int, _ string) error {
FILE: pkg/socket/sockopts_freebsd.go
function SetReuseport (line 27) | func SetReuseport(fd, reusePort int) error {
FILE: pkg/socket/sockopts_linux.go
function SetBindToDevice (line 28) | func SetBindToDevice(fd int, ifname string) error {
FILE: pkg/socket/sockopts_openbsd.go
function SetKeepAlivePeriod (line 21) | func SetKeepAlivePeriod(_, _ int) error {
function SetKeepAlive (line 27) | func SetKeepAlive(_ int, _ bool, _, _, _ int) error {
FILE: pkg/socket/sockopts_posix.go
function SetNoDelay (line 34) | func SetNoDelay(fd, noDelay int) error {
function SetRecvBuffer (line 40) | func SetRecvBuffer(fd, size int) error {
function SetSendBuffer (line 46) | func SetSendBuffer(fd, size int) error {
function SetReuseAddr (line 51) | func SetReuseAddr(fd, reuseAddr int) error {
function SetIPv6Only (line 56) | func SetIPv6Only(fd, ipv6only int) error {
function SetLinger (line 72) | func SetLinger(fd, sec int) error {
function SetMulticastMembership (line 86) | func SetMulticastMembership(proto string, udpAddr *net.UDPAddr) func(int...
function SetIPv4MulticastMembership (line 110) | func SetIPv4MulticastMembership(fd int, mcast net.IP, ifIndex int) error {
function SetIPv6MulticastMembership (line 137) | func SetIPv6MulticastMembership(fd int, mcast net.IP, ifIndex int) error {
function interfaceFirstIPv4Addr (line 155) | func interfaceFirstIPv4Addr(ifIndex int) (net.IP, error) {
FILE: pkg/socket/sockopts_unix.go
function SetKeepAlivePeriod (line 29) | func SetKeepAlivePeriod(fd, secs int) error {
function SetKeepAlive (line 43) | func SetKeepAlive(fd int, enabled bool, idle, intvl, cnt int) error {
FILE: pkg/socket/sockopts_unix1.go
function SetReuseport (line 26) | func SetReuseport(fd, reusePort int) error {
FILE: pkg/socket/sys_cloexec.go
function sysSocket (line 25) | func sysSocket(family, sotype, proto int) (fd int, err error) {
function sysAccept (line 40) | func sysAccept(fd int) (nfd int, sa unix.Sockaddr, err error) {
FILE: pkg/socket/tcp_socket.go
function GetTCPSockAddr (line 33) | func GetTCPSockAddr(proto, addr string) (sa unix.Sockaddr, family int, t...
function determineTCPProto (line 63) | func determineTCPProto(proto string, addr *net.TCPAddr) (string, error) {
function tcpSocket (line 85) | func tcpSocket(proto, addr string, passive bool, sockOptInts []Option[in...
FILE: pkg/socket/udp_socket.go
function GetUDPSockAddr (line 31) | func GetUDPSockAddr(proto, addr string) (sa unix.Sockaddr, family int, u...
function determineUDPProto (line 61) | func determineUDPProto(proto string, addr *net.UDPAddr) (string, error) {
function udpSocket (line 83) | func udpSocket(proto, addr string, connect bool, sockOptInts []Option[in...
FILE: pkg/socket/unix_socket.go
function GetUnixSockAddr (line 30) | func GetUnixSockAddr(proto, addr string) (sa unix.Sockaddr, family int, ...
function udsSocket (line 47) | func udsSocket(proto, addr string, passive bool, sockOptInts []Option[in...
FILE: reactor_default.go
method rotate (line 27) | func (el *eventloop) rotate() error {
method orbit (line 46) | func (el *eventloop) orbit() error {
method run (line 79) | func (el *eventloop) run() error {
FILE: reactor_ultimate.go
method rotate (line 26) | func (el *eventloop) rotate() error {
method orbit (line 45) | func (el *eventloop) orbit() error {
method run (line 65) | func (el *eventloop) run() error {
Condensed preview — 126 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (623K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 575,
"preview": "# These are supported funding model platforms\n\ngithub: [panjf2000]\npatreon: panjf2000\nopen_collective: panjf2000\nko_fi: "
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report.yaml",
"chars": 4150,
"preview": "name: Bug Report\ndescription: Oops!..., it's a bug.\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\nassignees:\n - panjf2000\nbody:\n - "
},
{
"path": ".github/ISSUE_TEMPLATE/feature-request.yaml",
"chars": 2522,
"preview": "name: Feature Request\ndescription: Propose an idea to make gnet even better.\ntitle: \"[Feature]: \"\nlabels: [\"proposal\", \""
},
{
"path": ".github/ISSUE_TEMPLATE/question.yaml",
"chars": 1953,
"preview": "name: Question\ndescription: Ask questions about gnet.\ntitle: \"[Question]: \"\nlabels: [\"question\", \"help wanted\"]\nbody:\n "
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 1528,
"preview": "<!--\nThank you for contributing to `gnet`! Please fill this out to help us review your pull request more efficiently.\n\nW"
},
{
"path": ".github/release-drafter.yml",
"chars": 2076,
"preview": "name-template: Gnet v$RESOLVED_VERSION\ntag-template: v$RESOLVED_VERSION\ncategories:\n - title: 🧨 Breaking changes\n "
},
{
"path": ".github/workflows/codeql.yml",
"chars": 2063,
"preview": "name: \"Code Scanning\"\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-ignore:\n - '**.md"
},
{
"path": ".github/workflows/cross-compile-bsd.yml",
"chars": 2311,
"preview": "name: Cross-compile for *BSD\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-ignore:\n -"
},
{
"path": ".github/workflows/gh-translator.yml",
"chars": 527,
"preview": "name: 'gh-translator'\n\non:\n issues:\n types: [opened]\n pull_request:\n types: [opened]\n issue_comment:\n types:"
},
{
"path": ".github/workflows/pull-request.yml",
"chars": 446,
"preview": "name: Check pull request target\non:\n pull_request:\n types:\n - opened\n - reopened\n - synchronize\n b"
},
{
"path": ".github/workflows/release-drafter.yml",
"chars": 1403,
"preview": "name: Release Drafter\n\non:\n push:\n # branches to consider in the event; optional, defaults to all\n branches:\n "
},
{
"path": ".github/workflows/stale-bot.yml",
"chars": 1998,
"preview": "name: Monitor inactive issues and PRs\non:\n schedule:\n - cron: '0 0 * * *'\n workflow_dispatch:\n\njobs:\n stale-issues"
},
{
"path": ".github/workflows/test.yml",
"chars": 3200,
"preview": "name: Run tests\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-ignore:\n - '**.md'\n "
},
{
"path": ".github/workflows/test_gc_opt.yml",
"chars": 2673,
"preview": "name: Run tests with -tags=gc_opt\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-ignore:\n "
},
{
"path": ".github/workflows/test_poll_opt.yml",
"chars": 2663,
"preview": "name: Run tests with -tags=poll_opt\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-ignore:\n"
},
{
"path": ".github/workflows/test_poll_opt_gc_opt.yml",
"chars": 2691,
"preview": "name: Run tests with -tags=poll_opt,gc_opt\n\non:\n push:\n branches:\n - master\n - dev\n - 1.x\n paths-i"
},
{
"path": ".gitignore",
"chars": 270,
"preview": "# IDEs\n.idea/\n.vscode/\n\n# dependencies\n/node_modules\n\n# production\n/build\n\n# generated files\n.docusaurus\n.docusaurus/\n.c"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3351,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 816,
"preview": "# Contributing \n\n## With issues:\n - Use the search tool before opening a new issue.\n - Please provide source code and "
},
{
"path": "LICENSE",
"chars": 11387,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 13964,
"preview": "<p align=\"center\">\n<img src=\"https://raw.githubusercontent.com/panjf2000/logos/master/gnet/logo.png\" alt=\"gnet\" />\n<br /"
},
{
"path": "README_ZH.md",
"chars": 11428,
"preview": "<p align=\"center\">\n<img src=\"https://raw.githubusercontent.com/panjf2000/logos/master/gnet/logo.png\" alt=\"gnet\" />\n<br /"
},
{
"path": "acceptor_unix.go",
"chars": 4048,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "acceptor_windows.go",
"chars": 2307,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "client_test.go",
"chars": 24985,
"preview": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || windows\n\npackage gnet\n\nimport (\n\t\"bytes\"\n\tcra"
},
{
"path": "client_unix.go",
"chars": 7737,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "client_windows.go",
"chars": 5249,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "conn_map.go",
"chars": 1644,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "conn_matrix.go",
"chars": 4279,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "conn_matrix_test.go",
"chars": 2711,
"preview": "//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd) && gc_opt\n\npackage gnet\n\nimport (\n\t\"net\"\n\t\"tes"
},
{
"path": "connection_bsd.go",
"chars": 2032,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "connection_linux.go",
"chars": 2602,
"preview": "/*\n * Copyright (c) 2021 The Gnet Authors. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "connection_unix.go",
"chars": 13942,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "connection_windows.go",
"chars": 11985,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "context.go",
"chars": 2154,
"preview": "/*\n * Copyright (c) 2025 The Gnet Authors. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "engine_unix.go",
"chars": 7808,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "engine_windows.go",
"chars": 4430,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "eventloop_unix.go",
"chars": 12879,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "eventloop_unix_test.go",
"chars": 6937,
"preview": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd\n\npackage gnet\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"runt"
},
{
"path": "eventloop_windows.go",
"chars": 7283,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "gnet.go",
"chars": 27462,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "gnet_test.go",
"chars": 89452,
"preview": "package gnet\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"math\"\n\t\"ma"
},
{
"path": "go.mod",
"chars": 483,
"preview": "module github.com/panjf2000/gnet/v2\n\nrequire (\n\tgithub.com/panjf2000/ants/v2 v2.11.3\n\tgithub.com/stretchr/testify v1.10."
},
{
"path": "go.sum",
"chars": 2122,
"preview": "github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.m"
},
{
"path": "internal/gfd/gfd.go",
"chars": 3150,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "listener_unix.go",
"chars": 4603,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "listener_windows.go",
"chars": 3607,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "load_balancer.go",
"chars": 3985,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "options.go",
"chars": 11073,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "os_unix_test.go",
"chars": 20181,
"preview": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd\n\npackage gnet\n\nimport (\n\t\"context\"\n\tcrand \"crypt"
},
{
"path": "os_windows_test.go",
"chars": 137,
"preview": "//go:build windows\n\npackage gnet\n\nimport (\n\t\"syscall\"\n)\n\nfunc SysClose(fd int) error {\n\treturn syscall.CloseHandle(sysca"
},
{
"path": "pkg/bs/bs.go",
"chars": 1056,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/buffer/elastic/elastic_buffer_test.go",
"chars": 4480,
"preview": "package elastic\n\nimport (\n\t\"bytes\"\n\tcrand \"crypto/rand\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testif"
},
{
"path": "pkg/buffer/elastic/elastic_ring_buffer.go",
"chars": 5290,
"preview": "// Copyright (c) 2022 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/buffer/elastic/elastic_ring_list_buffer.go",
"chars": 4747,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/buffer/linkedlist/linked_list_buffer.go",
"chars": 6485,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/buffer/linkedlist/llbuffer_test.go",
"chars": 3624,
"preview": "package linkedlist\n\nimport (\n\t\"bytes\"\n\tcrand \"crypto/rand\"\n\t\"math/rand\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/requir"
},
{
"path": "pkg/buffer/ring/ring_buffer.go",
"chars": 11295,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n// Copyright (c) 2019 Chao yuepan, Allen Xu\n//\n// Permissio"
},
{
"path": "pkg/buffer/ring/ring_buffer_test.go",
"chars": 21451,
"preview": "package ring\n\nimport (\n\t\"bytes\"\n\tcrand \"crypto/rand\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"gith"
},
{
"path": "pkg/errors/errors.go",
"chars": 3139,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/io/io.go",
"chars": 693,
"preview": "/*\n * Copyright (c) 2025 The Gnet Authors. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "pkg/io/io_bsd.go",
"chars": 2231,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/io/io_linux.go",
"chars": 980,
"preview": "/*\n * Copyright (c) 2021 The Gnet Authors. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "pkg/logging/logger.go",
"chars": 8456,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/math/math.go",
"chars": 1690,
"preview": "// Copyright (c) 2022 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/math/math_test.go",
"chars": 4063,
"preview": "package math\n\nimport \"testing\"\n\nfunc TestCeilToPowerOfTwo(t *testing.T) {\n\ttype args struct {\n\t\tn int\n\t}\n\ttests := []str"
},
{
"path": "pkg/netpoll/defs_bsd_32bit.go",
"chars": 763,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_bsd_64bit.go",
"chars": 802,
"preview": "// Copyright (c) 2023 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_linux.go",
"chars": 725,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_linux_386.go",
"chars": 194,
"preview": "// created by cgo -cdefs and then converted to Go\n// cgo -cdefs defs2_linux.go\n\n//go:build poll_opt\n\npackage netpoll\n\nty"
},
{
"path": "pkg/netpoll/defs_linux_amd64.go",
"chars": 211,
"preview": "// created by cgo -cdefs and then converted to Go\n// cgo -cdefs defs_linux.go defs1_linux.go\n\n//go:build poll_opt\n\npacka"
},
{
"path": "pkg/netpoll/defs_linux_arm.go",
"chars": 289,
"preview": "// Copyright 2014 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/netpoll/defs_linux_arm64.go",
"chars": 254,
"preview": "// Created by cgo -cdefs and converted (by hand) to Go\n// ../cmd/cgo/cgo -cdefs defs_linux.go defs1_linux.go defs2_linux"
},
{
"path": "pkg/netpoll/defs_linux_mips64x.go",
"chars": 335,
"preview": "// Copyright 2015 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/netpoll/defs_linux_mipsx.go",
"chars": 309,
"preview": "// Copyright 2016 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/netpoll/defs_linux_ppc64.go",
"chars": 236,
"preview": "// created by cgo -cdefs and then converted to Go\n// cgo -cdefs defs_linux.go defs3_linux.go\n\n//go:build poll_opt\n\npacka"
},
{
"path": "pkg/netpoll/defs_linux_ppc64le.go",
"chars": 236,
"preview": "// created by cgo -cdefs and then converted to Go\n// cgo -cdefs defs_linux.go defs3_linux.go\n\n//go:build poll_opt\n\npacka"
},
{
"path": "pkg/netpoll/defs_linux_riscv64.go",
"chars": 314,
"preview": "// Generated using cgo, then manually converted into appropriate naming and code\n// for the Go runtime.\n// go tool cgo -"
},
{
"path": "pkg/netpoll/defs_linux_s390x.go",
"chars": 302,
"preview": "// Copyright 2016 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/netpoll/defs_poller.go",
"chars": 1034,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_poller_bsd.go",
"chars": 836,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_poller_epoll.go",
"chars": 2598,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_poller_kqueue.go",
"chars": 2495,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/defs_poller_netbsd.go",
"chars": 783,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/example_test.go",
"chars": 3841,
"preview": "// Copyright (c) 2025 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/netpoll.go",
"chars": 2992,
"preview": "// Copyright (c) 2025 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/poller_epoll_default.go",
"chars": 8022,
"preview": "// Copyright (c) 2019 Andy Pan\n// Copyright (c) 2017 Joshua J Baker\n//\n// Licensed under the Apache License, Version 2.0"
},
{
"path": "pkg/netpoll/poller_epoll_ultimate.go",
"chars": 8294,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/poller_kqueue_default.go",
"chars": 7309,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/poller_kqueue_ultimate.go",
"chars": 7674,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/poller_kqueue_wakeup.go",
"chars": 1507,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the"
},
{
"path": "pkg/netpoll/poller_kqueue_wakeup1.go",
"chars": 2021,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the"
},
{
"path": "pkg/netpoll/poller_unix_ultimate.go",
"chars": 960,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/syscall_epoll_generic_linux.go",
"chars": 1389,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/syscall_epoll_linux.go",
"chars": 965,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/syscall_epoll_riscv64_arm64_linux.go",
"chars": 1412,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/netpoll/syscall_errors_linux.go",
"chars": 682,
"preview": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
},
{
"path": "pkg/pool/bytebuffer/bytebuffer.go",
"chars": 1114,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/pool/byteslice/byteslice.go",
"chars": 2203,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/pool/byteslice/byteslice_test.go",
"chars": 866,
"preview": "package byteslice\n\nimport (\n\t\"runtime/debug\"\n\t\"testing\"\n)\n\nfunc TestByteSlice(t *testing.T) {\n\tbuf := Get(8)\n\tcopy(buf, "
},
{
"path": "pkg/pool/goroutine/goroutine.go",
"chars": 2092,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/pool/ringbuffer/ringbuffer.go",
"chars": 3962,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n// Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia\n//\n/"
},
{
"path": "pkg/queue/lock_free_queue.go",
"chars": 6061,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/queue/queue.go",
"chars": 1667,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/queue/queue_test.go",
"chars": 947,
"preview": "package queue_test\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/panjf2000/gnet/v2/pkg/queue\"\n)\n\nfunc TestLo"
},
{
"path": "pkg/socket/fd_unix.go",
"chars": 1943,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sock_bsd.go",
"chars": 1282,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n// Copyright (c) 2017 Ma Weiwei, Max Riveiro\n//\n// Licensed"
},
{
"path": "pkg/socket/sock_cloexec.go",
"chars": 979,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sock_linux.go",
"chars": 1335,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n// Copyright (c) 2017 Ma Weiwei, Max Riveiro\n//\n// Licensed"
},
{
"path": "pkg/socket/sock_posix.go",
"chars": 2559,
"preview": "// Copyright (c) 2022 The Gnet Authors. All rights reserved.\n// Copyright 2009 The Go Authors. All rights reserved.\n//\n/"
},
{
"path": "pkg/socket/sockaddr.go",
"chars": 5418,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n// Copyright (c) 2012 The Go Authors. All rights reserved.\n"
},
{
"path": "pkg/socket/socket.go",
"chars": 2422,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n// Copyright (c) 2017 Max Riveiro\n//\n// Licensed under the "
},
{
"path": "pkg/socket/sockopts_bsd.go",
"chars": 935,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_darwin.go",
"chars": 2265,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_freebsd.go",
"chars": 911,
"preview": "/*\n * Copyright (c) 2024 The Gnet Authors. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "pkg/socket/sockopts_linux.go",
"chars": 1086,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_openbsd.go",
"chars": 1184,
"preview": "// Copyright (c) 2024 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_posix.go",
"chars": 6204,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_unix.go",
"chars": 2075,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sockopts_unix1.go",
"chars": 957,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/sys_cloexec.go",
"chars": 1357,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "pkg/socket/tcp_socket.go",
"chars": 3436,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n// Copyright (c) 2017 Max Riveiro\n//\n// Licensed under the "
},
{
"path": "pkg/socket/udp_socket.go",
"chars": 3391,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n// Copyright (c) 2017 Max Riveiro\n//\n// Licensed under the "
},
{
"path": "pkg/socket/unix_socket.go",
"chars": 2503,
"preview": "// Copyright (c) 2020 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "reactor_default.go",
"chars": 3830,
"preview": "// Copyright (c) 2019 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "reactor_ultimate.go",
"chars": 2242,
"preview": "// Copyright (c) 2021 The Gnet Authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \""
}
]
About this extraction
This page contains the full source code of the panjf2000/gnet GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 126 files (558.5 KB), approximately 167.9k tokens, and a symbol index with 865 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.