I have a golang server doing something like this:
package main
func main() {
for {
c := listener.Accept()
go handle(c)
}
}
...
func handle(c net.Conn) {
m := readMessage(c) // func(net.Conn)Message
r := processMessage(m) //func(Message)Result
sendResult(c, r) // func(net.Conn,Result)
}
Which reads and writes messages synchronously. What I need now is to send messages asynchronously through a given open connection, I know a channel can be used by I'm kind of lost.
This is my idea:
...
func someWhereElese(c chan Result) {
// generate a message and a result
r := createResultFromSomewhere()
c <- r // send the result through the channel
}
And modify my handle to use that same channel instead
func handle(c net.Conn, rc chan Result) {
m := readMessage(c) // func(net.Conn)Message
r := processMessage(m) //func(Message)Result
//sendResult(c, r) // func(net.Conn,Result)
rc <- r
}
And here's where my confusion lies.
The result channel should be created and it should have a connection where to send whatever it receives
func doSend(c net.Con, rc chan Result) {
r := rc // got a result channel
sendResult(c, r) // send it through the wire
}
But where should that channel be created? In the main loop?
func main() {
...
for {
c := l.Accept()
rc := make(chan Result)
go doSend(c, rc)
}
}
What about the read? Should it go in it's own channel/gorutine?
If I need to broadcast to n clients, should I keep a slice of result channels? a slice of connections?
I'm kind of confused here, but I feel I'm close.
This program seems to solve my immediate question
package main
import (
"bytes"
"encoding/binary"
"log"
"net"
)
var rcs []chan int = make([]chan int,0)
func main() {
a, e := net.ResolveTCPAddr("tcp", ":8082")
if e != nil {
log.Fatal(e)
}
l, e := net.ListenTCP("tcp", a)
for {
c, e := l.Accept()
if e != nil {
log.Fatal(e)
}
rc := make(chan int)
go read(c, rc)
go write(c, rc)
rcs = append(rcs, rc)
// simulate broacast
log.Println(len(rcs))
if len(rcs) > 5 {
func() {
for _, v := range rcs {
log.Println("sending")
select {
case v <- 34:
log.Println("done sending")
default:
log.Println("didn't send")
}
}
}()
}
}
}
func read(c net.Conn, rc chan int) {
h := make([]byte, 2)
for {
_, err := c.Read(h)
if err != nil {
rc <- -1
}
var v int16
binary.Read(bytes.NewReader(h[:2]), binary.BigEndian, &v)
rc <- int(v)
}
}
func write(c net.Conn, rc chan int) {
for {
r := <-rc
o := []byte{byte(r * 2)}
c.Write(o)
}
}
Related
in this exercise I am trying to compute Fibonacci numbers but by spawning goroutines and sending back result by pointer in argument but I am doing something wrong please help
package main
import (
"fmt"
)
func fib(n int, p *int) {
fmt.Println(n)
var a, b int
if n > 1 {
go fib(n-1, &a)
go fib(n-2, &b)
*p = a + b
} else {
*p = n
}
fmt.Println(*p)
}
func main() {
c := -1
go fib(5, &c)
fmt.Println(c)
}
Thank you #poWar below is my solution posted only to not leave question unanswered (probably not best but gets exercise done)
package main
import (
"fmt"
"sync"
)
func fib(n int, p *int, wg1 *sync.WaitGroup) {
// fmt.Println(n)
a, b := -1, -1
var wg sync.WaitGroup
if n > 1 {
wg.Add(1)
go fib(n-1, &a, &wg)
wg.Add(1)
go fib(n-2, &b, &wg)
wg.Wait()
*p = a + b
} else {
*p = n
}
wg1.Done()
// fmt.Println(*p)
}
func main() {
c := -1
var wg sync.WaitGroup
wg.Add(1)
go fib(30, &c, &wg)
wg.Wait()
fmt.Println(c)
}
I am trying to call the TransmitPackets function on windows using GO.
The goal is to be able to send multiple packets with one syscall (can't be achieved with WSASend [it'll send fragmented IP packets]).
My code panics
panic: write udp 192.168.1.26:51817->8.8.8.8:8000: transmitpackets: An invalid argument was supplied.
goroutine 1 [running]:
main.main()
c:/Users/amit/dev/go/src/rio/main.go:26 +0x210
exit status 2
Process exiting with code: 1
Here's my test code
package main
import (
"math/rand"
"net"
)
func main() {
raddr, err := net.ResolveUDPAddr("udp", "8.8.8.8:8000")
if err != nil {
panic(err)
}
con, err := net.DialUDP("udp", nil, raddr)
if err != nil {
panic(err)
}
packets := make(net.Buffers, 10)
for i := 0; i < len(packets); i++ {
packets[i] = make([]byte, 1400)
rand.Read(packets[i])
}
_, err = con.WriteMultiple(packets)
if err != nil {
panic(err)
}
}
and Here's my call to TransmitPackets:
type TransmitPacketsElement struct {
dwElFlags uint32
cLength uint32
pBuffer unsafe.Pointer
nFileOffset uint64
hFile uintptr
}
func transmitPackets(s Handle, bufs [][]byte, overlapped *Overlapped) (err error) {
var maxPacketLen = 0
tpElements := make([]TransmitPacketsElement, len(bufs))
for i, tpElement := range tpElements {
buffer := bufs[i]
if len(buffer) > maxPacketLen {
maxPacketLen = len(buffer)
}
tpElement.cLength = uint32(len(buffer))
tpElement.dwElFlags = uint32(uint32(TP_ELEMENT_MEMORY) | uint32(TP_ELEMENT_EOP))
tpElement.pBuffer = unsafe.Pointer(&buffer[0])
}
r1, _, e1 := Syscall6(transmitPacketsFunc.addr, 6, uintptr(s), uintptr(unsafe.Pointer(&tpElements[0])), uintptr(uint32(len(tpElements))), uintptr(uint32(maxPacketLen)), uintptr(unsafe.Pointer(overlapped)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = EINVAL
}
}
return
}
You can see the full implementation in my modified go 1.8.3 source on github
I'm working with Go in an API for the bus frequency of my city, but i'm a little bit stuck on the threads when i try to make HTTP Get to many urls.
Without concurrency, the programs takes over 16 minutes to complete the 1500 url calls to take the HTTP status code, and i was trying to use the concurrency, but after reading many posts i don't understand how goroutines work...
The idea is to make ONE function and change the number of requests, like here:
go getBusPostStatus(600, 800)
But i'm completely stucked on that...
Here is the code:
package main
import (
"fmt"
"net/http"
"strconv"
"time"
)
var i int = 0
var convStr string
var message = make(chan string)
/*func main(){
for i = 0; i < 1500; i++ {
z = strconv.Itoa(i)
url := "http://www.urbanosdezaragoza.es/frm_esquemaparadatime.php?poste=" + z
resp, err := http.Get(url)
if err != nil {
fmt.Println("Houston, we've got problems")
}else{
if resp.StatusCode == 200{
fmt.Println("OK: El poste "+z+" existe")
}else{
fmt.Println("WARN: El poste "+z+" NO existe")
}
}
}
}*/
//Return 2 houndred posts
func returnTH(c chan string){
for i = 0; i < 200; i++ {
convStr = strconv.Itoa(i)
url := "http://www.urbanosdezaragoza.es/frm_esquemaparadatime.php?poste=" + convStr
resp, err := http.Get(url)
if err != nil {
fmt.Println("Houston, we've got problems")
}else{
if resp.StatusCode == 200{
//fmt.Println("OK: El poste "+z+" existe")
c <- "OK: The bus post "+convStr+" exists"
}else{
//fmt.Println("WARN: El poste "+z+" NO existe")
c <- "WARN: The bus post "+convStr+" does not exist"
}
}
}
}
func returnFH(z chan string){
for i = 201; i < 400; i++ {
convStr = strconv.Itoa(i)
url := "http://www.urbanosdezaragoza.es/frm_esquemaparadatime.php?poste=" + convStr
resp, err := http.Get(url)
if err != nil {
fmt.Println("Houston, we've got problems")
}else{
if resp.StatusCode == 200{
//fmt.Println("OK: El poste "+z+" existe")
z <- "OK: The bus post "+convStr+" exists"
}else{
//fmt.Println("WARN: El poste "+z+" NO existe")
z <- "WARN: The bus post "+convStr+" does not exist"
}
}
}
}
func threadPrint(c, z chan string){
for {
threadOne := <- c
threadTwo := <- z
fmt.Println(threadOne)
fmt.Println(threadTwo)
}
}
func main(){
start := time.Now()
var c chan string = make(chan string)
var z chan string = make(chan string)
//for i = 0; i < 1500; i++{
go returnTH(c)
go returnFH(z)
go threadPrint(c,z)
/*go getBusPostStatus(400, 600)
go getBusPostStatus(600, 800)
go getBusPostStatus(800, 1000)
go getBusPostStatus(1000, 1200)
go getBusPostStatus(1200, 1400)
go getBusPostStatus(1400, 1500)*/
//}
timeExec:= time.Since(start)
fmt.Println("Time to exec code = ", timeExec)
/*var input string
fmt.Scanln(&input)
fmt.Println("done")*/
}
Many thanks in advance!!
Following is a simplified example code which requests 100 times concurrently and prints results, using goroutine and channel. Hope this code helps.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rep := 100
results := make(chan string)
// Use goroutine to send multiple time-consuming jobs to the channel.
for i := 0; i < rep; i++ {
go func(num int) {
results <- mockHTTPRequest(num)
}(i)
}
// Receive results from the channel and use them.
for i := 0; i < rep; i++ {
fmt.Println(<-results)
}
}
func mockHTTPRequest(num int) string {
timeDelay := rand.Intn(5000)
time.Sleep(time.Duration(timeDelay) * time.Millisecond)
if timeDelay%2 == 0 {
return fmt.Sprintf("OK: The bus post %v exists", num)
}
return fmt.Sprintf("WARN: The bus post %v does not exist", num)
}
You can run this code on https://play.golang.org/p/RR34roRIl4 .
I am trying to parse a yaml file dynamically (Therefore no struct).
package main
import (
"fmt"
"gopkg.in/yaml.v2"
"log"
)
func main() {
var out = `
a: First!
f: Second
b:
c:
f: Third
`
m := make(map[interface{}]interface{})
err := yaml.Unmarshal([]byte(out), &m)
if err != nil {
log.Fatal(err)
}
fmt.Println(m["b"].(map[interface{}]interface{})["c"].(map[interface{}]interface{})["f"])
}
Everytime I have to access a subkey, I am forced to convert map variable in question to (map[interface{}]interface{}). This is causing bit of a hassle for me as I have to iterate through the map.
Is there any easier method for parsing YAML file in Go?
Another approach is to flatten the yaml data structure into a key,value map in which keys and values are strings. Then if you need the actual type (5 being an int) you can do the conversion yourself. Example:
"a" = "First!"
"f" = "Second"
"b.c.f" = "Third"
"b.c.g.size" = "2"
"b.c.g.0 = "zero"
"b.c.g.1 = "one"
In Go:
func main() {
any := map[string]interface{}{}
err := yaml.Unmarshal([]byte(out), &any)
if err != nil {
log.Fatal(err)
}
flatmap := map[string]string{}
for k, v := range any {
flatten(k, v, flatmap)
}
for k, v := range flatmap {
fmt.Println(k, "=", v)
}
}
func flatten(prefix string, value interface{}, flatmap map[string]string) {
submap, ok := value.(map[interface{}]interface{})
if ok {
for k, v := range submap {
flatten(prefix+"."+k.(string), v, flatmap)
}
return
}
stringlist, ok := value.([]interface{})
if ok {
flatten(fmt.Sprintf("%s.size", prefix), len(stringlist), flatmap)
for i, v := range stringlist {
flatten(fmt.Sprintf("%s.%d", prefix, i), v, flatmap)
}
return
}
flatmap[prefix] = fmt.Sprintf("%v", value)
}
How to cast reflect.Value to its type?
type Cat struct {
Age int
}
cat := reflect.ValueOf(obj)
fmt.Println(cat.Type()) // Cat
fmt.Println(Cat(cat).Age) // doesn't compile
fmt.Println((cat.(Cat)).Age) // same
Thanks!
concreteCat,_ := reflect.ValueOf(cat).Interface().(Cat)
see http://golang.org/doc/articles/laws_of_reflection.html
fox example
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)
Ok, I found it
reflect.Value has a function Interface() that converts it to interface{}
This func auto-converts types as needed. It loads a config file values into a simple struct based on struct name and fields:
import (
"fmt"
toml "github.com/pelletier/go-toml"
"log"
"os"
"reflect"
)
func LoadConfig(configFileName string, configStruct interface{}) {
defer func() {
if r := recover(); r != nil {
fmt.Println("LoadConfig.Recovered: ", r)
}
}()
conf, err := toml.LoadFile(configFileName)
if err == nil {
v := reflect.ValueOf(configStruct)
typeOfS := v.Elem().Type()
sectionName := getTypeName(configStruct)
for i := 0; i < v.Elem().NumField(); i++ {
if v.Elem().Field(i).CanInterface() {
kName := conf.Get(sectionName + "." + typeOfS.Field(i).Name)
kValue := reflect.ValueOf(kName)
if (kValue.IsValid()) {
v.Elem().Field(i).Set(kValue.Convert(typeOfS.Field(i).Type))
}
}
}
} else {
fmt.Println("LoadConfig.Error: " + err.Error())
}
}
Seems the only way would be to do a switch statement similar to (code below) (also, something like the commented line would've-been nice though doesn't work (:()):
func valuesFromStruct (rawV interface{}) []interface{} {
v := reflect.ValueOf(rawV)
out := make([]interface{}, 0)
for i := 0; i < v.NumField(); i += 1 {
field := v.Field(i)
fieldType := field.Type()
// out = append(out, field.Interface().(reflect.PtrTo(fieldType)))
switch (fieldType.Name()) {
case "int64":
out = append(out, field.Interface().(int64))
break`enter code here`
case "float64":
out = append(out, field.Interface().(float64))
break
case "string":
out = append(out, field.Interface().(string))
break
// And all your other types (here) ...
default:
out = append(out, field.Interface())
break
}
}
return out
}
Cheers!