Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simple-tcp game server to use for testing. #1071

Merged
merged 1 commit into from
Sep 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions examples/simple-tcp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2019 Google LLC 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.

# build
FROM golang:1.11.5 as builder
WORKDIR /go/src/simple-tcp

COPY examples/simple-tcp/main.go .
COPY . /go/src/agones.dev/agones
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .

# final image
FROM alpine:3.9

RUN adduser -D server
COPY --from=builder /go/src/simple-tcp/server /home/server/server
RUN chown -R server /home/server && \
chmod o+x /home/server/server

USER server
ENTRYPOINT ["/home/server/server"]
42 changes: 42 additions & 0 deletions examples/simple-tcp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2019 Google LLC 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.

#
# Makefile for building a simple tcp server
#

# __ __ _ _ _
# \ \ / /_ _ _ __(_) __ _| |__ | | ___ ___
# \ \ / / _` | '__| |/ _` | '_ \| |/ _ \ __|
# \ V / (_| | | | | (_| | |_) | | __\__ \
# \_/ \__,_|_| |_|\__,_|_.__/|_|\___|___/
#

REPOSITORY = gcr.io/agones-images

mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
project_path := $(dir $(mkfile_path))
server_tag = $(REPOSITORY)/tcp-server:0.1
root_path = $(realpath $(project_path)/../..)

# _____ _
# |_ _|_ _ _ __ __ _ ___| |_ ___
# | |/ _` | '__/ _` |/ _ \ __/ __|
# | | (_| | | | (_| | __/ |_\__ \
# |_|\__,_|_| \__, |\___|\__|___/
# |___/

# Build a docker image for the server, and tag it
build:
cd $(root_path) && docker build -f $(project_path)/Dockerfile --tag=$(server_tag) .
26 changes: 26 additions & 0 deletions examples/simple-tcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Simple TCP Server

A very simple game server, for the purposes of testing a TCP based server on Agones.

## Server
Starts a server on port `7654` by default. Can be overwritten by `PORT` env var or `port` flag.

When it receives a text message ending with a newline, it will send back "ACK:<text content>" as an echo.

If it receives the text "EXIT", then it will `sys.Exit(0)`

## Firewalls

If you plan to access your server remotely, you may need to open up a hole in your
firewall.

For example, if you created a cluster running on Google Kubernetes Engine following
the installation guide, you can create a firewall rule to allow TCP traffic to nodes
tagged as game-server via ports 7000-8000.

```bash
gcloud compute firewall-rules create game-server-firewall-tcp \
--allow tcp:7000-8000 \
--target-tags game-server \
--description "Firewall to allow game server udp traffic"
```
36 changes: 36 additions & 0 deletions examples/simple-tcp/gameserver.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2019 Google LLC 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.

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
generateName: "simple-tcp-"
spec:
ports:
- name: default
portPolicy: Dynamic
containerPort: 7654
protocol: TCP
template:
spec:
containers:
- name: simple-tcp
image: gcr.io/agones-images/tcp-server:0.1
resources:
requests:
memory: "32Mi"
cpu: "20m"
limits:
memory: "32Mi"
cpu: "20m"
144 changes: 144 additions & 0 deletions examples/simple-tcp/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2019 Google LLC 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 main is a very simple echo TCP server
package main

import (
"bufio"
"flag"
"log"
"net"
"os"
"strings"
"time"

"agones.dev/agones/pkg/util/signals"
sdk "agones.dev/agones/sdks/go"
)

// main starts a TCP server that receives a message at a time
// (newline delineated), and echos the output.
func main() {
go doSignal()

port := flag.String("port", "7654", "The port to listen to tcp traffic on")
flag.Parse()
if ep := os.Getenv("PORT"); ep != "" {
port = &ep
}

log.Printf("Starting TCP server, listening on port %s", *port)
ln, err := net.Listen("tcp", ":"+*port)
if err != nil {
log.Fatalf("Could not start tcp server: %v", err)
}
defer ln.Close() // nolint: errcheck

log.Print("Creating SDK instance")
s, err := sdk.NewSDK()
if err != nil {
log.Fatalf("Could not connect to sdk: %v", err)
}

log.Print("Starting Health Ping")
stop := make(chan struct{})
go doHealth(s, stop)

log.Print("Marking this server as ready")
if err := s.Ready(); err != nil {
log.Fatalf("Could not send ready message")
}

for {
conn, err := ln.Accept()
if err != nil {
log.Printf("Unable to accept incoming tcp connection: %v", err)
}
go handleConnection(conn, stop, s)
}
}

// doSignal shutsdown on SIGTERM/SIGKILL
func doSignal() {
stop := signals.NewStopChannel()
<-stop
log.Println("Exit signal received. Shutting down.")
os.Exit(0)
}

// handleConnection services a single tcp connection to the server
func handleConnection(conn net.Conn, stop chan struct{}, s *sdk.SDK) {
log.Printf("Client %s connected", conn.RemoteAddr().String())
scanner := bufio.NewScanner(conn)
for {
if ok := scanner.Scan(); !ok {
log.Printf("Client %s disconnected", conn.RemoteAddr().String())
return
}
handleCommand(conn, scanner.Text(), stop, s)
}
}

// respond responds to a given sender.
func respond(conn net.Conn, txt string) {
log.Printf("Responding with %q", txt)
if _, err := conn.Write([]byte(txt+"\n")); err != nil {
log.Fatalf("Could not write to tcp stream: %v", err)
}
}

func handleCommand(conn net.Conn, txt string, stop chan struct{}, s *sdk.SDK) {
parts := strings.Split(strings.TrimSpace(txt), " ")

log.Printf("parts: %v", parts)
switch parts[0] {
// shuts down the gameserver
case "EXIT":
respond(conn, "ACK: "+txt)
exit(s)

// turns off the health pings
case "UNHEALTHY":
close(stop)
}

respond(conn, "ACK: "+txt+"\n")
}

// exit shutdowns the server
func exit(s *sdk.SDK) {
log.Printf("Received EXIT command. Exiting.")
// This tells Agones to shutdown this Game Server
if err := s.Shutdown(); err != nil {
log.Printf("Could not call shutdown: %v", err)
}
os.Exit(0)
}

// doHealth sends the regular Health Pings
func doHealth(sdk *sdk.SDK, stop <-chan struct{}) {
tick := time.Tick(2 * time.Second)
for {
if err := sdk.Health(); err != nil {
log.Fatalf("Could not send health ping: %v", err)
}
select {
case <-stop:
log.Print("Stopped health pings")
return
case <-tick:
}
}
}
3 changes: 3 additions & 0 deletions site/content/en/docs/Examples/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ These are full examples for each of the resource types of Agones
These are all examples of simple game server implementations, that integrate the Agones game server SDK.

- {{< ghlink href="examples/simple-udp" >}}Simple UDP{{< /ghlink >}} (Go) - simple server and client that send UDP packets back and forth.
{{% feature publishVersion="1.1.0" %}}
- {{< ghlink href="examples/simple-tcp" >}}Simple TCP{{< /ghlink >}} (Go) - simple server that responds to new-line delimited messages sent over a TCP connection.
{{% /feature %}}
- {{< ghlink href="examples/cpp-simple" >}}CPP Simple{{< /ghlink >}} (C++) - C++ example that starts up, stays healthy and then shuts down after 60 seconds.
- {{< ghlink href="examples/nodejs-simple" >}}Node.js Simple{{< /ghlink >}} (Node.js) -
A simple Node.js example that marks itself as ready, sets some labels and then shutsdown.
Expand Down