I'm building out a simple Slackbot as a learning experience with Go, and I've hit my first snag: I'm unable to write a message back to the connection!
Here's my main func:
func main() {
conn, botId, err := slackInit(os.Getenv("SLACKBOT_TOKEN"))
if err != nil { return }
defer conn.Close()
for {
_, event, err := conn.ReadMessage()
if err != nil {
fmt.Println("Error processing message:", err)
return
}
fmt.Println(string(event))
message, err := slackGetMessage(event)
if strings.Contains(message, botId) {
fmt.Println("Bot was mentioned!")
resp := []byte("You talkin' to me?")
err = conn.WriteMessage(1, resp)
if err != nil {
fmt.Println("Error writing message:", string(resp))
return
}
}
}
}
This listens and reports events with no issue, but fails as soon as the conn.WriteMessage method is called. I get a fatal error from websocket with code 1006 & the message "unexpected closure".
I'm using Gorilla for my websocket library, and I suspect maybe this is a concurrency issue related to their "one reader, one writer" limit. I've tried a few tweaks, but honestly just don't know enough about the languge/library yet to really dive deep on debugging this. :-/
I'm sure I'm missing something obvious here! Any tips for managing my Writer to get my bot talking back? Thanks!
The message sent to the server is not JSON as expected by the server. The server closed the connection without a handshake.
Related
I'm trying to send more than one request to a TCP server in Go but for some reason the second request is never sent, even if it is identical to the first one.
This is the server:
func StartServer(host string) {
l, err := net.Listen("tcp", host)
log.Println("Starting server on:", host)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer l.Close()
log.Println("Server is running...")
for {
// Listen for an incoming connection.
conn, err := l.Accept()
if err != nil {
log.Fatal("Error accepting: ", err.Error())
}
// Handle connections in a new goroutine.
fmt.Println("got a request")
go handleRequest(conn)
}
}
And this is the function in the client that sends the requests to the server:
func (u *User) ConnectToServer(host string, partner string) {
conn, _ := net.Dial("tcp", host)
fmt.Fprintf(conn, "message1\n")
fmt.Fprintf(conn, "message2\n")
}
EDIT: In the handleRequest function I read the input as follows:
// Handles incoming requests.
func handleRequest(conn net.Conn) {
rec, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Println("Error reading:", err.Error())
}
log.Println("Got message: ", rec)
// Send a response back to person contacting us.
conn.Write([]byte("Message received."))
// conn.Close()
}
Which according to the documentation only takes the first part before the first line break detected so I believe the second message is ignored because of this. How can I read both messages? Should I change the delimiter in the client maybe?
The server should read multiple lines given that the client sends multiple lines. Use bufio.Scanner to read lines:
func handleRequest(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
fmt.Printf("Got message: %s\n", scanner.Bytes())
conn.Write([]byte("Message received."))
}
if err := scanner.Err(); err != nil {
fmt.Printf("error reading connection: %v\n", err)
}
}
Some notes about the code:
To prevent a resource leak, the function closes the connection on return.
The scanner loop breaks on error reading the connection. If the error is not io.EOF, then the function logs the error.
bufio.Reader can also be used to read lines, but bufio.Scanner is easier to use.
In handleRequest(), you call ReadString() on the bufio Reader. Let's look at the docs:
ReadString reads until the first occurrence of delim in the input,
returning a string containing the data up to and including the
delimiter. If ReadString encounters an error before finding a
delimiter, it returns the data read before the error and the error
itself (often io.EOF). ReadString returns err != nil if and only if
the returned data does not end in delim. For simple uses, a Scanner
may be more convenient.
Considering the packets you're sending are terminated by a \n, you must call ReadString() twice on the same reader. You probably want to call ReadString() in a loop until it returns an error. Make sure to distinguish io.EOF, then.
Here's a playground with some inspiration. Note: it appears the playground does not allow TCP sockets, so you will have to run this elsewhere.
I am using a Go TCP Client to connect to our Go TCP Server.
I am able to connect to the Server and run commands properly, but every so often there will be an unusually high amount of consecutive TCP connection errors reported by my TCP Client when trying to either connect to our TCP Server or sending a message once connected:
dial tcp kubernetes_node_ip:exposed_kubernetes_port:
connectex: A connection attempt failed because the connected party did not properly
respond after a period of time, or established connection failed because connected
host has failed to respond.
read tcp unfamiliar_ip:unfamiliar_port->kubernetes_node_ip:exposed_kubernetes_port
wsarecv: A connection attempt failed because the connected party did not properly
respond after a period of time, or established connection failed because connected
host has failed to respond.
I say "unusually high" because I assume that the number of times these errors occur should be very minimal (about 5 or less within the hour). Note that I am not dismissing the possibility of this being caused by connection instabilities, as I have also noticed that it is possible to run several commands in rapid succession without any errors.
However, I am still going to post my code in case I am doing something wrong.
Below is the code that my TCP Client uses to connect to our server:
serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
if err != nil {
fmt.Println(err)
return
}
// Never stop asking for commands from the user.
for {
// Connect to the server.
serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
if err != nil {
fmt.Println(err)
continue
}
defer serverConnection.Close()
// Added to prevent connection timeout errors, but doesn't seem to be helping
// because said errors happen within just 1 or 2 minutes.
err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
if err != nil {
fmt.Println(err)
continue
}
// Ask for a command from the user and convert to JSON bytes...
// Send message to server.
_, err = serverConnection.Write(clientMsgBytes)
if err != nil {
err = merry.Wrap(err)
fmt.Println(merry.Details(err))
continue
}
err = serverConnection.CloseWrite()
if err != nil {
err = merry.Wrap(err)
fmt.Println(merry.Details(err))
continue
}
// Wait for a response from the server and print...
}
Below is the code that our TCP Server uses to accept client requests:
// We only supply the port so the IP can be dynamically assigned:
serverAddress, err := net.ResolveTCPAddr("tcp", ":"+server_port)
if err != nil {
return err
}
tcpListener, err := net.ListenTCP("tcp", serverAddress)
if err != nil {
return err
}
defer tcpListener.Close()
// Never stop listening for client requests.
for {
clientConnection, err := tcpListener.AcceptTCP()
if err != nil {
fmt.Println(err)
continue
}
go func() {
// Add client connection to Job Queue.
// Note that `clientConnections` is a buffered channel with a size of 1500.
// Since I am the only user connecting to our server right now, I do not think
// this is a channel blocking issue.
clientConnections <- clientConnection
}()
}
Below is the code that our TCP Server uses to process client requests:
defer clientConnection.Close()
// Added to prevent connection timeout errors, but doesn't seem to be helping
// because said errors happen within just 1 or 2 minutes.
err := clientConnection.SetDeadline(time.Now().Add(10 * time.Minute))
if err != nil {
return err
}
// Read full TCP message.
// Does not stop until an EOF is reported by `CloseWrite()`
clientMsgBytes, err := ioutil.ReadAll(clientConnection)
if err != nil {
err = merry.Wrap(err)
return nil, err
}
// Process the message bytes...
My questions are:
Am I doing something wrong in the above code, or is the above decent enough for basic TCP Client-Server operations?
Is it okay that both the TCP Client and TCP Server have code that defers closing their one connection?
I seem to recall that calling defer inside a loop does nothing. How do I properly close Client connections before starting new ones?
Some extra information:
Said errors are not logged by the TCP Server, so aside from
connection instabilities, this might also be a
Kubernetes/Docker-related issue.
It seems this piece of code does not act as you think it does. The defer statement on the connection close will only happen when the function returns, not when an iteration ends. So as far as I can see here, you are creating a lot of connections on the client side, it could be the problem.
serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
if err != nil {
fmt.Println(err)
return
}
// Never stop asking for commands from the user.
for {
// Connect to the server.
serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
if err != nil {
fmt.Println(err)
continue
}
defer serverConnection.Close()
// Added to prevent connection timeout errors, but doesn't seem to be helping
// because said errors happen within just 1 or 2 minutes.
err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
if err != nil {
fmt.Println(err)
continue
}
// Ask for a command from the user and send to the server...
// Wait for a response from the server and print...
}
I suggest to write it this way:
func start() {
serverAddress, err := net.ResolveTCPAddr("tcp", kubernetes_ip+":"+kubernetes_port)
if err != nil {
fmt.Println(err)
return
}
for {
if err := listen(serverAddress); err != nil {
fmt.Println(err)
}
}
}
func listen(serverAddress string) error {
// Connect to the server.
serverConnection, err := net.DialTCP("tcp", nil, serverAddress)
if err != nil {
fmt.Println(err)
continue
}
defer serverConnection.Close()
// Never stop asking for commands from the user.
for {
// Added to prevent connection timeout errors, but doesn't seem to be helping
// because said errors happen within just 1 or 2 minutes.
err = serverConnection.SetDeadline(time.Now().Add(10 * time.Minute))
if err != nil {
fmt.Println(err)
return err
}
// Ask for a command from the user and send to the server...
// Wait for a response from the server and print...
}
}
Also, you should keep a single connection open, or a pool of connections, instead of opening and closing the connection right away. Then when you send a message you get a connection from the pool (or the single connection), and you write the message and wait for the response, then you release the connection to the pool.
Something like that:
res, err := c.Send([]byte(`my message`))
if err != nil {
// handle err
}
// the implementation of send
func (c *Client) Send(msg []byte) ([]byte, error) {
conn, err := c.pool.Get() // returns a connection from the pool or starts a new one
if err != nil {
return nil, err
}
// send your message and wait for response
// ...
return response, nil
}
From the official documentation https://golang.org/pkg/net/http/#Client.Do it seems that the RoundTripper may not be able to re-use TCP connection for the next "keep-alive" request if Body is not closed and not fully read. What is this may about?
From what I see Close does not necessarily need to be called, when the whole Body is read. So what is the necessary requirement for connection re-use?
Code snippet (note commented out defer resp.Body.Close()) which creates multiple connections in a loop and from analysing it with netstat it seems the same TCP connection is used for all connections:
for nextPage != "" {
req, err := http.NewRequest("GET", nextPage, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *token))
if err != nil {
panic(err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
// defer resp.Body.Close()
result := []*User{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
panic(err)
}
nextPage = getNextPage(resp.Header.Get("Link"))
}
Document doesn't say Don't call Close() for keep-alive. Just say if you want to re-use connection, you MUST call Close() and fully read.
You can only count on what's in the documentation. In some circumstances (go version, OS, architecture, response content length, etc.) it may reuse the connection without fully reading it, or it may not. If you want to ensure the connection will be reused, you must fully read the body and close it.
I generally write a quick helper:
func cleanUpRequest(req *http.Request) {
if req != nil && req.Body != nil {
io.Copy(ioutil.Discard, req.Body)
req.Body.Close()
}
}
This is safe to defer even before error checking.
I'm writing an client that dials a websocket, and waits to receive information. I can successfully dial the websocket, but I cannot figure out how to implement some kind of asynchronous callback using the "golang.org/x/net/websocket" package. Is this even possible, or should I use the Gorilla package?
Use a for loop to read a websocket using the Gorilla package:
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
c.Close()
break
}
handleMessage(message)
}
Run the loop in a goroutine to make it asynchronous:
go func() {
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
c.Close()
break
}
handleMessage(message)
}
}()
I am playing wit golang and orientdb to test them. i have written a tiny web app which uppon a request fetches a single document from local orientdb instance and returns it. when i bench this app with apache bench, when concurrency is above 1 it get following error:
2015/04/08 19:24:07 http: panic serving [::1]:57346: Get http://localhost:2480/d
ocument/t1/9:1441: EOF
when i bench Orientdb itself, it runs perfectley OK with any cuncurrency factor.
also when i change the url to fetch from this document to anything (other program whritten in golang, some internet site etc) the app runs OK.
here is the code:
func main() {
fmt.Println("starting ....")
var aa interface{}
router := gin.New()
router.GET("/", func(c *gin.Context) {
ans := getdoc("http://localhost:2480/document/t1/9:1441")
json.Unmarshal(ans, &aa)
c.JSON(http.StatusOK, aa)
})
router.Run(":3000")
}
func getdoc(addr string) []byte {
client := new(http.Client)
req, err := http.NewRequest("GET", addr, nil)
req.SetBasicAuth("admin","admin")
resp, err := client.Do(req)
if err != nil {
fmt.Println("oops", resp, err)
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return body
}
thanks in advance
The keepalive connections are getting closed on you for some reason. You might be overwhelming the server, or going past the max number of connections the database can handle.
Also, the current http.Transport connection pool doesn't work well with synthetic benchmarks that make connections as fast as possible, and can quickly exhaust available file descriptors or ports (issue/6785).
To test this, I would set Request.Close = true to prevent the Transport from using the keepalive pool. If that works, one way to handle this with keepalive, is to specifically check for an io.EOF and retry that request, possibly with some backoff delay.