Send request repeatedly to the tcp server at regular interval - go

This is the code taken from the go book. The client enters the message and the request is sent to the server. How to send the same request repeatedly without entering values every time? Also, the time interval between successive requests should be 3 seconds. Should I use goroutines?
package main
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
arguments := os.Args
if len(arguments) == 1 {
fmt.Println("Please provide host:port.")
return
}
CONNECT := arguments[1]
c, err := net.Dial("tcp", CONNECT)
if err != nil {
fmt.Println(err)
return
}
for {
reader := bufio.NewReader(os.Stdin)
fmt.Print(">>")
text, _ := reader.ReadString('\n')
fmt.Fprintf(c, text+"\n")
}
}

Use a time.Ticker to execute code at some specified interval:
t := time.NewTicker(3 * time.Second)
defer t.Stop()
for range t.C {
_, err := c.Write([]byte("Hello!\n"))
if err != nil {
log.Fatal(err)
}
}
Run it on the playground.

Related

How to get gRPC response limit

I have Go gRPC endpoint that returns an array of some items. I want to limit the number of items in response to make fit the maximum that gRPC allows to send.
My idea is to get the maximum in handler func, divide it by item size and voilà. But how can I get response max size?
I don't want to set max response size to make my class independent of grpc instantiation.
main.go
package main
import (
"log"
"main/api"
"main/router"
"net"
"google.golang.org/grpc"
)
func main() {
l, err := net.Listen("tcp", "0.0.0.0:138080")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
r := router.New()
api.RegisterTestGenerateServiceServer(grpcServer, r)
err = grpcServer.Serve(l)
if err != nil {
log.Fatalf("failed to run server: %v", err)
}
}
router.go
package router
import (
"context"
"main/api"
"main/model"
"unsafe"
)
type router struct {
}
func New() *router {
return &router{}
}
func (r *router) TestCall(context.Context, *api.TestCallRequest) (*api.TestCallResponse, error) {
items := somewhere.GetItems()
apiItems := transform.ToAPIItems(items)
itemSize := unsafe.Sizeof(model.Item{})
responseSize := someWonderFuncGetGrpcResponseMaxSize()
NItems := responseSize / itemSize
return &api.TestCallResponse{
Items: apiItems[:NItems],
}, nil
}
Try updating grpc.MaxCallSendMsgSize(s int) on your client to enable your client send larger message.
This worked for me, as example:
func main() {
// create connection
opts := []grpc.DialOption{
// grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), // uncomment for large amount of data returned
}
cc, err := grpc.Dial("localhost:9089", opts...)
if err != nil {
fmt.Printf("failed to dial: %v\n", err)
return
}
defer cc.Close()
client := pb.NewGrpcServiceClient(cc)
data, err := client.GetDataFromGRPCService()

Program that listens for connections in background and also waits for user input

I want to create a program that waits for user input and at the same time, listens for connections in the background. Neither of my functions depends on the other. I'm new to Go so I'm not sure how I can accomplish this. Can this even be done?
func listen() {
listener, _ := net.Listen("tcp4", ":" + port)
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
return
}
// Some stuff
}
}
func getNewTransaction() {
for {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Receiver: ")
receiver, _ := reader.ReadString('\n')
var amount float32
fmt.Println("Amount: ")
fmt.Scanf("%f", &amount)
}
}
func main() {
go listen()
go getNewTransaction()
select{}
}
Your original code doesn't show how the port is being available inside the listen function. So I am pretty sure the code is erroring out in that function.
I updated the listen function, and the one that takes user input. Replaces the select{} with wait group.
Here's working code:
package main
import (
"bufio"
"fmt"
"net"
"os"
"sync"
)
func listen(port string) {
listener, _ := net.Listen("tcp4", ":"+port)
defer listener.Close()
fmt.Println("Telnet to: ", port)
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
} else {
conn.Write([]byte("kthx bye!"))
}
conn.Close()
}
}
func getNewTransaction() {
for {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Type something and press enter: ")
input, _ := reader.ReadString('\n')
fmt.Println(input)
}
}
func main() {
go listen("8080")
go getNewTransaction()
wg := &sync.WaitGroup{}
wg.Add(1)
wg.Wait()
}

Too Many open files/ No such host error while running a go program which makes concurrent requests

I have a golang program which is supposed to call an API with different payloads, the web application is a drop wizard application which is running on localhost, and the go program is below
package main
import (
"bufio"
"encoding/json"
"log"
"net"
"net/http"
"os"
"strings"
"time"
)
type Data struct {
PersonnelId string `json:"personnel_id"`
DepartmentId string `json:"department_id"`
}
type PersonnelEvent struct {
EventType string `json:"event_type"`
Data `json:"data"`
}
const (
maxIdleConnections = 20
maxIdleConnectionsPerHost = 20
timeout = time.Duration(5 * time.Second)
)
var transport = http.Transport{
Dial: dialTimeout,
MaxIdleConns: maxIdleConnections,
MaxIdleConnsPerHost: 20,
}
var client = &http.Client{
Transport: &transport,
}
func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, timeout)
}
func makeRequest(payload string) {
req, _ := http.NewRequest("POST", "http://localhost:9350/v1/provider-
location-personnel/index", strings.NewReader(payload))
req.Header.Set("X-App-Token", "TESTTOKEN1")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Println("Api invocation returned an error ", err)
} else {
defer resp.Body.Close()
log.Println(resp.Body)
}
}
func indexPersonnels(personnelPayloads []PersonnelEvent) {
for _, personnelEvent := range personnelPayloads {
payload, err := json.Marshal(personnelEvent)
if err != nil {
log.Println("Error while marshalling payload ", err)
}
log.Println(string(payload))
// go makeRequest(string(payload))
}
}
func main() {
ch := make(chan PersonnelEvent)
for i := 0; i < 20; i++ {
go func() {
for personnelEvent := range ch {
payload, err := json.Marshal(personnelEvent)
if err != nil {
log.Println("Error while marshalling payload", err)
}
go makeRequest(string(payload))
//log.Println("Payload ", string(payload))
}
}()
}
file, err := os.Open("/Users/tmp/Desktop/personnels.txt")
defer file.Close()
if err != nil {
log.Fatalf("Error opening personnel id file %v", err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
go func() {
ch <- PersonnelEvent{EventType: "provider_location_department_personnel_linked", Data: Data{DepartmentId: "2a8d9687-aea8-4a2c-bc08-c64d7716d973", PersonnelId: scanner.Text()}}
}()
}
}
Its reading some ids from a file and then creating a payload out of it and invoking a post request on the web server, but when i run the program it gives too many open file errors/no such host errors, i feel that the program is too much concurrent how to make it run gracefully?
inside your 20 goroutines started in main(), "go makeRequest(...)" again created one goroutine for each event. you don't need start extra goroutine there.
Besides, I think you don't need start goroutine in your scan loop, either. buffered channel is enough,because bottleneck should be at doing http requests.
You can use a buffered channel, A.K.A. counting semaphore, to limit the parallelism.
// The capacity of the buffered channel is 10,
// which means you can have 10 goroutines to
// run the makeRequest function in parallel.
var tokens = make(chan struct{}, 10)
func makeRequest(payload string) {
tokens <- struct{}{} // acquire the token or block here
defer func() { <-tokens }() // release the token to awake another goroutine
// other code...
}

Making Golang TCP server concurrent

New to Go and trying to make a TCP server concurrent. I found multiple examples of this, including this one, but what I am trying to figure out is why some changes I made to a non concurrent version are not working.
This is the original sample code I started from
package main
import "bufio"
import "fmt"
import "log"
import "net"
import "strings" // only needed below for sample processing
func main() {
fmt.Println("Launching server...")
fmt.Println("Listen on port")
ln, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
fmt.Println("Accept connection on port")
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("Entering loop")
// run loop forever (or until ctrl-c)
for {
// will listen for message to process ending in newline (\n)
message, _ := bufio.NewReader(conn).ReadString('\n')
// output message received
fmt.Print("Message Received:", string(message))
// sample process for string received
newmessage := strings.ToUpper(message)
// send new string back to client
conn.Write([]byte(newmessage + "\n"))
}
}
The above works, but it is not concurrent.
This is the code after I modified it
package main
import "bufio"
import "fmt"
import "log"
import "net"
import "strings" // only needed below for sample processing
func handleConnection(conn net.Conn) {
fmt.Println("Inside function")
// run loop forever (or until ctrl-c)
for {
fmt.Println("Inside loop")
// will listen for message to process ending in newline (\n)
message, _ := bufio.NewReader(conn).ReadString('\n')
// output message received
fmt.Print("Message Received:", string(message))
// sample process for string received
newmessage := strings.ToUpper(message)
// send new string back to client
conn.Write([]byte(newmessage + "\n"))
}
}
func main() {
fmt.Println("Launching server...")
fmt.Println("Listen on port")
ln, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
//defer ln.Close()
fmt.Println("Accept connection on port")
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("Calling handleConnection")
go handleConnection(conn)
}
I based my code on several other examples I found of concurrent servers, but yet when I run the above the server seems to exit instead of running the handleConnection function
Launching server...
Listen on port
Accept connection on port
Calling handleConnection
Would appreciate any feedback as similar code examples I found and tested using the same approach, concurrently calling function to handle connections, worked; so, would like to know what is different with my modified code from the other samples I saw since they seem to be the same to me.
I was not sure if it was the issue, but I tried commenting the defer call to close. That did not help.
Thanks.
Your main function is returning immediately after accepting a new connection, so your program exits before the connection can be handled. Since you probably also want to receive more than one single connection (or else there would be no concurrency), you should put this in a for loop.
You are also creating a new buffered reader in each iteration of the for loop, which would discard any buffered data. You need to do that outside the for loop, which I demonstrate here by creating a new bufio.Scanner which is a simpler way to read newline delimited text.
import (
"bufio"
"fmt"
"log"
"net"
"strings"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
message := scanner.Text()
fmt.Println("Message Received:", message)
newMessage := strings.ToUpper(message)
conn.Write([]byte(newMessage + "\n"))
}
if err := scanner.Err(); err != nil {
fmt.Println("error:", err)
}
}
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
fmt.Println("Accept connection on port")
for {
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("Calling handleConnection")
go handleConnection(conn)
}
}
The reason you see this behavior is the fact that your main method exits even though your go routine is still running. Make sure to block the main method to achieve what you are trying to achieve.
May be add something like this in the main:
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c // This will block until you manually exists with CRl-C
Also you can bring back your defer
When you run function using go func() syntax, you are executing a new goroutine without blocking the main one. However, the program will exit when the main goroutine finishes, so in short, you need to block the main goroutine for as long as you want your child goroutines to execute.
I often find myself checking how similar problems are solved in go standard library. For example, Server.Serve() from http package does something similar. Here is the extracted version (shortened, follow the link to see full version):
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
ctx := context.Background()
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
// handle the error
}
return e
}
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
To stop the above function, we could close the listener (e.g. via interrupt signal), which in turn would generate an error on Accept(). The above implementation checks whether serv.GetDoneChan() channel returns a value as an indicator that the error is expected and the server is closed.
This is what you want
Server
package main
import (
"bufio"
)
import "fmt"
import "log"
import "net"
import "strings" // only needed below for sample processing
func handleConnection(conn net.Conn) {
fmt.Println("Inside function")
// will listen for message to process ending in newline (\n)
message, _ := bufio.NewReader(conn).ReadString('\n')
// output message received
fmt.Print("Message Received:", string(message))
// sample process for string received
newmessage := strings.ToUpper(message)
// send new string back to client
conn.Write([]byte(newmessage + "\n"))
conn.Close()
}
func main() {
fmt.Println("Launching server...")
fmt.Println("Listen on port")
ln, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
fmt.Println("Accept connection on port")
for {
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("Calling handleConnection")
go handleConnection(conn)
}
}
Client
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
addr, _ := net.ResolveTCPAddr("tcp", ":8081")
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
panic(err.Error())
}
fmt.Fprintf(conn, "From the client\n")
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print(message)
conn.Close()
}

How to listen to a client continiously using gob in Golang

In my use case I would like to continuously listen to a TCP connection and receive the value. The expected value is an object. So I am using gob decoder to receive the value from the connection. I would like to continuously listen to the connection and receive the object using go routines. I have the code snippet here[It is part of the application. code snippet does not compile]. It is getting value for the first time but not receiving for the subsequent objects.
func main() {
//...
// SOME CODE
//...
// All hosts who are connected; a map wherein
// the keys are ip addreses and the values are
//net.Conn objects
allClients := make(map[string]net.Conn)
tMaps := make(chan map[string]int64)
for {
select {
// Accept new clients
//
case conn := <-newConnections:
log.Printf("Accepted new client, #%s", hostIp)
// Constantly read incoming messages from this
// client in a goroutine and push those onto
// the tMaps channel for broadcast to others.
//
go func(conn net.Conn) {
dec := gob.NewDecoder(conn)
for {
var tMap map[string]int64
err := dec.Decode(&tMap)
if err != nil {
fmt.Println("Error in decoding ", err)
break
}
log.Printf("Received values: %+v", tMap)
//update throttle map based on the received value
tMaps <- throttleMap
}
}(conn)
}
}
Could anyone help me on this?
Let's look at the basics of a TCP server in Go.
First, there is the "listening" part. We can set that up like this:
package main
import (
"fmt"
"io"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", ":9000")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
io.WriteString(conn, fmt.Sprint("Hello World\n", time.Now(), "\n"))
conn.Close()
}
}
Notice the infinite for loop. It is always running and looping over that code. What does the code that is being looped over do? If a connection comes in on the port which is being listened on, then that connection is accepted. We then do something with that connection. In this case, we write back to it with io.WriteString. To this one connection, we are sending a response. We then close the connection. And if there are more connections, we're ready to accept them.
Now let's create a client to connect to the TCP server. This is known as "dialing" in to the TCP server.
To run all of this code on your machine, run the TCP server code above. To run the code, go to your terminal and enter: go run main.go
Now put the code directly below into another file. Launch another tab in your terminal. Run that code also by entering: go run main.go
The code below which "dials" in to your TCP server will connect to the server and the TCP server will respond, then close the connection.
Here is the code for dialing into a TCP server as a client:
package main
import (
"fmt"
"io/ioutil"
"net"
)
func main() {
conn, err := net.Dial("tcp", "localhost:9000")
if err != nil {
panic(err)
}
defer conn.Close()
bs, _ := ioutil.ReadAll(conn)
fmt.Println(string(bs))
}
We can take these basics and start having fun.
Let's create an "echo" server.
This will illustrate accepting many connections.
package main
import (
"io"
"net"
)
func main() {
ln, err := net.Listen("tcp", ":9000")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
// handles unlimited connections
go func() {
io.Copy(conn, conn)
conn.Close()
}()
}
}
Run the file above the same way as before: go run main.go
If you get an error, make sure you have closed the TCP server we were running from the previous example. You close the TCP server with ctrl+c in the terminal.
Now that your new TCP server is running, let's connect to it using Telnet.
On windows you can install telnet; on Mac, it should already be there. Use this command to run telnet and connect to your TCP server: telnet localhost 9000
Now for one more example - an in-memory database like Redis:
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strings"
)
var data = make(map[string]string)
func handle(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
ln := scanner.Text()
fs := strings.Fields(ln)
if len(fs) < 2 {
io.WriteString(conn, "This is an in-memory database \n" +
"Use SET, GET, DEL like this: \n" +
"SET key value \n" +
"GET key \n" +
"DEL key \n\n" +
"For example - try these commands: \n" +
"SET fav chocolate \n" +
"GET fav \n\n\n")
continue
}
switch fs[0] {
case "GET":
key := fs[1]
value := data[key]
fmt.Fprintf(conn, "%s\n", value)
case "SET":
if len(fs) != 3 {
io.WriteString(conn, "EXPECTED VALUE\n")
continue
}
key := fs[1]
value := fs[2]
data[key] = value
case "DEL":
key := fs[1]
delete(data, key)
default:
io.WriteString(conn, "INVALID COMMAND "+fs[0]+"\n")
}
}
}
func main() {
li, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalln(err)
}
defer li.Close()
for {
conn, err := li.Accept()
if err != nil {
log.Fatalln(err)
}
handle(conn)
}
}
And adding in concurrency:
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strings"
)
type Command struct {
Fields []string
Result chan string
}
func redisServer(commands chan Command) {
var data = make(map[string]string)
for cmd := range commands {
if len(cmd.Fields) < 2 {
cmd.Result <- "Expected at least 2 arguments"
continue
}
fmt.Println("GOT COMMAND", cmd)
switch cmd.Fields[0] {
// GET <KEY>
case "GET":
key := cmd.Fields[1]
value := data[key]
cmd.Result <- value
// SET <KEY> <VALUE>
case "SET":
if len(cmd.Fields) != 3 {
cmd.Result <- "EXPECTED VALUE"
continue
}
key := cmd.Fields[1]
value := cmd.Fields[2]
data[key] = value
cmd.Result <- ""
// DEL <KEY>
case "DEL":
key := cmd.Fields[1]
delete(data, key)
cmd.Result <- ""
default:
cmd.Result <- "INVALID COMMAND " + cmd.Fields[0] + "\n"
}
}
}
func handle(commands chan Command, conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
ln := scanner.Text()
fs := strings.Fields(ln)
result := make(chan string)
commands <- Command{
Fields: fs,
Result: result,
}
io.WriteString(conn, <-result+"\n")
}
}
func main() {
li, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalln(err)
}
defer li.Close()
commands := make(chan Command)
go redisServer(commands)
for {
conn, err := li.Accept()
if err != nil {
log.Fatalln(err)
}
go handle(commands, conn)
}
}
See my lectures from my CSUF class describing all of this here. And one more great resource.

Resources